Diff for /parser3/src/classes/image.C between versions 1.90.2.15.2.1 and 1.111.6.1

version 1.90.2.15.2.1, 2003/03/18 15:14:14 version 1.111.6.1, 2005/07/27 07:35:51
Line 1 Line 1
 /** @file  /** @file
         Parser: @b image parser class.          Parser: @b image parser class.
   
         Copyright(c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)          Copyright(c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
 */  */
   
 static const char* IDENT_IMAGE_C="$Date$";  static const char * const IDENT_IMAGE_C="$Date$";
   
 /*  /*
         jpegsize: gets the width and height (in pixels) of a jpeg file          jpegsize: gets the width and height (in pixels) of a jpeg file
Line 33  static const char* IDENT_IMAGE_C="$Date$ Line 33  static const char* IDENT_IMAGE_C="$Date$
   
 class MImage: public Methoded {  class MImage: public Methoded {
 public: // VStateless_class  public: // VStateless_class
         ValuePtr create_new_value() { return ValuePtr(new VImage()); }          Value* create_new_value(Pool&, HashStringValue&) { return new VImage(); }
   
 public:  public:
         MImage();          MImage();
Line 50  DECLARE_CLASS_VAR(image, new MImage, 0); Line 50  DECLARE_CLASS_VAR(image, new MImage, 0);
 // helpers  // helpers
   
 /// value of exif tag -> it's value  /// value of exif tag -> it's value
 class EXIF_tag_value2name: public Hash<int, const char* > {  class EXIF_tag_value2name: public Hash<int, const char*> {
 public:  public:
         EXIF_tag_value2name() {          EXIF_tag_value2name() {
         // image JPEG Exif          // image JPEG Exif
Line 155  public: Line 155  public:
 #ifndef DOXYGEN  #ifndef DOXYGEN
 class Measure_reader {  class Measure_reader {
 public:  public:
         virtual size_t read(const char *&buf, size_t limit)=0;          virtual size_t read(const char* &buf, size_t limit)=0;
         virtual void seek(long value, int whence)=0;          virtual void seek(long value, int whence)=0;
         virtual long tell()=0;          virtual long tell()=0;
 };  };
   
 class Measure_file_reader: public Measure_reader {  class Measure_file_reader: public Measure_reader {
   
         ;  
         const String& file_name; const char* fname;          const String& file_name; const char* fname;
         int f;          int f;
   
 public:  public:
         Measure_file_reader(int af, const String& afile_name, const char* afname):           Measure_file_reader(int af, const String& afile_name, const char* afname): 
                 pool(), file_name(afile_name), fname(afname), f(af) {                  file_name(afile_name), fname(afname), f(af) {
         }          }
   
         override size_t read(const char *&abuf, size_t limit) {          override size_t read(const char* &abuf, size_t limit) {
                 if(limit==0)                  if(limit==0)
                         return 0;                          return 0;
   
                 char* lbuf=new char[limit];                  char* lbuf=new(PointerFreeGC) char[limit];
                 size_t read_size=(size_t)::read(f, lbuf, limit);  abuf=lbuf;                  size_t read_size=(size_t)::read(f, lbuf, limit);  abuf=lbuf;
                 if(ssize_t(read_size)<0 || read_size>limit)                  if(ssize_t(read_size)<0 || read_size>limit)
                         throw Exception(0,                          throw Exception(0,
                                 file_name,                                   &file_name, 
                                 "measure failed: actually read %lu bytes count not in [0..%lu] valid range",                                   "measure failed: actually read %u bytes count not in [0..%u] valid range", 
                         read_size, limit);                          read_size, limit);
   
                 return read_size;                  return read_size;
Line 189  public: Line 187  public:
         override void seek(long value, int whence) {          override void seek(long value, int whence) {
                 if(lseek(f, value, whence)<0)                  if(lseek(f, value, whence)<0)
                         throw Exception("image.format",                          throw Exception("image.format",
                                 file_name,                                   &file_name, 
                                 "seek(value=%ld, whence=%d) failed: %s (%d), actual filename '%s'",                                   "seek(value=%ld, whence=%d) failed: %s (%d), actual filename '%s'", 
                                         value, whence, strerror(errno), errno, fname);                                          value, whence, strerror(errno), errno, fname);
         }          }
Line 210  public: Line 208  public:
                 buf(abuf), size(asize), file_name(afile_name), offset(0) {                  buf(abuf), size(asize), file_name(afile_name), offset(0) {
         }          }
                   
         override size_t read(const char *&abuf, size_t limit) {          override size_t read(const char* &abuf, size_t limit) {
                 size_t to_read=min(limit, size-offset);                  size_t to_read=min(limit, size-offset);
                 abuf=buf+offset;                  abuf=buf+offset;
                 offset+=to_read;                  offset+=to_read;
Line 223  public: Line 221  public:
                 case SEEK_CUR: new_offset=offset+value; break;                  case SEEK_CUR: new_offset=offset+value; break;
                 case SEEK_SET: new_offset=(size_t)value; break;                  case SEEK_SET: new_offset=(size_t)value; break;
                 default:                   default: 
                         throw Exception(Exception::undefined_type,                           throw Exception(0, 
                                 Exception::undefined_source,                                   0, 
                                 "whence #%d not supported", 0, whence);                                   "whence #%d not supported", 0, whence); 
                         break; // never                          break; // never
                 }                  }
                                   
                 if((ssize_t)new_offset<0 || new_offset>size)                  if((ssize_t)new_offset<0 || new_offset>size)
                         throw Exception("image.format",                          throw Exception("image.format",
                                 file_name,                                   &file_name, 
                                 "seek(value=%l, whence=%d) failed: out of buffer, new_offset>size (%l>%l) or new_offset<0",                                   "seek(value=%l, whence=%d) failed: out of buffer, new_offset>size (%l>%l) or new_offset<0", 
                                         value, whence, new_offset, size);                                          value, whence, new_offset, size);
                 offset=new_offset;                  offset=new_offset;
Line 279  struct JPG_Size_segment_body { Line 277  struct JPG_Size_segment_body {
 };  };
   
 /// JPEG frame header  /// JPEG frame header
 struct JPG_Exif_segment_start {  struct JPG_Exif_segment_begin {
         char signature[6]; // Exif\0\0          char signature[6]; // Exif\0\0
 };  };
   
Line 291  struct JPG_Exif_TIFF_header { Line 289  struct JPG_Exif_TIFF_header {
 };  };
   
 // JPEG Exif IFD start  // JPEG Exif IFD start
 struct JPG_Exif_IFD_start {  struct JPG_Exif_IFD_begin {
         uchar directory_entry_count[2]; // the number of directory entry contains in this IFD          uchar directory_entry_count[2]; // the number of directory entry contains in this IFD
 };  };
   
Line 327  inline uint endian_to_uint(bool is_big, Line 325  inline uint endian_to_uint(bool is_big,
                 x_endian_to_uint(b[0], b[1], b[2], b[3]);                  x_endian_to_uint(b[0], b[1], b[2], b[3]);
 }  }
   
 static void measure_gifconst String* origin_string,   static void measure_gif(const String& origin_string, 
                          Measure_reader& reader, ushort& width, ushort& height) {                           Measure_reader& reader, ushort& width, ushort& height) {
   
         const char *buf;          const char* buf;
         const int head_size=sizeof(GIF_Header);          const size_t head_size=sizeof(GIF_Header);
         if(reader.read(buf, head_size)<head_size)          if(reader.read(buf, head_size)<head_size)
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not GIF file - too small");                          "not GIF file - too small");
         GIF_Header *head=(GIF_Header *)buf;          GIF_Header *head=(GIF_Header *)buf;
   
         if(strncmp(head->signature, "GIF", 3)!=0)          if(strncmp(head->signature, "GIF", 3)!=0)
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not GIF file - wrong signature");                                "not GIF file - wrong signature");      
   
         width=endian_to_ushort(false, head->width);          width=endian_to_ushort(false, head->width);
         height=endian_to_ushort(false, head->height);          height=endian_to_ushort(false, head->height);
 }  }
   
 static ValuePtr parse_IFD_entry_formatted_one_value(  static Value* parse_IFD_entry_formatted_one_value(
                                                                                                   bool is_big,                                                                                                    bool is_big,
                                                                                                   ushort format,                                                                                                     ushort format, 
                                                                                                   size_t component_size,                                                                                                     size_t component_size, 
                                                                                                   const uchar *value) {                                                                                                    const uchar *value) {
         switch(format) {          switch(format) {
         case 1: // unsigned byte          case 1: // unsigned byte
                 return ValuePtr(new VInt((uchar)value[0]));                  return new VInt((uchar)value[0]);
         case 3: // unsigned short          case 3: // unsigned short
                 return ValuePtr(new VInt(endian_to_ushort(is_big, value)));                  return new VInt(endian_to_ushort(is_big, value));
         case 4: // unsigned long          case 4: // unsigned long
                  // 'double' because parser's Int is signed                   // 'double' because parser's Int is signed
                 return ValuePtr(new VDouble(endian_to_uint(is_big, value)));                  return new VDouble(endian_to_uint(is_big, value));
         case 5: // unsigned rational          case 5: // unsigned rational
                 {                  {
                         uint numerator=endian_to_uint(is_big, value); value+=component_size/2;                          uint numerator=endian_to_uint(is_big, value); value+=component_size/2;
                         uint denominator=endian_to_uint(is_big, value);                          uint denominator=endian_to_uint(is_big, value);
                         if(!denominator)                          if(!denominator)
                                 return ValuePtr(0);                                  return 0;
                         return ValuePtr(new VDouble(((double)numerator)/denominator));                          return new VDouble(((double)numerator)/denominator);
                 }                  }
         case 6: // signed byte          case 6: // signed byte
                 return ValuePtr(new VInt((signed char)value[0]));                  return new VInt((signed char)value[0]);
         case 8: // signed short          case 8: // signed short
                 return ValuePtr(new VInt((signed short)endian_to_ushort(is_big, value)));                  return new VInt((signed short)endian_to_ushort(is_big, value));
         case 9: // signed long          case 9: // signed long
                 return ValuePtr(new VInt((signed int)endian_to_uint(is_big, value)));                  return new VInt((signed int)endian_to_uint(is_big, value));
         case 10: // signed rational          case 10: // signed rational
                 {                  {
                         signed int numerator=(signed int)endian_to_uint(is_big, value); value+=component_size/2;                          signed int numerator=(signed int)endian_to_uint(is_big, value); value+=component_size/2;
                         uint denominator=endian_to_uint(is_big, value);                          uint denominator=endian_to_uint(is_big, value);
                         if(!denominator)                          if(!denominator)
                                 return ValuePtr(0);                                  return 0;
                         return ValuePtr(new VDouble(numerator/denominator));                          return new VDouble(numerator/denominator);
                 }                  }
                 /*                  /*
         case 11: // single float          case 11: // single float
Line 390  static ValuePtr parse_IFD_entry_formatte Line 388  static ValuePtr parse_IFD_entry_formatte
                 */                  */
         };                };      
                   
         return ValuePtr(0);          return 0;
 }  }
   
 // date.C  // date.C
 time_t cstr_to_time_t(char *cstr, const String& report_error_origin);  tm cstr_to_time_t(char *cstr);
   
 static ValuePtr parse_IFD_entry_formatted_value(  static Value* parse_IFD_entry_formatted_value(bool is_big, ushort format, 
                                                                                           bool is_big, ushort format,                                                 size_t component_size, uint components_count, 
                                                                                           size_t component_size, uint components_count,                                                 const uchar *value) {
                                                                                           const uchar *value) {  
         if(format==2) { // ascii string, exception: the only type with varying size          if(format==2) { // ascii string, exception: the only type with varying size
                 const char* cstr=(const char* )value;                  const char* cstr=(const char* )value;
                 size_t size=components_count;                  size_t length=components_count;
                 // Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes                  // Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes
                 if(size==JPEG_EXIF_DATE_CHARS                   if(length==JPEG_EXIF_DATE_CHARS 
                         && isdigit(cstr[0])                          && isdigit((unsigned char)cstr[0])
                         && cstr[JPEG_EXIF_DATE_CHARS-1]==0) {                          && cstr[length-1]==0) {
                         char cstr_writable[JPEG_EXIF_DATE_CHARS];                           char cstr_writable[JPEG_EXIF_DATE_CHARS]; 
                         strcpy(cstr_writable, cstr);                          strcpy(cstr_writable, cstr);
   
                         time_t t=cstr_to_time_t(cstr_writable,                           try {
                                 0/* do not throw exception, just return bad result */);                                  return new VDate(cstr_to_time_t(cstr_writable));
                         if(t>=0)                          }
                                 return ValuePtr(new VDate(t));                          catch(...) { /*ignore bad date times*/ }
                 }                  }
   
                 if(const char* premature_zero_pos=(const char* )memchr(cstr, 0, size))                  if(const char* premature_zero_pos=(const char* )memchr(cstr, 0, length))
                         size=premature_zero_pos-cstr;                          length=premature_zero_pos-cstr;
                 return ValuePtr(new VString(String* (new String(cstr, size, true/*tainted*/))));                  return new VString(*new String(cstr, length, true/*tainted*/));
         }          }
   
         if(components_count==1)          if(components_count==1)
                 return parse_IFD_entry_formatted_one_value(pool, is_big, format, component_size, value);                  return parse_IFD_entry_formatted_one_value(is_big, format, component_size, value);
   
         VHashPtr result(new VHash);          VHash* result=new VHash;
         HashStringValue& hash=result->hash(Exception::undefined_source);          HashStringValue& hash=result->hash();
         for(uint i=0; i<components_count; i++, value+=component_size) {          for(uint i=0; i<components_count; i++, value+=component_size) {
                 char *key_cstr=new char[MAX_NUMBER];                  hash.put(
                 const String& skey(new String(key_cstr, snprintf(key_cstr, MAX_NUMBER, "%d", i)));                          String::Body::Format(i),
                 hash.put(skey,                           parse_IFD_entry_formatted_one_value(is_big, format, component_size, value));
                         parse_IFD_entry_formatted_one_value(pool, is_big, format, component_size, value));  
         }          }
   
         return result;          return result;
 }  }
   
 static ValuePtr parse_IFD_entry_value(  static Value* parse_IFD_entry_value(
                                                                         bool is_big, Measure_reader& reader, long tiff_base,                                                                          bool is_big, Measure_reader& reader, long tiff_base,
                                                                         JPG_Exif_IFD_entry& entry) {                                                                          JPG_Exif_IFD_entry& entry) {
         size_t format2component_size[]={          size_t format2component_size[]={
Line 459  static ValuePtr parse_IFD_entry_value( Line 455  static ValuePtr parse_IFD_entry_value(
   
         ushort format=endian_to_ushort(is_big, entry.format);          ushort format=endian_to_ushort(is_big, entry.format);
         if(format>=sizeof(format2component_size)/sizeof(format2component_size[0]))          if(format>=sizeof(format2component_size)/sizeof(format2component_size[0]))
                 return ValuePtr(0); // format out of range, ignoring                  return 0; // format out of range, ignoring
   
         size_t component_size=format2component_size[format];          size_t component_size=format2component_size[format];
         if(component_size==0)          if(component_size==0)
                 return ValuePtr(0); // undefined format                  return 0; // undefined format
   
         // You can get the total data byte length by multiplies           // You can get the total data byte length by multiplies 
         // a 'bytes/components' value (see above chart) by number of components stored 'NNNNNNNN' area          // a 'bytes/components' value (see above chart) by number of components stored 'NNNNNNNN' area
         uint components_count=endian_to_uint(is_big, entry.components_count);          uint components_count=endian_to_uint(is_big, entry.components_count);
         size_t value_size=component_size*components_count;          size_t value_size=component_size*components_count;
         // If its size is over 4bytes, 'DDDDDDDD' contains the offset to data stored address          // If its size is over 4bytes, 'DDDDDDDD' contains the offset to data stored address
         ValuePtr result;          Value* result;
   
         if(value_size<=4)          if(value_size<=4)
                 result=parse_IFD_entry_formatted_value(pool,                  result=parse_IFD_entry_formatted_value(
                         is_big, format,                           is_big, format, 
                         component_size, components_count,                           component_size, components_count, 
                         entry.value_or_offset_to_it);                          entry.value_or_offset_to_it);
Line 481  static ValuePtr parse_IFD_entry_value( Line 477  static ValuePtr parse_IFD_entry_value(
                 long remembered=reader.tell();                  long remembered=reader.tell();
                 {                  {
                         reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET);                          reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET);
                         const char *value;                          const char* value;
                         if(reader.read(value, value_size)<sizeof(value_size))                          if(reader.read(value, value_size)<sizeof(value_size))
                                 return ValuePtr(0);                                  return 0;
                         result=parse_IFD_entry_formatted_value(pool,                          result=parse_IFD_entry_formatted_value(
                                 is_big, format,                                   is_big, format, 
                                 component_size, components_count,                                   component_size, components_count, 
                                 (const uchar*)value);                                  (const uchar*)value);
Line 495  static ValuePtr parse_IFD_entry_value( Line 491  static ValuePtr parse_IFD_entry_value(
         return result;          return result;
 }  }
   
 static void parse_IFD(  static void parse_IFD(HashStringValue& hash,
                                           HashStringValue& hash,                        bool is_big, Measure_reader& reader, long tiff_base);
                                           bool is_big, Measure_reader& reader, long tiff_base);  
   
 static void parse_IFD_entryHashStringValue& hash,  static void parse_IFD_entry(HashStringValue& hash,
                                                         bool is_big, Measure_reader& reader, long tiff_base,                              bool is_big, Measure_reader& reader, long tiff_base,
                                                         JPG_Exif_IFD_entry& entry) {                              JPG_Exif_IFD_entry& entry) {
         ushort tag=endian_to_ushort(is_big, entry.tag);          ushort tag=endian_to_ushort(is_big, entry.tag);
         if(tag==JPG_IFD_TAG_EXIF_OFFSET) {          if(tag==JPG_IFD_TAG_EXIF_OFFSET) {
                 long remembered=reader.tell();                  long remembered=reader.tell();
                 {                  {
                         reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET);                          reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET);
                         parse_IFD(pool, hash, is_big, reader, tiff_base);                          parse_IFD(hash, is_big, reader, tiff_base);
                 }                  }
                 reader.seek(remembered, SEEK_SET);                  reader.seek(remembered, SEEK_SET);
                 return;                  return;
         }          }
                   
         if(ValuePtr value=parse_IFD_entry_value(pool, is_big, reader, tiff_base, entry)) {          if(Value* value=parse_IFD_entry_value(is_big, reader, tiff_base, entry)) {
                 if(const char* name=exif_tag_value2name.get(tag))                  if(const char* name=exif_tag_value2name.get(tag))
                         hash.put(String* (new String(name)), value);                          hash.put(String::Body(name), value);
                 else {                  else
                         const String& skey(new String(pool.format_integer(tag)));                          hash.put(String::Body::Format(tag), value);
                         hash.put(skey, value);  
                 }  
         }          }
 }  }
   
 static void parse_IFD(  static void parse_IFD(
                                           HashStringValue& hash,                        HashStringValue& hash,
                                           bool is_big, Measure_reader& reader, long tiff_base) {                        bool is_big, Measure_reader& reader, long tiff_base) {
         const char *buf;          const char* buf;
         if(reader.read(buf, sizeof(JPG_Exif_IFD_start))<sizeof(JPG_Exif_IFD_start))          if(reader.read(buf, sizeof(JPG_Exif_IFD_begin))<sizeof(JPG_Exif_IFD_begin))
                 return;                  return;
         JPG_Exif_IFD_start *start=(JPG_Exif_IFD_start *)buf;          JPG_Exif_IFD_begin *start=(JPG_Exif_IFD_begin *)buf;
   
         ushort directory_entry_count=endian_to_ushort(is_big, start->directory_entry_count);          ushort directory_entry_count=endian_to_ushort(is_big, start->directory_entry_count);
         for(int i=0; i<directory_entry_count; i++) {          for(int i=0; i<directory_entry_count; i++) {
                 if(reader.read(buf, sizeof(JPG_Exif_IFD_entry))<sizeof(JPG_Exif_IFD_entry))                  if(reader.read(buf, sizeof(JPG_Exif_IFD_entry))<sizeof(JPG_Exif_IFD_entry))
                         return;                          return;
   
                 parse_IFD_entry(pool, hash, is_big, reader, tiff_base, *(JPG_Exif_IFD_entry *)buf);                  parse_IFD_entry(hash, is_big, reader, tiff_base, *(JPG_Exif_IFD_entry *)buf);
         }          }
         // then goes: LLLLLLLL Offset to next IFD [not going there]          // then goes: LLLLLLLL Offset to next IFD [not going there]
 }  }
   
 static ValuePtr parse_exif(  static Value* parse_exif(Measure_reader& reader, const String& origin_string) {
                                            Measure_reader& reader,          const char* buf;
                                            const String* origin_string) {          if(reader.read(buf, sizeof(JPG_Exif_segment_begin))<sizeof(JPG_Exif_segment_begin))
         const char *buf;  
         if(reader.read(buf, sizeof(JPG_Exif_segment_start))<sizeof(JPG_Exif_segment_start))  
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not JPEG file - can not fully read Exif segment start");                          "not JPEG file - can not fully read Exif segment start");
   
         JPG_Exif_segment_start *start=(JPG_Exif_segment_start *)buf;          JPG_Exif_segment_begin *start=(JPG_Exif_segment_begin *)buf;
         if(memcmp(start->signature, "Exif\0\0", 4+2)!=0) //signature invalid?          if(memcmp(start->signature, "Exif\0\0", 4+2)!=0) //signature invalid?
                 return ValuePtr(0); // ignore invalid block                  return 0; // ignore invalid block
   
         uint tiff_base=reader.tell();          uint tiff_base=reader.tell();
         if(reader.read(buf, sizeof(JPG_Exif_TIFF_header))<sizeof(JPG_Exif_TIFF_header))          if(reader.read(buf, sizeof(JPG_Exif_TIFF_header))<sizeof(JPG_Exif_TIFF_header))
                 return ValuePtr(0);                  return 0;
   
         JPG_Exif_TIFF_header *head=(JPG_Exif_TIFF_header *)buf;          JPG_Exif_TIFF_header *head=(JPG_Exif_TIFF_header *)buf;
         bool is_big=head->byte_align_identifier[0]=='M'; // [M]otorola vs [I]ntel          bool is_big=head->byte_align_identifier[0]=='M'; // [M]otorola vs [I]ntel
Line 564  static ValuePtr parse_exif( Line 555  static ValuePtr parse_exif(
         uint first_IFD_offset=endian_to_uint(is_big, head->first_IFD_offset);          uint first_IFD_offset=endian_to_uint(is_big, head->first_IFD_offset);
         reader.seek(tiff_base+first_IFD_offset, SEEK_SET);          reader.seek(tiff_base+first_IFD_offset, SEEK_SET);
   
         VHashPtr vhash(new VHash);          VHash* vhash=new VHash;
   
         // IFD          // IFD
         parse_IFD(pool, vhash->hash(Exception::undefined_source), is_big, reader, tiff_base);          parse_IFD(vhash->hash(), is_big, reader, tiff_base);
   
         return vhash;          return vhash;
 }  }
   
 static void measure_jpegconst String* origin_string,   static void measure_jpeg(const String& origin_string, 
                          Measure_reader& reader, ushort& width, ushort& height, ValuePtr* exif) {                           Measure_reader& reader, ushort& width, ushort& height, Value** exif) {
         // JFIF format markers          // JFIF format markers
         const uchar MARKER=0xFF;          const uchar MARKER=0xFF;
         const uchar CODE_SIZE_A=0xC0;          const uchar CODE_SIZE_A=0xC0;
Line 582  static void measure_jpegconst String* or Line 573  static void measure_jpegconst String* or
         const uchar CODE_SIZE_D=0xC3;          const uchar CODE_SIZE_D=0xC3;
         const uchar CODE_EXIF=0xE1;          const uchar CODE_EXIF=0xE1;
   
         const char *buf;          const char* buf;
         const size_t prefix_size=2;          const size_t prefix_size=2;
         if(reader.read(buf, prefix_size)<prefix_size)          if(reader.read(buf, prefix_size)<prefix_size)
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not JPEG file - too small");                          "not JPEG file - too small");
         uchar *signature=(uchar *)buf;          uchar *signature=(uchar *)buf;
                   
         if(!(signature[0]==0xFF && signature[1]==0xD8))           if(!(signature[0]==0xFF && signature[1]==0xD8)) 
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not JPEG file - wrong signature");                          "not JPEG file - wrong signature");
   
         while(true) {          while(true) {
Line 604  static void measure_jpegconst String* or Line 595  static void measure_jpegconst String* or
         // Verify that it's a valid segment.          // Verify that it's a valid segment.
                 if(head->marker!=MARKER)                  if(head->marker!=MARKER)
                         throw Exception("image.format",                           throw Exception("image.format", 
                                 origin_string,                                   &origin_string, 
                                 "not JPEG file - marker not found");                                  "not JPEG file - marker not found");
   
                 switch(head->code) {                  switch(head->code) {
                 // http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html                  // http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
                 case CODE_EXIF:                  case CODE_EXIF:
                         if(exif && !*exif) // seen .jpg with some xml under EXIF tag, after real exif block :)                          if(exif && !*exif) // seen .jpg with some xml under EXIF tag, after real exif block :)
                                 *exif=parse_exif(pool, reader, origin_string);                                  *exif=parse_exif(reader, origin_string);
                         break;                          break;
   
                 case CODE_SIZE_A:                  case CODE_SIZE_A:
Line 622  static void measure_jpegconst String* or Line 613  static void measure_jpegconst String* or
                                 // Segments that contain size info                                  // Segments that contain size info
                                 if(reader.read(buf, sizeof(JPG_Size_segment_body))<sizeof(JPG_Size_segment_body))                                  if(reader.read(buf, sizeof(JPG_Size_segment_body))<sizeof(JPG_Size_segment_body))
                                         throw Exception("image.format",                                           throw Exception("image.format", 
                                                 origin_string,                                                   &origin_string, 
                                                 "not JPEG file - can not fully read Size segment");                                                  "not JPEG file - can not fully read Size segment");
                                 JPG_Size_segment_body *body=(JPG_Size_segment_body *)buf;                                  JPG_Size_segment_body *body=(JPG_Size_segment_body *)buf;
                                                                   
Line 636  static void measure_jpegconst String* or Line 627  static void measure_jpegconst String* or
         }          }
   
         throw Exception("image.format",           throw Exception("image.format", 
                 origin_string,                   &origin_string, 
                 "broken JPEG file - size frame not found");                  "broken JPEG file - size frame not found");
 }  }
   
 static void measure_pngconst String* origin_string,   static void measure_png(const String& origin_string, 
                          Measure_reader& reader, ushort& width, ushort& height) {                           Measure_reader& reader, ushort& width, ushort& height) {
   
         const char *buf;          const char* buf;
         const int head_size=sizeof(PNG_Header);          const size_t head_size=sizeof(PNG_Header);
         if(reader.read(buf, head_size)<head_size)          if(reader.read(buf, head_size)<head_size)
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not PNG file - too small");                          "not PNG file - too small");
         PNG_Header *head=(PNG_Header *)buf;          PNG_Header *head=(PNG_Header *)buf;
   
         if(strncmp(head->signature, "IHDR", 4)!=0)          if(strncmp(head->signature, "IHDR", 4)!=0)
                 throw Exception("image.format",                   throw Exception("image.format", 
                         origin_string,                           &origin_string, 
                         "not PNG file - wrong signature");                                "not PNG file - wrong signature");      
   
         width=endian_to_ushort(true, head->width);          width=endian_to_ushort(true, head->width);
Line 662  static void measure_pngconst String* ori Line 653  static void measure_pngconst String* ori
   
 // measure center  // measure center
   
 static void measureconst String& file_name,   static void measure(const String& file_name, 
                          Measure_reader& reader, ushort& width, ushort& height, ValuePtr* exif) {                           Measure_reader& reader, ushort& width, ushort& height, Value** exif) {
         const char* file_name_cstr=file_name->cstr(String::UL_FILE_SPEC);          const char* file_name_cstr=file_name.cstr(String::L_FILE_SPEC);
         if(const char* cext=strrchr(file_name_cstr, '.')) {          if(const char* cext=strrchr(file_name_cstr, '.')) {
                 cext++;                  cext++;
                 if(strcasecmp(cext, "GIF")==0)                  if(strcasecmp(cext, "GIF")==0)
                         measure_gif(pool, file_name, reader, width, height);                          measure_gif(file_name, reader, width, height);
                 else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)                   else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0) 
                         measure_jpeg(pool, file_name, reader, width, height, exif);                          measure_jpeg(file_name, reader, width, height, exif);
                 else if(strcasecmp(cext, "PNG")==0)                  else if(strcasecmp(cext, "PNG")==0)
                         measure_png(pool, file_name, reader, width, height);                          measure_png(file_name, reader, width, height);
                 else                  else
                         throw Exception("image.format",                           throw Exception("image.format", 
                                 file_name,                                   &file_name, 
                                 "unhandled image file name extension '%s'", cext);                                  "unhandled image file name extension '%s'", cext);
         } else          } else
                 throw Exception("image.format",                   throw Exception("image.format", 
                         file_name,                           &file_name, 
                         "can not determine image type - no file name extension");                          "can not determine image type - no file name extension");
 }  }
   
Line 689  static void measureconst String& file_na Line 680  static void measureconst String& file_na
 struct File_measure_action_info {  struct File_measure_action_info {
         ushort* width;          ushort* width;
         ushort* height;          ushort* height;
         ValuePtr* exif;          Value** exif;
         const String& file_name;          const String* file_name;
 };  };
 #endif  #endif
 static void file_measure_action(  static void file_measure_action(
                                                                 struct stat& finfo, int f,                                                                   struct stat& /*finfo*/, int f, 
                                                                 const String& file_spec, const char* fname, bool as_text,                                                                  const String& /*file_spec*/, const char* fname, bool /*as_text*/,
                                                                 void *context) {                                                                  void *context) {
         File_measure_action_info& info=*static_cast<File_measure_action_info *>(context);          File_measure_action_info& info=*static_cast<File_measure_action_info *>(context);
   
         Measure_file_reader reader(pool, f, info.file_name, fname);          Measure_file_reader reader(f, *info.file_name, fname);
         measure(pool, info.file_name, reader, *info.width, *info.height, info.exif);          measure(*info.file_name, reader, *info.width, *info.height, info.exif);
 }  }
   
 static void _measure(Request& r, const String& method_name, MethodParams* params) {  static void _measure(Request& r, MethodParams& params) {
         =r.pool();          Value& data=params.as_no_junction(0, "data must not be code");
   
         ValuePtr data=params->as_no_junction(0, "data must not be code");  
   
         ushort width=0;          ushort width=0;
         ushort height=0;          ushort height=0;
         ValuePtr exif(0);          Value* exif=0;
         const String& file_name;          const String* file_name;
         if(file_name=data->get_string(&pool)) {          if((file_name=data.get_string())) {
                 File_measure_action_info info;                  File_measure_action_info info={
                 info.width=&width;                          &width, &height,
                 info.height=&height;                          &exif,
                 info.exif=&exif;                          file_name
                 info.file_name=file_name;                  };
                 file_read_action_under_lock(pool, r.absolute(file_name),                   file_read_action_under_lock(r.absolute(*file_name), 
                         "measure", file_measure_action, &info);                          "measure", file_measure_action, &info);
         } else {          } else {
                 VFilePtr vfile=data->as_vfile();                  VFile* vfile=data.as_vfile();
                 file_name=vfile->fields().get(name_name)->as_string(&pool);                  file_name=&vfile->fields().get(name_name)->as_string();
                 Measure_buf_reader reader(                  Measure_buf_reader reader(
                         vfile->value_ptr(),                          vfile->value_ptr(),
                         vfile->value_size(),                          vfile->value_size(),
                         file_name                          *file_name
                 );                  );
                 measure(pool, file_name, reader, width, height, &exif);                  measure(*file_name, reader, width, height, &exif);
         }          }
   
         GET_SELF(r, VImage).set(file_name, width, height, gdImagePtr(0), exif);          GET_SELF(r, VImage).set(file_name, width, height, 0, exif);
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Attrib_info {  struct Attrib_info {
         ;  
         String* tag; ///< html tag being constructed          String* tag; ///< html tag being constructed
         HashStringValue* skip; ///< tag attributes not to append to tag string [to skip]          HashStringValue* skip; ///< tag attributes not to append to tag string [to skip]
 };  };
 #endif  #endif
 static void append_attrib_pair(  static void append_attrib_pair(
                                                            HashStringValue::key_type key,                                  HashStringValue::key_type key, 
                                                            HashStringValue::value_type value,                                  HashStringValue::value_type value, 
                                                            Attrib_info* info) {                                 Attrib_info* info) {
         // skip user-specified and internal(starting with "line-") attributes           // skip user-specified and internal(starting with "line-") attributes 
         if(info->skip && info->skip->get(key) || key->pos("line-")==0)          if(info->skip && info->skip->get(key) || key.pos("line-")==0)
                 return;                  return;
   
         // src="a.gif" width=123 ismap[=-1]          // src="a.gif" width=123 ismap[=-1]
         *info->tag << " " << key;          *info->tag << " " << key;
         if(value->is_string() || value->as_int()>=0)          if(value->is_string() || value->as_int()>=0)
                 *info->tag << "=\"" << value->as_string(info->pool) << "\"";                  *info->tag << "=\"" << value->as_string() << "\"";
 }  }
 static void _html(Request& r, const String& method_name, MethodParams* params) {  static void _html(Request& r, MethodParams& params) {
         =r.pool();  
   
         String tag;          String tag;
         tag << "<img";          tag << "<img";
Line 763  static void _html(Request& r, const Stri Line 750  static void _html(Request& r, const Stri
         const HashStringValue& fields=GET_SELF(r, VImage).fields();          const HashStringValue& fields=GET_SELF(r, VImage).fields();
         HashStringValue* attribs=0;          HashStringValue* attribs=0;
   
         if(params->count()) {          if(params.count()) {
                 // for backward compatibility: someday was ^html{}                  // for backward compatibility: someday was ^html{}
                 ValuePtr vattribs=r.process_to_value(params->get(0),                  Value& vattribs=r.process_to_value(params[0],
                         /*0/*no name* /,*/  
                         false/*don't intercept string*/);                          false/*don't intercept string*/);
                 if(!vattribs->is_string()) // allow empty                  if(!vattribs.is_string()) // allow empty
                         if(attribs=vattribs->get_hash(method_name)) {                          if((attribs=vattribs.get_hash())) {
                                 Attrib_info info={&pool, &tag};                                  Attrib_info info={&tag, 0};
                                 attribs->for_each(append_attrib_pair, &info);                                  attribs->for_each(append_attrib_pair, &info);
                         } else                          } else
                                 throw Exception("parser.runtime",                                   throw Exception("parser.runtime", 
                                         method_name,                                           0, 
                                         "attributes must be hash");                                          "attributes must be hash");
         }          }
   
         {          {
                 Attrib_info info={&pool, &tag, attribs};                  Attrib_info info={&tag, attribs};
                 fields.for_each(append_attrib_pair, &info);                  fields.for_each(append_attrib_pair, &info);
         }          }
         tag << " />";          tag << " />";
Line 787  static void _html(Request& r, const Stri Line 773  static void _html(Request& r, const Stri
 }  }
   
 /// @test wrap FILE to auto-object  /// @test wrap FILE to auto-object
 static gdImagePtr load(Request& r, const String& method_name,   static gdImage* load(Request& r, 
                                          const String& file_name){                                           const String& file_name){
         =r.pool();          const char* file_name_cstr=r.absolute(file_name).cstr(String::L_FILE_SPEC);
   
         const char* file_name_cstr=r.absolute(file_name)->cstr(String::UL_FILE_SPEC);  
         if(FILE *f=fopen(file_name_cstr, "rb")) {          if(FILE *f=fopen(file_name_cstr, "rb")) {
                 gdImagePtr image(new gdImage);                  gdImage* image=new gdImage;
                 bool ok=image->CreateFromGif(f);                  bool ok=image->CreateFromGif(f);
                 fclose(f);                  fclose(f);
                 if(!ok)                  if(!ok)
                         throw Exception("image.format",                           throw Exception("image.format", 
                                 file_name,                                  &file_name,
                                 "is not in GIF format");                                  "is not in GIF format");
                 return image;                  return image;
         } else {          } else {
                 throw Exception("file.missing",                   throw Exception("file.missing", 
                         method_name,                           0, 
                         "can not open '%s'", file_name_cstr.get());                          "can not open '%s'", file_name_cstr);
                 return gdImagePtr(0);  
         }          }
 }  }
   
   
 static void _load(Request& r, const String& method_name, MethodParams* params) {  static void _load(Request& r, MethodParams& params) {
         =r.pool();          const String& file_name=params.as_string(0, "file name must not be code");
   
         const String& file_name=params->as_string(0, "file name must not be code");  
   
         gdImagePtr image=load(r, method_name, file_name);          gdImage* image=load(r, file_name);
         GET_SELF(r, VImage).set(file_name, image->SX(), image->SY(), image);          GET_SELF(r, VImage).set(&file_name, image->SX(), image->SY(), image);
 }  }
   
 static void _create(Request& r, const String& method_name, MethodParams* params) {  static void _create(Request& r, MethodParams& params) {
         =r.pool();          int width=params.as_int(0, "width must be int", r);
           int height=params.as_int(1, "height must be int", r);
         int width=params->as_int(0, "width must be int", r);  
         int height=params->as_int(1, "height must be int", r);  
         int bgcolor_value=0xffFFff;          int bgcolor_value=0xffFFff;
         if(params->count()>2)          if(params.count()>2)
                 bgcolor_value=params->as_int(2, "color must be int", r);                  bgcolor_value=params.as_int(2, "color must be int", r);
         gdImagePtr image(new gdImage);          gdImage* image=new gdImage;
         image->Create(width, height);          image->Create(width, height);
         image->FilledRectangle(0, 0, width-1, height-1, image->Color(bgcolor_value));          image->FilledRectangle(0, 0, width-1, height-1, image->Color(bgcolor_value));
         GET_SELF(r, VImage).set(0, width, height, image);          GET_SELF(r, VImage).set(0, width, height, image);
 }  }
   
 static void _gif(Request& r, const String& method_name, MethodParams* params) {  static void _gif(Request& r, MethodParams& params) {
         =r.pool();          gdImage& image=GET_SELF(r, VImage).image();
   
         gdImagePtr image=GET_SELF(r, VImage).image;  
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         // could _ but don't thing it's wise to use $image.src for vfile.name          const String *file_name=0;
           if(params.count()>0)
                   file_name=&params.as_string(0, "file name must be string");
   
         String out; image->Gif(pool, out);          gdBuf buf=image.Gif();
                   
         VFilePtr vfile(new VFile);          VFile& vfile=*new VFile;
         ValuePtr content_type(new VString(String* (new String("image/gif"))));          Value* content_type=new VString(*new String("image/gif"));
         vfile->set(pool, false/*not tainted*/,           vfile.set(false/*not tainted*/, 
                 out.cstr(), out.size(), 0, content_type);                  (const char*)buf.ptr, buf.size, 
                   file_name? file_name->cstr(String::L_FILE_SPEC): 0, 
                   content_type);
   
         r.write_no_lang(vfile);          r.write_no_lang(vfile);
 }  }
   
 static void _line(Request& r, const String& method_name, MethodParams* params) {  static void _line(Request& r, MethodParams& params) {
         =r.pool();          gdImage& image=GET_SELF(r, VImage).image();
   
         gdImagePtr image=GET_SELF(r, VImage).image;          image.Line(
         if(!image)                  params.as_int(0, "x0 must be int", r), 
                 throw Exception(0,                   params.as_int(1, "y0 must be int", r), 
                         method_name,                   params.as_int(2, "x1 must be int", r), 
                         "does not contain an image");                  params.as_int(3, "y1 must be int", r), 
                   image.Color(params.as_int(4, "color must be int", r)));
         image->Line(  }
                 params->as_int(0, "x0 must be int", r),   
                 params->as_int(1, "y0 must be int", r),   static void _fill(Request& r, MethodParams& params) {
                 params->as_int(2, "x1 must be int", r),           gdImage& image=GET_SELF(r, VImage).image();
                 params->as_int(3, "y1 must be int", r),   
                 image->Color(params->as_int(4, "color must be int", r)));          image.Fill(
 }                  params.as_int(0, "x must be int", r), 
                   params.as_int(1, "y must be int", r), 
 static void _fill(Request& r, const String& method_name, MethodParams* params) {                  image.Color(params.as_int(2, "color must be int", r)));
         =r.pool();  }
   
         gdImagePtr image=GET_SELF(r, VImage).image;  static void _rectangle(Request& r, MethodParams& params) {
         if(!image)          gdImage& image=GET_SELF(r, VImage).image();
                 throw Exception(0,   
                         method_name,           image.Rectangle(
                         "does not contain an image");                  params.as_int(0, "x0 must be int", r), 
                   params.as_int(1, "y0 must be int", r), 
         image->Fill(                  params.as_int(2, "x1 must be int", r), 
                 params->as_int(0, "x must be int", r),                   params.as_int(3, "y1 must be int", r), 
                 params->as_int(1, "y must be int", r),                   image.Color(params.as_int(4, "color must be int", r)));
                 image->Color(params->as_int(2, "color must be int", r)));  }
 }  
   static void _bar(Request& r, MethodParams& params) {
 static void _rectangle(Request& r, const String& method_name, MethodParams* params) {          gdImage& image=GET_SELF(r, VImage).image();
         =r.pool();  
           image.FilledRectangle(
         gdImagePtr image=GET_SELF(r, VImage).image;                  params.as_int(0, "x0 must be int", r), 
         if(!image)                  params.as_int(1, "y0 must be int", r), 
                 throw Exception(0,                   params.as_int(2, "x1 must be int", r), 
                         method_name,                   params.as_int(3, "y1 must be int", r), 
                         "does not contain an image");                  image.Color(params.as_int(4, "color must be int", r)));
   
         image->Rectangle(  
                 params->as_int(0, "x0 must be int", r),   
                 params->as_int(1, "y0 must be int", r),   
                 params->as_int(2, "x1 must be int", r),   
                 params->as_int(3, "y1 must be int", r),   
                 image->Color(params->as_int(4, "color must be int", r)));  
 }  
   
 static void _bar(Request& r, const String& method_name, MethodParams* params) {  
         =r.pool();  
   
         gdImagePtr image=GET_SELF(r, VImage).image;  
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         image->FilledRectangle(  
                 params->as_int(0, "x0 must be int", r),   
                 params->as_int(1, "y0 must be int", r),   
                 params->as_int(2, "x1 must be int", r),   
                 params->as_int(3, "y1 must be int", r),   
                 image->Color(params->as_int(4, "color must be int", r)));  
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 static void add_point(Table::element_type row,   static void add_point(Table::element_type row, 
                                           gdImage::Point **p) {                                            gdImage::Point **p) {
           if(row->count()!=2)
                   throw Exception(0,
                           0,
                           "coordinates table must contain two columns: x and y values");
         (**p).x=row->get(0)->as_int();          (**p).x=row->get(0)->as_int();
         (**p).y=row->get(1)->as_int();          (**p).y=row->get(1)->as_int();
         (*p)++;          (*p)++;
 }  }
 #endif  #endif
 static void _replace(Request& r, const String& method_name, MethodParams* params) {  static void _replace(Request& r, MethodParams& params) {
         =r.pool();          gdImage& image=GET_SELF(r, VImage).image();
   
         gdImagePtr image=GET_SELF(r, VImage).image;  
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         Table* table=params->as_no_junction(2, "coordinates must not be code")->get_table();          Table* table=params.as_no_junction(2, "coordinates must not be code").get_table();
         if(!table)           if(!table) 
                 throw Exception(0,                  throw Exception(0,
                         method_name,                          0,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point *all_p=new gdImage::Point[table->count()];          gdImage::Point *all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point *add_p=all_p;              gdImage::Point *add_p=all_p;    
         table->for_each(add_point, &add_p);          table->for_each(add_point, &add_p);
         image->FilledPolygonReplaceColor(all_p, table->count(),           image.FilledPolygonReplaceColor(all_p, table->count(), 
                 image->Color(params->as_int(0, "src color must be int", r)),                  image.Color(params.as_int(0, "src color must be int", r)),
                 image->Color(params->as_int(1, "dest color must be int", r)));                  image.Color(params.as_int(1, "dest color must be int", r)));
 }  }
   
 static void _polyline(Request& r, const String& method_name, MethodParams* params) {  static void _polyline(Request& r, MethodParams& params) {
         =r.pool();          gdImage& image=GET_SELF(r, VImage).image();
   
         gdImagePtr image=GET_SELF(r, VImage).image;          Table* table=params.as_no_junction(1, "coordinates must not be code").get_table();
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         Table* table=params->as_no_junction(1, "coordinates must not be code")->get_table();  
         if(!table)           if(!table) 
                 throw Exception(0,                  throw Exception(0,
                         method_name,                          0,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point* all_p=new gdImage::Point[table->count()];          gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point *add_p=all_p;              gdImage::Point *add_p=all_p;    
         table->for_each(add_point, &add_p);          table->for_each(add_point, &add_p);
         image->Polygon(all_p, table->count(),           image.Polygon(all_p, table->count(), 
                 image->Color(params->as_int(0, "color must be int", r)),                  image.Color(params.as_int(0, "color must be int", r)),
                 false/*not closed*/);                  false/*not closed*/);
 }  }
   
 static void _polygon(Request& r, const String& method_name, MethodParams* params) {  static void _polygon(Request& r, MethodParams& params) {
         =r.pool();          gdImage& image=GET_SELF(r, VImage).image();
   
         gdImagePtr image=GET_SELF(r, VImage).image;  
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         Table* table=params->as_no_junction(1, "coordinates must not be code")->get_table();          Table* table=params.as_no_junction(1, "coordinates must not be code").get_table();
         if(!table)           if(!table) 
                 throw Exception(0,                  throw Exception(0,
                         method_name,                          0,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point* all_p=new gdImage::Point[table->count()];          gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point *add_p=all_p;              gdImage::Point *add_p=all_p;    
         table->for_each(add_point, &add_p);          table->for_each(add_point, &add_p);
         image->Polygon(all_p, table->count(),           image.Polygon(all_p, table->count(), 
                 image->Color(params->as_int(0, "color must be int", r)));                  image.Color(params.as_int(0, "color must be int", r)));
 }  }
   
 static void _polybar(Request& r, const String& method_name, MethodParams* params) {  static void _polybar(Request& r, MethodParams& params) {
         =r.pool();          gdImage& image=GET_SELF(r, VImage).image();
   
         gdImagePtr image=GET_SELF(r, VImage).image;          Table* table=params.as_no_junction(1, "coordinates must not be code").get_table();
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         Table* table=params->as_no_junction(1, "coordinates must not be code")->get_table();  
         if(!table)           if(!table) 
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         method_name,                          0,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point* all_p=new gdImage::Point[table->count()];          gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point *add_p=all_p;              gdImage::Point *add_p=all_p;    
         table->for_each(add_point, &add_p);          table->for_each(add_point, &add_p);
         image->FilledPolygon(all_p, table->count(),           image.FilledPolygon(all_p, table->count(), 
                 image->Color(params->as_int(0, "color must be int", r)));                  image.Color(params.as_int(0, "color must be int", r)));
 }  }
   
 // font  // font
Line 1028  const int Font::letter_spacing=1; Line 961  const int Font::letter_spacing=1;
   
 Font::Font(//,   Font::Font(//, 
         const String& aalphabet,           const String& aalphabet, 
         gdImagePtr aifont, int aheight, int amonospace, int aspacebarspace):          gdImage* aifont, int aheight, int amonospace, int aspacebarspace):
         alphabet(aalphabet),   
         height(aheight), monospace(amonospace),  spacebarspace(aspacebarspace),          height(aheight), monospace(amonospace),  spacebarspace(aspacebarspace),
         ifont(aifont) {          ifont(aifont),
           alphabet(aalphabet)     {
 }  }
   
 /* ******************************** char ********************************** */  /* ******************************** char ********************************** */
   
 int Font::index_of(char ch) {  size_t Font::index_of(char ch) {
         if(ch==' ') return -1;          if(ch==' ') return STRING_NOT_FOUND;
         return alphabet->pos(&ch, 1);          return alphabet.pos(ch);
 }  }
   
 int Font::index_width(int index) {  int Font::index_width(size_t index) {
         if(index<0)          if(index==STRING_NOT_FOUND)
                 return spacebarspace;                  return spacebarspace;
         int tr=ifont->GetTransparent();          int tr=ifont->GetTransparent();
         for(int x=ifont->SX()-1; x>=0; x--) {          for(int x=ifont->SX()-1; x>=0; x--) {
Line 1053  int Font::index_width(int index) { Line 986  int Font::index_width(int index) {
         return 0;          return 0;
 }  }
   
 void Font::index_display(gdImagePtr image, int x, int y, int index){  void Font::index_display(gdImage& image, int x, int y, size_t index){
         if(index>=0)           if(index!=STRING_NOT_FOUND) 
                 ifont->Copy(*image, x, y, 0, Y(0), index_width(index), height);                  ifont->Copy(image, x, y, 0, Y(0), index_width(index), height);
 }  }
   
 /* ******************************** string ********************************** */  /* ******************************** string ********************************** */
Line 1066  int Font::step_width(int index) { Line 999  int Font::step_width(int index) {
   
 // counts trailing letter_spacing, consider this OK. useful for contiuations  // counts trailing letter_spacing, consider this OK. useful for contiuations
 int Font::string_width(const String& s){  int Font::string_width(const String& s){
         const char* cstr=s->cstr();          const char* cstr=s.cstr();
         int result=0;          int result=0;
         for(const char* current=cstr; *current; current++)          for(const char* current=cstr; *current; current++)
                 result+=step_width(index_of(*current));                  result+=step_width(index_of(*current));
         return result;          return result;
 }  }
   
 void Font::string_display(gdImagePtr image, int x, int y, const String& s){  void Font::string_display(gdImage& image, int x, int y, const String& s){
         const char* cstr=s->cstr();          const char* cstr=s.cstr();
         for(const char* current=cstr; *current; current++) {          for(const char* current=cstr; *current; current++) {
                 int index=index_of(*current);                  size_t index=index_of(*current);
                 index_display(image, x, y, index);                  index_display(image, x, y, index);
                 x+=step_width(index);                  x+=step_width(index);
         }          }
Line 1085  void Font::string_display(gdImagePtr ima Line 1018  void Font::string_display(gdImagePtr ima
 //  //
   
   
 static void _font(Request& r, const String& method_name, MethodParams* params) {  static void _font(Request& r, MethodParams& params) {
         =r.pool();          const String& alphabet=params.as_string(0, "alphabet must not be code");
           gdImage* image=load(r, params.as_string(1, "file_name must not be code"));
         const String& alphabet=params->as_string(0, "alphabet must not be code");          int spacebar_width=params.as_int(2, "spacebar_width must be int", r);
         gdImagePtr image=load(r, method_name, params->as_string(1, "file_name must not be code"));  
         int spacebar_width=params->as_int(2, "spacebar_width must be int", r);  
         int monospace_width;          int monospace_width;
         if(params->count()>3) {          if(params.count()>3) {
                 monospace_width=params->as_int(3, "monospace_width must be int", r);                  monospace_width=params.as_int(3, "monospace_width must be int", r);
                 if(!monospace_width)                  if(!monospace_width)
                         monospace_width=image->SX();                          monospace_width=image->SX();
         } else          } else
                 monospace_width=0;                  monospace_width=0;
   
         if(!alphabet->size())          if(!alphabet.length())
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         method_name,                          0,
                         "alphabet must not be empty");                          "alphabet must not be empty");
   
         if(int remainder=image->SY() % alphabet->size())          if(int remainder=image->SY() % alphabet.length())
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         method_name,                          0,
                         "font-file height(%d) not divisable by alphabet size(%d), remainder=%d",                          "font-file height(%d) not divisable by alphabet size(%d), remainder=%d",
                                 image->SY(), alphabet->size(), remainder);                                  image->SY(), alphabet.length(), remainder);
                   
         GET_SELF(r, VImage).font=FontPtr(new Font(//pool,           GET_SELF(r, VImage).set_font(new Font(
                 alphabet,                   alphabet, 
                 image,                   image, 
                 image->SY() / alphabet->size(), monospace_width, spacebar_width));                  image->SY() / alphabet.length(), monospace_width, spacebar_width));
 }  }
   
 static void _text(Request& r, const String& method_name, MethodParams* params) {  static void _text(Request& r, MethodParams& params) {
         =r.pool();          int x=params.as_int(0, "x must be int", r);
           int y=params.as_int(1, "y must be int", r);
         int x=params->as_int(0, "x must be int", r);          const String& s=params.as_string(2, "text must not be code");
         int y=params->as_int(1, "y must be int", r);  
         const String& s=params->as_string(2, "text must not be code");  
   
         VImage& vimage=GET_SELF(r, VImage);          VImage& vimage=GET_SELF(r, VImage);
         if(vimage.image)          vimage.font().string_display(vimage.image(), x, y, s);
                 if(vimage.font)  
                         vimage.font->string_display(vimage.image, x, y, s);  
                 else  
                         throw Exception("parser.runtime",  
                                 method_name,  
                                 "set the font first");  
         else  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
 }  }
   
 static void _length(Request& r, const String& method_name, MethodParams* params) {  static void _length(Request& r, MethodParams& params) {
         =r.pool();          const String& s=params.as_string(0, "text must not be code");
   
         const const String& s=params->as_string(0, "text must not be code");  
   
         VImage& vimage=GET_SELF(r, VImage);          VImage& vimage=GET_SELF(r, VImage);
         if(vimage.image)          r.write_no_lang(*new VInt(vimage.font().string_width(s)));
                 if(vimage.font) {  }
                         r.write_no_lang(ValuePtr(new VInt(vimage.font->string_width(s))));  
                 } else  static void _arc(Request& r, MethodParams& params) {
                         throw Exception("parser.runtime",          gdImage& image=GET_SELF(r, VImage).image();
                                 method_name,  
                                 "set the font first");          image.Arc(
         else                  params.as_int(0, "center_x must be int", r), 
                 throw Exception(0,                   params.as_int(1, "center_y must be int", r), 
                         method_name,                   params.as_int(2, "width must be int", r), 
                         "does not contain an image");                  params.as_int(3, "height must be int", r), 
 }                  params.as_int(4, "start degrees must be int", r), 
                   params.as_int(5, "end degrees must be int", r), 
 static void _arc(Request& r, const String& method_name, MethodParams* params) {                  image.Color(params.as_int(6, "cx must be int", r)));
         =r.pool();  }
   
         gdImagePtr image=GET_SELF(r, VImage).image;  static void _sector(Request& r, MethodParams& params) {
         if(!image)          gdImage& image=GET_SELF(r, VImage).image();
                 throw Exception(0,   
                         method_name,           image.Sector(
                         "does not contain an image");                  params.as_int(0, "center_x must be int", r), 
                   params.as_int(1, "center_y must be int", r), 
         image->Arc(                  params.as_int(2, "width must be int", r), 
                 params->as_int(0, "center_x must be int", r),                   params.as_int(3, "height must be int", r), 
                 params->as_int(1, "center_y must be int", r),                   params.as_int(4, "start degrees must be int", r), 
                 params->as_int(2, "width must be int", r),                   params.as_int(5, "end degrees must be int", r), 
                 params->as_int(3, "height must be int", r),                   image.Color(params.as_int(6, "color must be int", r)));
                 params->as_int(4, "start degrees must be int", r),   }
                 params->as_int(5, "end degrees must be int", r),   
                 image->Color(params->as_int(6, "cx must be int", r)));  static void _circle(Request& r, MethodParams& params) {
 }          gdImage& image=GET_SELF(r, VImage).image();
   
 static void _sector(Request& r, const String& method_name, MethodParams* params) {          int size=params.as_int(2, "radius must be int", r)*2;
         =r.pool();          image.Arc(
                   params.as_int(0, "center_x must be int", r), 
         gdImagePtr image=GET_SELF(r, VImage).image;                  params.as_int(1, "center_y must be int", r), 
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         image->Sector(  
                 params->as_int(0, "center_x must be int", r),   
                 params->as_int(1, "center_y must be int", r),   
                 params->as_int(2, "width must be int", r),   
                 params->as_int(3, "height must be int", r),   
                 params->as_int(4, "start degrees must be int", r),   
                 params->as_int(5, "end degrees must be int", r),   
                 image->Color(params->as_int(6, "color must be int", r)));  
 }  
   
 static void _circle(Request& r, const String& method_name, MethodParams* params) {  
         =r.pool();  
   
         gdImagePtr image=GET_SELF(r, VImage).image;  
         if(!image)  
                 throw Exception(0,   
                         method_name,   
                         "does not contain an image");  
   
         int size=params->as_int(2, "radius must be int", r)*2;  
         image->Arc(  
                 params->as_int(0, "center_x must be int", r),   
                 params->as_int(1, "center_y must be int", r),   
                 size, //w                  size, //w
                 size, //h                  size, //h
                 0, //s                  0, //s
                 360, //e                  360, //e
                 image->Color(params->as_int(3, "color must be int", r)));                  image.Color(params.as_int(3, "color must be int", r)));
 }  }
   
 gdImagePtr as_imageconst String& method_name, MethodParams* params,   gdImage& as_image(MethodParams& params, int index, const char* msg) {
                                                 int index, const char* msg) {          Value& value=params.as_no_junction(index, msg);
         gdImagePtr src(0);  
   
         ValuePtr value=params->as_no_junction(index, msg);          if(Value* vimage=value.as(VIMAGE_TYPE, false)) {
                   return static_cast<VImage *>(vimage)->image();
         if(Value* vimage=value->as(VIMAGE_TYPE, false)) {  
                 src=static_cast<VImage *>(vimage)->image;  
                 if(!src)  
                         throw Exception("parser.runtime",   
                                 method_name,   
                                 msg);  
         } else          } else
                 throw Exception("parser.runtime",                   throw Exception("parser.runtime", 
                         method_name,                           0, 
                         msg);                          msg);
   
         return src;  
 }  }
   
 static void _copy(Request& r, const String& method_name, MethodParams* params) {  static void _copy(Request& r, MethodParams& params) {
         =r.pool();          gdImage& dest=GET_SELF(r, VImage).image();
   
         gdImagePtr dest=GET_SELF(r, VImage).image;          gdImage& src=as_image(params, 0, "src must be image");
         if(!dest)  
                 throw Exception(0,   
                         method_name,   
                         "self does not contain an image");  
   
         gdImagePtr src=as_image(pool, method_name, params, 0, "src must be image");  
   
         int sx=params->as_int(1, "src_x must be int", r);  
         int sy=params->as_int(2, "src_y must be int", r);  
         int sw=params->as_int(3, "src_w must be int", r);  
         int sh=params->as_int(4, "src_h must be int", r);  
         int dx=params->as_int(5, "dest_x must be int", r);  
         int dy=params->as_int(6, "dest_y must be int", r);  
         if(params->count()>1+2+2+2) {  
                 int dw=params->as_int(1+2+2+2, "dest_w must be int", r);  
                 int dh=(int)(params->count()>1+2+2+2+1?  
                         params->as_int(1+2+2+2+1, "dest_h must be int", r):sh*(((double)dw)/((double)sw)));  
                 int tolerance=params->count()>1+2+2+2+2?  
                         params->as_int(1+2+2+2+2, "tolerance must be int", r):150;  
   
                 src->CopyResampled(*dest, dx, dy, sx, sy, dw, dh, sw, sh, tolerance);          int sx=params.as_int(1, "src_x must be int", r);
           int sy=params.as_int(2, "src_y must be int", r);
           int sw=params.as_int(3, "src_w must be int", r);
           int sh=params.as_int(4, "src_h must be int", r);
           int dx=params.as_int(5, "dest_x must be int", r);
           int dy=params.as_int(6, "dest_y must be int", r);
           if(params.count()>1+2+2+2) {
                   int dw=params.as_int(1+2+2+2, "dest_w must be int", r);
                   int dh=(int)(params.count()>1+2+2+2+1?
                           params.as_int(1+2+2+2+1, "dest_h must be int", r):sh*(((double)dw)/((double)sw)));
                   int tolerance=params.count()>1+2+2+2+2?
                           params.as_int(1+2+2+2+2, "tolerance must be int", r):150;
   
                   src.CopyResampled(dest, dx, dy, sx, sy, dw, dh, sw, sh, tolerance);
         } else          } else
                 src->Copy(*dest, dx, dy, sx, sy, sw, sh);                  src.Copy(dest, dx, dy, sx, sy, sw, sh);
   }
   
   static void _pixel(Request& r, MethodParams& params) {
           gdImage& image=GET_SELF(r, VImage).image();
   
           int x=params.as_int(0, "x must be int", r); 
           int y=params.as_int(1, "y must be int", r);
   
           if(params.count()>2) {
                   image.SetPixel(x, y, 
                           image.Color(params.as_int(2, "color must be int", r)));
           } else 
                   r.write_no_lang(*new VInt(image.DecodeColor(image.GetPixel(x, y))));
 }  }
   
   
Line 1282  MImage::MImage(): Methoded("image") { Line 1169  MImage::MImage(): Methoded("image") {
         add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);          add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
   
         // ^image.gif[]          // ^image.gif[]
         add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 0);          add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 1);
   
         // ^image.line(x0;y0;x1;y1;color)          // ^image.line(x0;y0;x1;y1;color)
         add_native_method("line", Method::CT_DYNAMIC, _line, 5, 5);          add_native_method("line", Method::CT_DYNAMIC, _line, 5, 5);
Line 1329  MImage::MImage(): Methoded("image") { Line 1216  MImage::MImage(): Methoded("image") {
   
         // ^image.copy[source](src x;src y;src w;src h;dst x;dst y[;dest w[;dest h[;tolerance]]])          // ^image.copy[source](src x;src y;src w;src h;dst x;dst y[;dest w[;dest h[;tolerance]]])
         add_native_method("copy", Method::CT_DYNAMIC, _copy, 1+2+2+2, (1+2+2+2)+2+1);          add_native_method("copy", Method::CT_DYNAMIC, _copy, 1+2+2+2, (1+2+2+2)+2+1);
   
           // ^image.pixel(x;y)[(color)]
           add_native_method("pixel", Method::CT_DYNAMIC, _pixel, 2, 3);
 }  }

Removed from v.1.90.2.15.2.1  
changed lines
  Added in v.1.111.6.1


E-mail: