Diff for /parser3/src/classes/image.C between versions 1.84 and 1.90.2.15.2.4

version 1.84, 2002/11/27 09:43:53 version 1.90.2.15.2.4, 2003/03/20 08:11:12
Line 1 Line 1
 /** @file  /** @file
         Parser: @b image parser class.          Parser: @b image parser class.
   
         Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)          Copyright(c) 2001-2003 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)
 */  */
   
Line 18  static const char* IDENT_IMAGE_C="$Date$ Line 18  static const char* IDENT_IMAGE_C="$Date$
   
 #include "pa_config_includes.h"  #include "pa_config_includes.h"
   
   #include "pa_vmethod_frame.h"
   
 #include "gif.h"  #include "gif.h"
   
 #include "pa_common.h"  #include "pa_common.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_vfile.h"  #include "pa_vfile.h"
 #include "pa_vimage.h"  #include "pa_vimage.h"
   #include "pa_vdate.h"
   #include "pa_table.h"
   
 // class  // class
   
 class MImage : public Methoded {  class MImage: public Methoded {
 public: // VStateless_class  public: // VStateless_class
         Value *create_new_value(Pool& pool) { return new(pool) VImage(pool); }          ValuePtr create_new_value() { return ValuePtr(new VImage()); }
   
 public:  public:
         MImage(Pool& pool);          MImage();
   
 public: // Methoded  public: // Methoded
         bool used_directly() { return true; }          bool used_directly() { return true; }
   
 };  };
   
   // globals
   
   DECLARE_CLASS_VAR(image, new MImage, 0);
   
 // helpers  // helpers
   
   /// value of exif tag -> it's value
   class EXIF_tag_value2name: public Hash<int, const char* > {
   public:
           EXIF_tag_value2name() {
           // image JPEG Exif
                   #define EXIF_TAG(tag, name) \
                           put(tag, #name);
                   // Tags used by IFD0 (main image)
                   EXIF_TAG(0x010e,        ImageDescription);
                   EXIF_TAG(0x010f,        Make);
                   EXIF_TAG(0x0110,        Model);
                   EXIF_TAG(0x0112,        Orientation);
                   EXIF_TAG(0x011a,        XResolution);
                   EXIF_TAG(0x011b,        YResolution);
                   EXIF_TAG(0x0128,        ResolutionUnit);
                   EXIF_TAG(0x0131,        Software);
                   EXIF_TAG(0x0132,        DateTime);
                   EXIF_TAG(0x013e,        WhitePoint);
                   EXIF_TAG(0x013f,        PrimaryChromaticities);
                   EXIF_TAG(0x0211,        YCbCrCoefficients);
                   EXIF_TAG(0x0213,        YCbCrPositioning);
                   EXIF_TAG(0x0214,        ReferenceBlackWhite);
                   EXIF_TAG(0x8298,        Copyright);
                   EXIF_TAG(0x8769,        ExifOffset);
                   // Tags used by Exif SubIFD
                   EXIF_TAG(0x829a,        ExposureTime);
                   EXIF_TAG(0x829d,        FNumber);
                   EXIF_TAG(0x8822,        ExposureProgram);
                   EXIF_TAG(0x8827,        ISOSpeedRatings);
                   EXIF_TAG(0x9000,        ExifVersion);
                   EXIF_TAG(0x9003,        DateTimeOriginal);
                   EXIF_TAG(0x9004,        DateTimeDigitized);
                   EXIF_TAG(0x9101,        ComponentsConfiguration);
                   EXIF_TAG(0x9102,        CompressedBitsPerPixel);
                   EXIF_TAG(0x9201,        ShutterSpeedValue);
                   EXIF_TAG(0x9202,        ApertureValue);
                   EXIF_TAG(0x9203,        BrightnessValue);
                   EXIF_TAG(0x9204,        ExposureBiasValue);
                   EXIF_TAG(0x9205,        MaxApertureValue);
                   EXIF_TAG(0x9206,        SubjectDistance);
                   EXIF_TAG(0x9207,        MeteringMode);
                   EXIF_TAG(0x9208,        LightSource);
                   EXIF_TAG(0x9209,        Flash);
                   EXIF_TAG(0x920a,        FocalLength);
                   EXIF_TAG(0x927c,        MakerNote);
                   EXIF_TAG(0x9286,        UserComment);
                   EXIF_TAG(0x9290,        SubsecTime);
                   EXIF_TAG(0x9291,        SubsecTimeOriginal);
                   EXIF_TAG(0x9292,        SubsecTimeDigitized);
                   EXIF_TAG(0xa000,        FlashPixVersion);
                   EXIF_TAG(0xa001,        ColorSpace);
                   EXIF_TAG(0xa002,        ExifImageWidth);
                   EXIF_TAG(0xa003,        ExifImageHeight);
                   EXIF_TAG(0xa004,        RelatedSoundFile);
                   EXIF_TAG(0xa005,        ExifInteroperabilityOffset);
                   EXIF_TAG(0xa20e,        FocalPlaneXResolution);
                   EXIF_TAG(0xa20f,        FocalPlaneYResolution);
                   EXIF_TAG(0xa210,        FocalPlaneResolutionUnit);
                   EXIF_TAG(0xa215,        ExposureIndex);
                   EXIF_TAG(0xa217,        SensingMethod);
                   EXIF_TAG(0xa300,        FileSource);
                   EXIF_TAG(0xa301,        SceneType);
                   EXIF_TAG(0xa302,        CFAPattern);
                   // Misc Tags
                   EXIF_TAG(0x00fe,        NewSubfileType);
                   EXIF_TAG(0x00ff,        SubfileType);
                   EXIF_TAG(0x012d,        TransferFunction);
                   EXIF_TAG(0x013b,        Artist);
                   EXIF_TAG(0x013d,        Predictor);
                   EXIF_TAG(0x0142,        TileWidth);
                   EXIF_TAG(0x0143,        TileLength);
                   EXIF_TAG(0x0144,        TileOffsets);
                   EXIF_TAG(0x0145,        TileByteCounts);
                   EXIF_TAG(0x014a,        SubIFDs);
                   EXIF_TAG(0x015b,        JPEGTables);
                   EXIF_TAG(0x828d,        CFARepeatPatternDim);
                   EXIF_TAG(0x828e,        CFAPattern);
                   EXIF_TAG(0x828f,        BatteryLevel);
                   EXIF_TAG(0x83bb,        IPTC/NAA);
                   EXIF_TAG(0x8773,        InterColorProfile);
                   EXIF_TAG(0x8824,        SpectralSensitivity);
                   EXIF_TAG(0x8825,        GPSInfo);
                   EXIF_TAG(0x8828,        OECF);
                   EXIF_TAG(0x8829,        Interlace);
                   EXIF_TAG(0x882a,        TimeZoneOffset);
                   EXIF_TAG(0x882b,        SelfTimerMode);
                   EXIF_TAG(0x920b,        FlashEnergy);
                   EXIF_TAG(0x920c,        SpatialFrequencyResponse);
                   EXIF_TAG(0x920d,        Noise);
                   EXIF_TAG(0x9211,        ImageNumber);
                   EXIF_TAG(0x9212,        SecurityClassification);
                   EXIF_TAG(0x9213,        ImageHistory);
                   EXIF_TAG(0x9214,        SubjectLocation);
                   EXIF_TAG(0x9215,        ExposureIndex);
                   EXIF_TAG(0x9216,        TIFF/EPStandardID);
                   EXIF_TAG(0xa20b,        FlashEnergy);
                   EXIF_TAG(0xa20c,        SpatialFrequencyResponse);
                   EXIF_TAG(0xa214,        SubjectLocation);
                   #undef EXIF_TAG
           }
   } exif_tag_value2name;
   
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 class Measure_reader {  class Measure_reader {
 public:  public:
         virtual size_t read(const void *&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;
           int f;
   
 public:  public:
         Measure_file_reader(Pool& apool, int af, const String& afile_name, const char *afname):           Measure_file_reader(int af, const String& afile_name, const char* afname): 
                 pool(apool), file_name(afile_name), fname(afname), f(af) {                  pool(), file_name(afile_name), fname(afname), f(af) {
         }          }
   
         /*override*/size_t read(const void *&abuf, size_t limit) {          override size_t read(const char *&abuf, size_t limit) {
                 if(limit==0)                  if(limit==0)
                         return 0;                          return 0;
   
                 void *lbuf=pool.malloc(limit);                  char* lbuf=new 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 %lu bytes count not in [0..%lu] valid range", 
                         read_size, limit);                          read_size, limit);
   
                 return read_size;                  return read_size;
         }          }
   
         /*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("file.seek",                          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);
         }          }
   
         /*override*/long tell() { return lseek(f, 0, SEEK_CUR); }          override long tell() { return lseek(f, 0, SEEK_CUR); }
   
 private:  
         Pool& pool;  
         const String& file_name; const char *fname;  
         int f;  
 };  };
   
 class Measure_buf_reader: public Measure_reader {  class Measure_buf_reader: public Measure_reader {
   
           const char* buf; size_t size;
           const String& file_name; 
   
           size_t offset;
   
 public:  public:
         Measure_buf_reader(const void *abuf, size_t asize, const String& afile_name):           Measure_buf_reader(const char* abuf, size_t asize, const String& afile_name): 
                 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 void *&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=(const char*)buf+offset;                  abuf=buf+offset;
                 offset+=to_read;                  offset+=to_read;
                 return to_read;                  return to_read;
         }          }
   
         /*override*/void seek(long value, int whence) {          override void seek(long value, int whence) {
                 size_t new_offset;                  size_t new_offset;
                 switch(whence) {                  switch(whence) {
                 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: throw Exception("file.seek", 0, "whence #%d not supported", 0, whence); break;                  default: 
                           throw Exception(0, 
                                   0, 
                                   "whence #%d not supported", 0, whence); 
                           break; // never
                 }                  }
                                   
                 if((ssize_t)new_offset<0 || new_offset>size)                  if((ssize_t)new_offset<0 || new_offset>size)
                         throw Exception("file.seek",                          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;
         }          }
   
         /*override*/long tell() { return offset; }          override long tell() { return offset; }
   
 private:  
   
         const void *buf; size_t size;  
         const String& file_name;   
   
         size_t offset;  
 };  };
   
 #endif  #endif
Line 189  struct JPG_Exif_IFD_entry { Line 305  struct JPG_Exif_IFD_entry {
   
 #define JPG_IFD_TAG_EXIF_OFFSET 0x8769  #define JPG_IFD_TAG_EXIF_OFFSET 0x8769
   
   #define JPEG_EXIF_DATE_CHARS 20
   
 //  //
   
 inline ushort x_endian_to_ushort(uchar b0, uchar b1) {  inline ushort x_endian_to_ushort(uchar b0, uchar b1) {
Line 209  inline uint endian_to_uint(bool is_big, Line 327  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_gif(Pool& pool, const String *origin_string,   static void measure_gifconst String* origin_string, 
                          Measure_reader& reader, ushort& width, ushort& height) {                           Measure_reader& reader, ushort& width, ushort& height) {
   
         const void *buf;          const char *buf;
         const int head_size=sizeof(GIF_Header);          const int 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", 
Line 229  static void measure_gif(Pool& pool, cons Line 347  static void measure_gif(Pool& pool, cons
         height=endian_to_ushort(false, head->height);          height=endian_to_ushort(false, head->height);
 }  }
   
 static Value *parse_IFD_entry_formatted_one_value(Pool& pool,  static ValuePtr 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 new(pool) VInt(pool, (uchar)value[0]);                  return ValuePtr(new VInt((uchar)value[0]));
         case 3: // unsigned short          case 3: // unsigned short
                 return new(pool) VInt(pool, endian_to_ushort(is_big, value));                  return ValuePtr(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 new(pool) VDouble(pool, endian_to_uint(is_big, value));                  return ValuePtr(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 0;                                  return ValuePtr(0);
                         return new(pool) VDouble(pool, ((double)numerator)/denominator);                          return ValuePtr(new VDouble(((double)numerator)/denominator));
                 }                  }
         case 6: // signed byte          case 6: // signed byte
                 return new(pool) VInt(pool, (signed char)value[0]);                  return ValuePtr(new VInt((signed char)value[0]));
         case 8: // signed short          case 8: // signed short
                 return new(pool) VInt(pool, (signed short)endian_to_ushort(is_big, value));                  return ValuePtr(new VInt((signed short)endian_to_ushort(is_big, value)));
         case 9: // signed long          case 9: // signed long
                 return new(pool) VInt(pool, (signed int)endian_to_uint(is_big, value));                  return ValuePtr(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 0;                                  return ValuePtr(0);
                         return new(pool) VDouble(pool, numerator/denominator);                          return ValuePtr(new VDouble(numerator/denominator));
                 }                  }
                 /*                  /*
         case 11: // single float          case 11: // single float
Line 272  static Value *parse_IFD_entry_formatted_ Line 390  static Value *parse_IFD_entry_formatted_
                 */                  */
         };                };      
                   
         return 0;          return ValuePtr(0);
 }  }
   
 static Value *parse_IFD_entry_formatted_value(Pool& pool,  // date.C
   time_t cstr_to_time_t(char *cstr, const String& report_error_origin);
   
   static ValuePtr 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 size=components_count;
                 if(const char *premature_zero_pos=(const char *)memchr(cstr, 0, size))                  // Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes
                   if(size==JPEG_EXIF_DATE_CHARS 
                           && isdigit(cstr[0])
                           && cstr[JPEG_EXIF_DATE_CHARS-1]==0) {
                           char cstr_writable[JPEG_EXIF_DATE_CHARS]; 
                           strcpy(cstr_writable, cstr);
   
                           time_t t=cstr_to_time_t(cstr_writable, 
                                   0/* do not throw exception, just return bad result */);
                           if(t>=0)
                                   return ValuePtr(new VDate(t));
                   }
   
                   if(const char* premature_zero_pos=(const char* )memchr(cstr, 0, size))
                         size=premature_zero_pos-cstr;                          size=premature_zero_pos-cstr;
                 return new(pool) VString(*new(pool) String(pool, cstr, size, true/*tainted*/));                  return ValuePtr(new VString(String* (new String(cstr, size, 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);
   
         VHash& result=*new(pool) VHash(pool);          VHashPtr result(new VHash);
         Hash& hash=result.hash(0);          HashStringValue& hash=result->hash(0);
         for(uint i=0; i<components_count; i++, value+=component_size) {          for(uint i=0; i<components_count; i++, value+=component_size) {
                 String& skey=*new(pool) String(pool);                  char *key_cstr=new char[MAX_NUMBER];
                 {                  const String& skey(new String(key_cstr, snprintf(key_cstr, MAX_NUMBER, "%d", i)));
                         char *buf=(char *)pool.malloc(MAX_NUMBER);                  hash.put(skey, 
                         snprintf(buf, MAX_NUMBER, "%d", i);                          parse_IFD_entry_formatted_one_value(is_big, format, component_size, value));
                         skey << buf;  
                 }  
                 hash.put(skey, parse_IFD_entry_formatted_one_value(pool, is_big, format, component_size, value));  
         }          }
   
         return &result;          return result;
 }  }
   
 static Value *parse_IFD_entry_value(Pool& pool,  static ValuePtr 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 328  static Value *parse_IFD_entry_value(Pool Line 459  static Value *parse_IFD_entry_value(Pool
   
         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 0; // format out of range, ignoring                  return ValuePtr(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 0; // undefined format                  return ValuePtr(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
         Value *result;          ValuePtr result;
   
         if(value_size<=4)          if(value_size<=4)
                 result=parse_IFD_entry_formatted_value(pool,                  result=parse_IFD_entry_formatted_value(pool,
Line 350  static Value *parse_IFD_entry_value(Pool Line 481  static Value *parse_IFD_entry_value(Pool
                 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 void *value;                          const char *value;
                         if(reader.read(value, value_size)<sizeof(value_size))                          if(reader.read(value, value_size)<sizeof(value_size))
                                 return 0;                                  return ValuePtr(0);
                         result=parse_IFD_entry_formatted_value(pool,                          result=parse_IFD_entry_formatted_value(pool,
                                 is_big, format,                                   is_big, format, 
                                 component_size, components_count,                                   component_size, components_count, 
Line 364  static Value *parse_IFD_entry_value(Pool Line 495  static Value *parse_IFD_entry_value(Pool
         return result;          return result;
 }  }
   
 static void parse_IFD(Pool& pool,  static void parse_IFD(
                                           Hash& 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_entry(Pool& pool, Hash& hash,  static void parse_IFD_entryHashStringValue& 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);
Line 376  static void parse_IFD_entry(Pool& pool, Line 507  static void parse_IFD_entry(Pool& pool,
                 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(Value *value=parse_IFD_entry_value(pool, is_big, reader, tiff_base, entry)) {          if(ValuePtr value=parse_IFD_entry_value(is_big, reader, tiff_base, entry)) {
                 String& skey=*new(pool) String(pool);                  if(const char* name=exif_tag_value2name.get(tag))
                 {                          hash.put(String* (new String(name)), value);
                         char *buf=(char *)pool.malloc(MAX_NUMBER);                  else {
                         snprintf(buf, MAX_NUMBER, "%u", tag);                          const String& skey(new String(pool.format_integer(tag)));
                         skey << buf;  
                 }  
   
                 if(const char *name=(const char *)exif_tag_value2name->get(skey))  
                         hash.put(*new(pool) String(pool, name), value);  
                 else  
                         hash.put(skey, value);                          hash.put(skey, value);
                   }
         }          }
 }  }
   
 static void parse_IFD(Pool& pool,  static void parse_IFD(
                                           Hash& hash,                                            HashStringValue& hash,
                                           bool is_big, Measure_reader& reader, long tiff_base) {                                            bool is_big, Measure_reader& reader, long tiff_base) {
         const void *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_start))<sizeof(JPG_Exif_IFD_start))
                 return;                  return;
         JPG_Exif_IFD_start *start=(JPG_Exif_IFD_start *)buf;          JPG_Exif_IFD_start *start=(JPG_Exif_IFD_start *)buf;
Line 410  static void parse_IFD(Pool& pool, Line 536  static void parse_IFD(Pool& pool,
                 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 Value *parse_exif(Pool& pool,  static ValuePtr parse_exif(
                                            Measure_reader& reader,                                             Measure_reader& reader,
                                            const String *origin_string) {                                             const String* origin_string) {
         const void *buf;          const char *buf;
         if(reader.read(buf, sizeof(JPG_Exif_segment_start))<sizeof(JPG_Exif_segment_start))          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, 
Line 426  static Value *parse_exif(Pool& pool, Line 552  static Value *parse_exif(Pool& pool,
   
         JPG_Exif_segment_start *start=(JPG_Exif_segment_start *)buf;          JPG_Exif_segment_start *start=(JPG_Exif_segment_start *)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 0; // ignore invalid block                  return ValuePtr(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 0;                  return ValuePtr(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 438  static Value *parse_exif(Pool& pool, Line 564  static Value *parse_exif(Pool& pool,
         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);
   
         VHash& vhash=*new(pool) VHash(pool);          VHashPtr vhash(new VHash);
   
         // IFD          // IFD
         parse_IFD(pool, vhash.hash(0), is_big, reader, tiff_base);          parse_IFD(vhash->hash(0), is_big, reader, tiff_base);
   
         return &vhash;          return vhash;
 }  }
   
 static void measure_jpeg(Pool& pool, const String *origin_string,   static void measure_jpegconst String* origin_string, 
                          Measure_reader& reader, ushort& width, ushort& height, Value ** exif) {                           Measure_reader& reader, ushort& width, ushort& height, ValuePtr* 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 456  static void measure_jpeg(Pool& pool, con Line 582  static void measure_jpeg(Pool& pool, con
         const uchar CODE_SIZE_D=0xC3;          const uchar CODE_SIZE_D=0xC3;
         const uchar CODE_EXIF=0xE1;          const uchar CODE_EXIF=0xE1;
   
         const void *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", 
Line 485  static void measure_jpeg(Pool& pool, con Line 611  static void measure_jpeg(Pool& pool, con
                 // 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 514  static void measure_jpeg(Pool& pool, con Line 640  static void measure_jpeg(Pool& pool, con
                 "broken JPEG file - size frame not found");                  "broken JPEG file - size frame not found");
 }  }
   
 static void measure_png(Pool& pool, const String *origin_string,   static void measure_pngconst String* origin_string, 
                          Measure_reader& reader, ushort& width, ushort& height) {                           Measure_reader& reader, ushort& width, ushort& height) {
   
         const void *buf;          const char *buf;
         const int head_size=sizeof(PNG_Header);          const int 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", 
Line 536  static void measure_png(Pool& pool, cons Line 662  static void measure_png(Pool& pool, cons
   
 // measure center  // measure center
   
 static void measure(Pool& pool, const String& file_name,   static void measureconst String& file_name, 
                          Measure_reader& reader, ushort& width, ushort& height, Value ** exif) {                           Measure_reader& reader, ushort& width, ushort& height, ValuePtr* exif) {
         if(const char *cext=strrchr(file_name.cstr(String::UL_FILE_SPEC), '.')) {          const char* file_name_cstr=file_name->cstr(String::UL_FILE_SPEC);
           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 560  static void measure(Pool& pool, const St Line 687  static void measure(Pool& pool, const St
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct File_measure_action_info {  struct File_measure_action_info {
         ushort *width;          ushort* width;
         ushort *height;          ushort* height;
         Value ** exif;          ValuePtr* exif;
         const String *file_name;          const String& file_name;
 };  };
 #endif  #endif
 static void file_measure_action(Pool& pool,  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, const String& method_name, MethodParams* params) {
         Pool& pool=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;
         Value *exif=0;          ValuePtr exif(0);
         const String *file_name;          const String& file_name;
         if(file_name=data.get_string()) {          if(file_name=data->get_string(&pool)) {
                 File_measure_action_info info={&width, &height, &exif, file_name};                  File_measure_action_info info;
                 file_read_action_under_lock(pool, r.absolute(*file_name),                   info.width=&width;
                   info.height=&height;
                   info.exif=&exif;
                   info.file_name=file_name;
                   file_read_action_under_lock(r.absolute(file_name), 
                         "measure", file_measure_action, &info);                          "measure", file_measure_action, &info);
         } else {          } else {
                 const VFile& vfile=*data.as_vfile();                  VFilePtr vfile=data->as_vfile();
                 file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();                  file_name=vfile->fields().get(name_name)->as_string(&pool);
                 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);
         }          }
   
         VImage &vimage=*static_cast<VImage *>(r.get_self());          GET_SELF(r, VImage).set(file_name, width, height, gdImagePtr(0), exif);
         vimage.set(file_name, width, height, 0, exif);  
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Attrib_info {  struct Attrib_info {
         String *tag; ///< html tag being constructed          ;
         Hash *skip; ///< tag attributes not to append to tag string [to skip]          String* tag; ///< html tag being constructed
           HashStringValue* skip; ///< tag attributes not to append to tag string [to skip]
 };  };
 #endif  #endif
 static void append_attrib_pair(const Hash::Key& key, Hash::Val *val, void *info) {  static void append_attrib_pair(
         Attrib_info& ai=*static_cast<Attrib_info *>(info);                                                             HashStringValue::key_type key, 
                                                              HashStringValue::value_type value, 
                                                              Attrib_info* info) {
         // skip user-specified and internal(starting with "line-") attributes           // skip user-specified and internal(starting with "line-") attributes 
         if(ai.skip && ai.skip->get(key) || key.pos("line-")==0)          if(info->skip && info->skip->get(key) || key->pos("line-")==0)
                 return;                  return;
   
         Value& value=*static_cast<Value *>(val);  
         // src="a.gif" width=123 ismap[=-1]          // src="a.gif" width=123 ismap[=-1]
         *ai.tag << " " << key;          *info->tag << " " << key;
         if(value.is_string() || value.as_int()>=0)          if(value->is_string() || value->as_int()>=0)
                 *ai.tag << "=\"" << value.as_string() << "\"";                  *info->tag << "=\"" << value->as_string(info->pool) << "\"";
 }  }
 static void _html(Request& r, const String& method_name, MethodParams *params) {  static void _html(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         String tag(pool);          String tag;
         tag << "<img";          tag << "<img";
   
         const Hash& fields=static_cast<VImage *>(r.get_self())->fields();          const HashStringValue& fields=GET_SELF(r, VImage).fields();
         Hash *attribs=0;          HashStringValue* attribs=0;
   
         if(params->size()) {          if(params->count()) {
                 // for backward compatibility: someday was ^html{}                  // for backward compatibility: someday was ^html{}
                 Value &vattribs=r.process_to_value(params->get(0),                  ValuePtr vattribs=r.process_to_value(params->get(0),
                         /*0/*no name* /,*/                          /*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(method_name)) {
                                 Attrib_info attrib_info={&tag, 0};                                  Attrib_info info={&&tag};
                                 attribs->for_each(append_attrib_pair, &attrib_info);                                  attribs->for_each(append_attrib_pair, &info);
                         } else                          } else
                                 throw Exception("parser.runtime",                                   throw Exception("parser.runtime", 
                                         &method_name,                                           method_name, 
                                         "attributes must be hash");                                          "attributes must be hash");
         }          }
   
         Attrib_info attrib_info={&tag, attribs};          {
         fields.for_each(append_attrib_pair, &attrib_info);                  Attrib_info info={&&tag, attribs};
                   fields.for_each(append_attrib_pair, &info);
           }
         tag << " />";          tag << " />";
         r.write_pass_lang(tag);          r.write_pass_lang(tag);
 }  }
   
 /// @test wrap FILE to auto-object  /// @test wrap FILE to auto-object
 static gdImage *load(Request& r, const String& method_name,   static gdImagePtr load(Request& r, const String& method_name, 
                                          const String& file_name){                                           const String& file_name){
         Pool& pool=r.pool();  
   
         const char *file_name_cstr=r.absolute(file_name).cstr(String::UL_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")) {
                 gdImage& image=*new(pool) gdImage(pool);                  gdImagePtr 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,                           method_name, 
                         "can not open '%s'", file_name_cstr);                          "can not open '%s'", file_name_cstr.get());
                 return 0;                  return gdImagePtr(0);
         }          }
 }  }
   
   
 static void _load(Request& r, const String& method_name, MethodParams *params) {  static void _load(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=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");
   
         gdImage& image=*load(r, method_name, file_name);          gdImagePtr image=load(r, method_name, file_name);
         int width=image.SX();          GET_SELF(r, VImage).set(file_name, image->SX(), image->SY(), image);
         int height=image.SY();  
         static_cast<VImage *>(r.get_self())->set(&file_name, width, height, &image);  
 }  }
   
 static void _create(Request& r, const String& method_name, MethodParams *params) {  static void _create(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         int width=params->as_int(0, "width 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 height=params->as_int(1, "height must be int", r);
         int bgcolor_value=0xffFFff;          int bgcolor_value=0xffFFff;
         if(params->size()>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);
         gdImage& image=*new(pool) gdImage(pool);          gdImagePtr 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));
         static_cast<VImage *>(r.get_self())->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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         // could _ but don't thing it's wise to use $image.src for vfile.name          // could _ but don't thing it's wise to use $image.src for vfile.name
   
         String out(pool); image->Gif(out);          String out; image->Gif(out);
                   
         VFile& vfile=*new(pool) VFile(pool);          VFilePtr vfile(new VFile);
         Value *content_type=new(pool) VString(*new(pool) String(pool, "image/gif"));          ValuePtr content_type(new VString(String* (new String("image/gif"))));
         vfile.set(false/*not tainted*/,           vfile->set(false/*not tainted*/, 
                 out.cstr(), out.size(), 0, content_type);                  out.cstr(), out.size(), 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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         image->Line(          image->Line(
Line 740  static void _line(Request& r, const Stri Line 864  static void _line(Request& r, const Stri
                 image->Color(params->as_int(4, "color must be int", r)));                  image->Color(params->as_int(4, "color must be int", r)));
 }  }
   
 static void _fill(Request& r, const String& method_name, MethodParams *params) {  static void _fill(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         image->Fill(          image->Fill(
Line 755  static void _fill(Request& r, const Stri Line 878  static void _fill(Request& r, const Stri
                 image->Color(params->as_int(2, "color must be int", r)));                  image->Color(params->as_int(2, "color must be int", r)));
 }  }
   
 static void _rectangle(Request& r, const String& method_name, MethodParams *params) {  static void _rectangle(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         image->Rectangle(          image->Rectangle(
Line 772  static void _rectangle(Request& r, const Line 894  static void _rectangle(Request& r, const
                 image->Color(params->as_int(4, "color 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) {  static void _bar(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         image->FilledRectangle(          image->FilledRectangle(
Line 790  static void _bar(Request& r, const Strin Line 911  static void _bar(Request& r, const Strin
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 static void add_point(Array::Item *value, void *info) {  static void add_point(Table::element_type row, 
         Array& row=*static_cast<Array *>(value);                                            gdImage::Point **p) {
         gdImage::Point **p=static_cast<gdImage::Point **>(info);          (**p).x=row->get(0)->as_int();
                   (**p).y=row->get(1)->as_int();
         (**p).x=row.get_string(0)->as_int();  
         (**p).y=row.get_string(1)->as_int();  
         (*p)++;          (*p)++;
 }  }
 #endif  #endif
 static void _replace(Request& r, const String& method_name, MethodParams *params) {  static void _replace(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "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,                          method_name,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());          gdImage::Point *all_p=new 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->size(),           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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "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,                          method_name,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());          gdImage::Point* all_p=new 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->size(),           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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "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,                          method_name,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());          gdImage::Point* all_p=new 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->size(),           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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "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("parser.runtime",                  throw Exception("parser.runtime",
                         &method_name,                          method_name,
                         "coordinates must be table");                          "coordinates must be table");
   
         gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());          gdImage::Point* all_p=new 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->size(),           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
   
 #define Y(y)(y+index*height+1)  #define Y(y)(y+index*height)
   
 /// simple gdImage-based font storage & text output   // Font class
 class Font : public Pooled {  
 public:  const int Font::letter_spacing=1;
           
         const static int letter_spacing;  Font::Font(//, 
         int height;         ///< Font heigth          const String& aalphabet, 
         int monospace;      ///< Default char width          gdImagePtr aifont, int aheight, int amonospace, int aspacebarspace):
         int spacebarspace; ///< spacebar width          alphabet(aalphabet), 
         gdImage& ifont;          height(aheight), monospace(amonospace),  spacebarspace(aspacebarspace),
         const String& alphabet;          ifont(aifont) {
           }
         Font(Pool& pool,   
                 const String& aalphabet,   /* ******************************** char ********************************** */
                 gdImage& aifont, int aheight, int amonospace, int aspacebarspace) : Pooled(pool),   
                 alphabet(aalphabet),   int Font::index_of(char ch) {
                 height(aheight), monospace(amonospace),  spacebarspace(aspacebarspace),          if(ch==' ') return -1;
                 ifont(aifont) {          return alphabet->pos(&ch, 1);
         }  }
           
         /* ******************************** char ********************************** */  int Font::index_width(int index) {
                   if(index<0)
         int index_of(char ch) {                  return spacebarspace;
                 if(ch==' ') return -1;          int tr=ifont->GetTransparent();
                 return alphabet.pos(&ch, 1);          for(int x=ifont->SX()-1; x>=0; x--) {
         }                  for(int y=0; y<height; y++)
                                   if(ifont->GetPixel(x, Y(y))!=tr) 
         int index_width(int index) {                                  return x+1;
                 if(index<0)  
                         return spacebarspace;  
                 int tr=ifont.GetTransparent();  
                 for(int x=ifont.SX()-1; x>=0; x--) {  
                         for(int y=0; y<height-1; y++)  
                                 if(ifont.GetPixel(x, Y(y))!=tr)   
                                         return x+1;  
                 }  
                 return 0;  
         }  
           
         void index_display(gdImage& image, int x, int y, int index){  
                 if(index>=0)   
                         ifont.Copy(image, x, y, 0, Y(0), index_width(index), height-1);  
         }  
           
         /* ******************************** string ********************************** */  
           
         int string_width(const String& s){  
                 const char *cstr=s.cstr();  
                 int result=0;  
                 for(; *cstr; cstr++)  
                         result+=index_width(index_of(*cstr));  
                 return result;  
         }          }
                   return 0;
         void string_display(gdImage& image, int x, int y, const String& s){  }
                 const char *cstr=s.cstr();  
                 if(cstr) for(; *cstr; cstr++) {  void Font::index_display(gdImagePtr image, int x, int y, int index){
                         int index=index_of(*cstr);          if(index>=0) 
                         index_display(image, x, y, index);                  ifont->Copy(*image, x, y, 0, Y(0), index_width(index), height);
                         x+=letter_spacing + (monospace ? monospace : index_width(index));  }
                 }  
   /* ******************************** string ********************************** */
   
   int Font::step_width(int index) {
           return letter_spacing + (monospace ? monospace : index_width(index));
   }
   
   // counts trailing letter_spacing, consider this OK. useful for contiuations
   int Font::string_width(const String& s){
           const char* cstr=s->cstr();
           int result=0;
           for(const char* current=cstr; *current; current++)
                   result+=step_width(index_of(*current));
           return result;
   }
   
   void Font::string_display(gdImagePtr image, int x, int y, const String& s){
           const char* cstr=s->cstr();
           for(const char* current=cstr; *current; current++) {
                   int index=index_of(*current);
                   index_display(image, x, y, index);
                   x+=step_width(index);
         }          }
           }
 };  
 const int Font::letter_spacing=1;  //
   
 static void _font(Request& r, const String& method_name, MethodParams *params) {  
         Pool& pool=r.pool();  static void _font(Request& r, const String& method_name, MethodParams* params) {
   
         const String& alphabet=params->as_string(0, "alphabet must not be code");          const String& alphabet=params->as_string(0, "alphabet must not be code");
         gdImage& image=*load(r, method_name, params->as_string(1, "file_name must not be code"));          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 spacebar_width=params->as_int(2, "spacebar_width must be int", r);
         int monospace_width;          int monospace_width;
         if(params->size()>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->size())
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         &method_name,                          method_name,
                         "alphabet must not be empty");                          "alphabet must not be empty");
   
         if(int remainder=image.SY() % alphabet.size())          if(int remainder=image->SY() % alphabet->size())
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         &method_name,                          method_name,
                         "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->size(), remainder);
                   
         static_cast<VImage *>(r.get_self())->font=new(pool) Font(pool,           GET_SELF(r, VImage).font=FontPtr(new Font(//
                 alphabet,                   alphabet, 
                 image,                   image, 
                 image.SY() / alphabet.size(), monospace_width, spacebar_width);                  image->SY() / alphabet->size(), monospace_width, spacebar_width));
 }  }
   
 static void _text(Request& r, const String& method_name, MethodParams *params) {  static void _text(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         int x=params->as_int(0, "x must be int", r);          int x=params->as_int(0, "x must be int", r);
         int y=params->as_int(1, "y must be int", r);          int y=params->as_int(1, "y must be int", r);
         const String& s=params->as_string(2, "text must not be code");          const String& s=params->as_string(2, "text must not be code");
   
         VImage& vimage=*static_cast<VImage *>(r.get_self());          VImage& vimage=GET_SELF(r, VImage);
         if(vimage.image)          if(vimage.image)
                 if(vimage.font)                  if(vimage.font)
                         vimage.font->string_display(*vimage.image, x, y, s);                          vimage.font->string_display(vimage.image, x, y, s);
                 else                  else
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 &method_name,                                  method_name,
                                 "set the font first");                                  "set the font first");
         else          else
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
 }  }
   
 static void _length(Request& r, const String& method_name, MethodParams *params) {  static void _length(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=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=*static_cast<VImage *>(r.get_self());          VImage& vimage=GET_SELF(r, VImage);
         if(vimage.image)          if(vimage.image)
                 if(vimage.font) {                  if(vimage.font) {
                         r.write_no_lang(*new(pool) VInt(pool, vimage.font->string_width(s)));                          r.write_no_lang(ValuePtr(new VInt(vimage.font->string_width(s))));
                 } else                  } else
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 &method_name,                                  method_name,
                                 "set the font first");                                  "set the font first");
         else          else
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
 }  }
   
 static void _arc(Request& r, const String& method_name, MethodParams *params) {  static void _arc(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         image->Arc(          image->Arc(
Line 1048  static void _arc(Request& r, const Strin Line 1157  static void _arc(Request& r, const Strin
                 image->Color(params->as_int(6, "cx must be int", r)));                  image->Color(params->as_int(6, "cx must be int", r)));
 }  }
   
 static void _sector(Request& r, const String& method_name, MethodParams *params) {  static void _sector(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         image->Sector(          image->Sector(
Line 1067  static void _sector(Request& r, const St Line 1175  static void _sector(Request& r, const St
                 image->Color(params->as_int(6, "color 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) {  static void _circle(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *image=static_cast<VImage *>(r.get_self())->image;          gdImagePtr image=GET_SELF(r, VImage).image;
         if(!image)          if(!image)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "does not contain an image");                          "does not contain an image");
   
         int size=params->as_int(2, "radius must be int", r)*2;          int size=params->as_int(2, "radius must be int", r)*2;
Line 1087  static void _circle(Request& r, const St Line 1194  static void _circle(Request& r, const St
                 image->Color(params->as_int(3, "color must be int", r)));                  image->Color(params->as_int(3, "color must be int", r)));
 }  }
   
 gdImage& as_image(Pool& pool, const String& method_name, MethodParams *params,   gdImagePtr as_imageconst String& method_name, MethodParams* params, 
                                                 int index, const char *msg) {                                                  int index, const char* msg) {
         gdImage *src=0;          gdImagePtr src(0);
   
         Value& value=params->as_no_junction(index, msg);          ValuePtr value=params->as_no_junction(index, msg);
   
         if(Value *vimage=value.as(VIMAGE_TYPE, false)) {          if(Value* vimage=value->as(VIMAGE_TYPE, false)) {
                 src=static_cast<VImage *>(vimage)->image;                  src=static_cast<VImage *>(vimage)->image;
                 if(!src)                  if(!src)
                         throw Exception("parser.runtime",                           throw Exception("parser.runtime", 
                                 &method_name,                                   method_name, 
                                 msg);                                  msg);
         } else          } else
                 throw Exception("parser.runtime",                   throw Exception("parser.runtime", 
                         &method_name,                           method_name, 
                         msg);                          msg);
   
         return *src;          return src;
 }  }
   
 static void _copy(Request& r, const String& method_name, MethodParams *params) {  static void _copy(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         gdImage *dest=static_cast<VImage *>(r.get_self())->image;          gdImagePtr dest=GET_SELF(r, VImage).image;
         if(!dest)          if(!dest)
                 throw Exception(0,                   throw Exception(0, 
                         &method_name,                           method_name, 
                         "self does not contain an image");                          "self does not contain an image");
   
         gdImage& src=as_image(pool, method_name, params, 0, "src must be image");          gdImagePtr src=as_image(method_name, params, 0, "src must be image");
   
         int sx=params->as_int(1, "src_x must be int", r);          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 sy=params->as_int(2, "src_y must be int", r);
Line 1124  static void _copy(Request& r, const Stri Line 1230  static void _copy(Request& r, const Stri
         int sh=params->as_int(4, "src_h 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 dx=params->as_int(5, "dest_x must be int", r);
         int dy=params->as_int(6, "dest_y must be int", r);          int dy=params->as_int(6, "dest_y must be int", r);
         if(params->size()>1+2+2+2) {          if(params->count()>1+2+2+2) {
                 int dw=params->as_int(1+2+2+2, "dest_w must be int", r);                  int dw=params->as_int(1+2+2+2, "dest_w must be int", r);
                 int dh=(int)(params->size()>1+2+2+2+1?                  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)));                          params->as_int(1+2+2+2+1, "dest_h must be int", r):sh*(((double)dw)/((double)sw)));
                 int tolerance=params->size()>1+2+2+2+2?                  int tolerance=params->count()>1+2+2+2+2?
                         params->as_int(1+2+2+2+2, "tolerance must be int", r):150;                          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);                  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);
 }  }
   
   
 // constructor  // constructor
   
 MImage::MImage(Pool& apool) : Methoded(apool, "image") {  MImage::MImage(): Methoded("image") {
         // ^image:measure[DATA]          // ^image:measure[DATA]
         add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);          add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
   
Line 1203  MImage::MImage(Pool& apool) : Methoded(a Line 1309  MImage::MImage(Pool& apool) : Methoded(a
         // ^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);
 }  }
   
 // global variable  
   
 Methoded *image_class;  
   
 // creator  
   
 Methoded *MImage_create(Pool& pool) {  
         return image_class=new(pool) MImage(pool);  
 }  

Removed from v.1.84  
changed lines
  Added in v.1.90.2.15.2.4


E-mail: