property.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %            PPPP    RRRR    OOO   PPPP   EEEEE  RRRR   TTTTT  Y   Y          %
00007 %            P   P   R   R  O   O  P   P  E      R   R    T     Y Y           %
00008 %            PPPP    RRRR   O   O  PPPP   EEE    RRRR     T      Y            %
00009 %            P       R R    O   O  P      E      R R      T      Y            %
00010 %            P       R  R    OOO   P      EEEEE  R  R     T      Y            %
00011 %                                                                             %
00012 %                                                                             %
00013 %                         MagickCore Property Methods                         %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                John Cristy                                  %
00017 %                                 March 2000                                  %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "magick/studio.h"
00044 #include "magick/cache.h"
00045 #include "magick/color.h"
00046 #include "magick/compare.h"
00047 #include "magick/constitute.h"
00048 #include "magick/draw.h"
00049 #include "magick/effect.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/fx.h"
00053 #include "magick/fx-private.h"
00054 #include "magick/gem.h"
00055 #include "magick/geometry.h"
00056 #include "magick/image.h"
00057 #include "magick/layer.h"
00058 #include "magick/list.h"
00059 #include "magick/memory_.h"
00060 #include "magick/monitor.h"
00061 #include "magick/montage.h"
00062 #include "magick/option.h"
00063 #include "magick/profile.h"
00064 #include "magick/property.h"
00065 #include "magick/quantum.h"
00066 #include "magick/resource_.h"
00067 #include "magick/splay-tree.h"
00068 #include "magick/signature-private.h"
00069 #include "magick/statistic.h"
00070 #include "magick/string_.h"
00071 #include "magick/token.h"
00072 #include "magick/utility.h"
00073 #include "magick/xml-tree.h"
00074 
00075 /*
00076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00077 %                                                                             %
00078 %                                                                             %
00079 %                                                                             %
00080 %   C l o n e I m a g e P r o p e r t i e s                                   %
00081 %                                                                             %
00082 %                                                                             %
00083 %                                                                             %
00084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00085 %
00086 %  CloneImageProperties() clones one or more image properties.
00087 %
00088 %  The format of the CloneImageProperties method is:
00089 %
00090 %      MagickBooleanType CloneImageProperties(Image *image,
00091 %        const Image *clone_image)
00092 %
00093 %  A description of each parameter follows:
00094 %
00095 %    o image: the image.
00096 %
00097 %    o clone_image: the clone image.
00098 %
00099 */
00100 MagickExport MagickBooleanType CloneImageProperties(Image *image,
00101   const Image *clone_image)
00102 {
00103   assert(image != (Image *) NULL);
00104   assert(image->signature == MagickSignature);
00105   if (image->debug != MagickFalse)
00106     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00107   assert(clone_image != (const Image *) NULL);
00108   assert(clone_image->signature == MagickSignature);
00109   if (clone_image->debug != MagickFalse)
00110     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00111       clone_image->filename);
00112   (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
00113   (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
00114     MaxTextExtent);
00115   image->compression=clone_image->compression;
00116   image->quality=clone_image->quality;
00117   image->depth=clone_image->depth;
00118   image->background_color=clone_image->background_color;
00119   image->border_color=clone_image->border_color;
00120   image->matte_color=clone_image->matte_color;
00121   image->transparent_color=clone_image->transparent_color;
00122   image->gamma=clone_image->gamma;
00123   image->chromaticity=clone_image->chromaticity;
00124   image->rendering_intent=clone_image->rendering_intent;
00125   image->black_point_compensation=clone_image->black_point_compensation;
00126   image->units=clone_image->units;
00127   image->montage=(char *) NULL;
00128   image->directory=(char *) NULL;
00129   (void) CloneString(&image->geometry,clone_image->geometry);
00130   image->offset=clone_image->offset;
00131   image->x_resolution=clone_image->x_resolution;
00132   image->y_resolution=clone_image->y_resolution;
00133   image->page=clone_image->page;
00134   image->tile_offset=clone_image->tile_offset;
00135   image->extract_info=clone_image->extract_info;
00136   image->bias=clone_image->bias;
00137   image->filter=clone_image->filter;
00138   image->blur=clone_image->blur;
00139   image->fuzz=clone_image->fuzz;
00140   image->interlace=clone_image->interlace;
00141   image->interpolate=clone_image->interpolate;
00142   image->endian=clone_image->endian;
00143   image->gravity=clone_image->gravity;
00144   image->compose=clone_image->compose;
00145   image->scene=clone_image->scene;
00146   image->orientation=clone_image->orientation;
00147   image->dispose=clone_image->dispose;
00148   image->delay=clone_image->delay;
00149   image->ticks_per_second=clone_image->ticks_per_second;
00150   image->iterations=clone_image->iterations;
00151   image->total_colors=clone_image->total_colors;
00152   image->taint=clone_image->taint;
00153   image->progress_monitor=clone_image->progress_monitor;
00154   image->client_data=clone_image->client_data;
00155   image->start_loop=clone_image->start_loop;
00156   image->error=clone_image->error;
00157   image->signature=clone_image->signature;
00158   if (clone_image->properties != (void *) NULL)
00159     {
00160       if (image->properties != (void *) NULL)
00161         DestroyImageProperties(image);
00162       image->properties=CloneSplayTree((SplayTreeInfo *)
00163         clone_image->properties,(void *(*)(void *)) ConstantString,
00164         (void *(*)(void *)) ConstantString);
00165     }
00166   return(MagickTrue);
00167 }
00168 
00169 /*
00170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00171 %                                                                             %
00172 %                                                                             %
00173 %                                                                             %
00174 %   D e f i n e I m a g e P r o p e r t y                                     %
00175 %                                                                             %
00176 %                                                                             %
00177 %                                                                             %
00178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00179 %
00180 %  DefineImageProperty() associates a key/value pair with an image property.
00181 %
00182 %  The format of the DefineImageProperty method is:
00183 %
00184 %      MagickBooleanType DefineImageProperty(Image *image,
00185 %        const char *property)
00186 %
00187 %  A description of each parameter follows:
00188 %
00189 %    o image: the image.
00190 %
00191 %    o property: the image property.
00192 %
00193 */
00194 MagickExport MagickBooleanType DefineImageProperty(Image *image,
00195   const char *property)
00196 {
00197   char
00198     key[MaxTextExtent],
00199     value[MaxTextExtent];
00200 
00201   register char
00202     *p;
00203 
00204   assert(image != (Image *) NULL);
00205   assert(property != (const char *) NULL);
00206   (void) CopyMagickString(key,property,MaxTextExtent-1);
00207   for (p=key; *p != '\0'; p++)
00208     if (*p == '=')
00209       break;
00210   *value='\0';
00211   if (*p == '=')
00212     (void) CopyMagickString(value,p+1,MaxTextExtent);
00213   *p='\0';
00214   return(SetImageProperty(image,key,value));
00215 }
00216 
00217 /*
00218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00219 %                                                                             %
00220 %                                                                             %
00221 %                                                                             %
00222 %   D e l e t e I m a g e P r o p e r t y                                     %
00223 %                                                                             %
00224 %                                                                             %
00225 %                                                                             %
00226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00227 %
00228 %  DeleteImageProperty() deletes an image property.
00229 %
00230 %  The format of the DeleteImageProperty method is:
00231 %
00232 %      MagickBooleanType DeleteImageProperty(Image *image,const char *property)
00233 %
00234 %  A description of each parameter follows:
00235 %
00236 %    o image: the image.
00237 %
00238 %    o property: the image property.
00239 %
00240 */
00241 MagickExport MagickBooleanType DeleteImageProperty(Image *image,
00242   const char *property)
00243 {
00244   assert(image != (Image *) NULL);
00245   assert(image->signature == MagickSignature);
00246   if (image->debug != MagickFalse)
00247     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00248       image->filename);
00249   if (image->properties == (void *) NULL)
00250     return(MagickFalse);
00251   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
00252 }
00253 
00254 /*
00255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00256 %                                                                             %
00257 %                                                                             %
00258 %                                                                             %
00259 %   D e s t r o y I m a g e P r o p e r t i e s                               %
00260 %                                                                             %
00261 %                                                                             %
00262 %                                                                             %
00263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00264 %
00265 %  DestroyImageProperties() releases memory associated with image property
00266 %  values.
00267 %
00268 %  The format of the DestroyDefines method is:
00269 %
00270 %      void DestroyImageProperties(Image *image)
00271 %
00272 %  A description of each parameter follows:
00273 %
00274 %    o image: the image.
00275 %
00276 */
00277 MagickExport void DestroyImageProperties(Image *image)
00278 {
00279   assert(image != (Image *) NULL);
00280   assert(image->signature == MagickSignature);
00281   if (image->debug != MagickFalse)
00282     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00283       image->filename);
00284   if (image->properties != (void *) NULL)
00285     image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
00286       image->properties);
00287 }
00288 
00289 /*
00290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00291 %                                                                             %
00292 %                                                                             %
00293 %                                                                             %
00294 %  F o r m a t I m a g e P r o p e r t y                                      %
00295 %                                                                             %
00296 %                                                                             %
00297 %                                                                             %
00298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00299 %
00300 %  FormatImageProperty() permits formatted property/value pairs to be saved as
00301 %  an image proporty.
00302 %
00303 %  The format of the FormatImageProperty method is:
00304 %
00305 %      MagickBooleanType FormatImageProperty(Image *image,const char *property,
00306 %        const char *format,...)
00307 %
00308 %  A description of each parameter follows.
00309 %
00310 %   o  image:  The image.
00311 %
00312 %   o  property:  The attribute property.
00313 %
00314 %   o  format:  A string describing the format to use to write the remaining
00315 %      arguments.
00316 %
00317 */
00318 
00319 MagickExport MagickBooleanType FormatImagePropertyList(Image *image,
00320   const char *property,const char *format,va_list operands)
00321 {
00322   char
00323     value[MaxTextExtent];
00324 
00325   int
00326     n;
00327 
00328 #if defined(MAGICKCORE_HAVE_VSNPRINTF)
00329   n=vsnprintf(value,MaxTextExtent,format,operands);
00330 #else
00331   n=vsprintf(value,format,operands);
00332 #endif
00333   if (n < 0)
00334     value[MaxTextExtent-1]='\0';
00335   return(SetImageProperty(image,property,value));
00336 }
00337 
00338 MagickExport MagickBooleanType FormatImageProperty(Image *image,
00339   const char *property,const char *format,...)
00340 {
00341   MagickBooleanType
00342     status;
00343 
00344   va_list
00345     operands;
00346 
00347   va_start(operands,format);
00348   status=FormatImagePropertyList(image,property,format,operands);
00349   va_end(operands);
00350   return(status);
00351 }
00352 
00353 /*
00354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00355 %                                                                             %
00356 %                                                                             %
00357 %                                                                             %
00358 %   G e t I m a g e P r o p e r t y                                           %
00359 %                                                                             %
00360 %                                                                             %
00361 %                                                                             %
00362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00363 %
00364 %  GetImageProperty() gets a value associated with an image property.
00365 %
00366 %  The format of the GetImageProperty method is:
00367 %
00368 %      const char *GetImageProperty(const Image *image,const char *key)
00369 %
00370 %  A description of each parameter follows:
00371 %
00372 %    o image: the image.
00373 %
00374 %    o key: the key.
00375 %
00376 */
00377 
00378 static char
00379   *TracePSClippath(const unsigned char *,size_t,const unsigned long,
00380     const unsigned long),
00381   *TraceSVGClippath(const unsigned char *,size_t,const unsigned long,
00382     const unsigned long);
00383 
00384 static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
00385 {
00386   char
00387     *attribute,
00388     *message;
00389 
00390   const StringInfo
00391     *profile;
00392 
00393   long
00394     count,
00395     dataset,
00396     record;
00397 
00398   register long
00399     i;
00400 
00401   size_t
00402     length;
00403 
00404   profile=GetImageProfile(image,"iptc");
00405   if (profile == (StringInfo *) NULL)
00406     profile=GetImageProfile(image,"8bim");
00407   if (profile == (StringInfo *) NULL)
00408     return(MagickFalse);
00409   count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
00410   if (count != 2)
00411     return(MagickFalse);
00412   attribute=(char *) NULL;
00413   for (i=0; i < (long) GetStringInfoLength(profile); i+=(long) length)
00414   {
00415     length=1;
00416     if ((long) GetStringInfoDatum(profile)[i] != 0x1c)
00417       continue;
00418     length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
00419     length|=GetStringInfoDatum(profile)[i+4];
00420     if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
00421         ((long) GetStringInfoDatum(profile)[i+2] == record))
00422       {
00423         message=(char *) NULL;
00424         if (~length >= 1)
00425           message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
00426         if (message != (char *) NULL)
00427           {
00428             (void) CopyMagickString(message,(char *) GetStringInfoDatum(
00429               profile)+i+5,length+1);
00430             (void) ConcatenateString(&attribute,message);
00431             (void) ConcatenateString(&attribute,";");
00432             message=DestroyString(message);
00433           }
00434       }
00435     i+=5;
00436   }
00437   if ((attribute == (char *) NULL) || (*attribute == ';'))
00438     {
00439       if (attribute != (char *) NULL)
00440         attribute=DestroyString(attribute);
00441       return(MagickFalse);
00442     }
00443   attribute[strlen(attribute)-1]='\0';
00444   (void) SetImageProperty((Image *) image,key,(const char *) attribute);
00445   attribute=DestroyString(attribute);
00446   return(MagickTrue);
00447 }
00448 
00449 static inline long MagickMax(const long x,const long y)
00450 {
00451   if (x > y)
00452     return(x);
00453   return(y);
00454 }
00455 
00456 static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
00457 {
00458   int
00459     c;
00460 
00461   if (*length < 1)
00462     return(EOF);
00463   c=(int) (*(*p)++);
00464   (*length)--;
00465   return(c);
00466 }
00467 
00468 static inline unsigned long ReadPropertyMSBLong(const unsigned char **p,
00469   size_t *length)
00470 {
00471   int
00472     c;
00473 
00474   register long
00475     i;
00476 
00477   unsigned char
00478     buffer[4];
00479 
00480   unsigned long
00481     value;
00482 
00483   if (*length < 4)
00484     return(~0UL);
00485   for (i=0; i < 4; i++)
00486   {
00487     c=(int) (*(*p)++);
00488     (*length)--;
00489     buffer[i]=(unsigned char) c;
00490   }
00491   value=(unsigned long) (buffer[0] << 24);
00492   value|=buffer[1] << 16;
00493   value|=buffer[2] << 8;
00494   value|=buffer[3];
00495   return(value & 0xffffffff);
00496 }
00497 
00498 static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
00499   size_t *length)
00500 {
00501   int
00502     c;
00503 
00504   register long
00505     i;
00506 
00507   unsigned char
00508     buffer[2];
00509 
00510   unsigned short
00511     value;
00512 
00513   if (*length < 2)
00514     return((unsigned short) ~0U);
00515   for (i=0; i < 2; i++)
00516   {
00517     c=(int) (*(*p)++);
00518     (*length)--;
00519     buffer[i]=(unsigned char) c;
00520   }
00521   value=(unsigned short) (buffer[0] << 8);
00522   value|=buffer[1];
00523   return((unsigned short) (value & 0xffff));
00524 }
00525 
00526 static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
00527 {
00528   char
00529     *attribute,
00530     format[MaxTextExtent],
00531     name[MaxTextExtent],
00532     *resource;
00533 
00534   const StringInfo
00535     *profile;
00536 
00537   const unsigned char
00538     *info;
00539 
00540   long
00541     id,
00542     start,
00543     stop,
00544     sub_number;
00545 
00546   MagickBooleanType
00547     status;
00548 
00549   register long
00550     i;
00551 
00552   ssize_t
00553     count;
00554 
00555   size_t
00556     length;
00557 
00558   /*
00559     There's no newlines in path names, so it's safe as terminator.
00560   */
00561   profile=GetImageProfile(image,"8bim");
00562   if (profile == (StringInfo *) NULL)
00563     return(MagickFalse);
00564   count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
00565     format);
00566   if ((count != 2) && (count != 3) && (count != 4))
00567     return(MagickFalse);
00568   if (count < 4)
00569     (void) CopyMagickString(format,"SVG",MaxTextExtent);
00570   if (count < 3)
00571     *name='\0';
00572   sub_number=1;
00573   if (*name == '#')
00574     sub_number=atol(&name[1]);
00575   sub_number=MagickMax(sub_number,1L);
00576   resource=(char *) NULL;
00577   status=MagickFalse;
00578   length=GetStringInfoLength(profile);
00579   info=GetStringInfoDatum(profile);
00580   while ((length > 0) && (status == MagickFalse))
00581   {
00582     if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
00583       continue;
00584     if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
00585       continue;
00586     if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
00587       continue;
00588     if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
00589       continue;
00590     id=(long) ReadPropertyMSBShort(&info,&length);
00591     if (id < start)
00592       continue;
00593     if (id > stop)
00594       continue;
00595     if (resource != (char *) NULL)
00596       resource=DestroyString(resource);
00597     count=(ssize_t) ReadPropertyByte(&info,&length);
00598     if ((count != 0) && ((size_t) count <= length))
00599       {
00600         resource=(char *) NULL;
00601         if (~(1UL*count) >= MaxTextExtent)
00602           resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
00603             sizeof(*resource));
00604         if (resource != (char *) NULL)
00605           {
00606             for (i=0; i < (long) count; i++)
00607               resource[i]=(char) ReadPropertyByte(&info,&length);
00608             resource[count]='\0';
00609           }
00610       }
00611     if ((count & 0x01) == 0)
00612       (void) ReadPropertyByte(&info,&length);
00613     count=(ssize_t) ReadPropertyMSBLong(&info,&length);
00614     if ((*name != '\0') && (*name != '#'))
00615       if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
00616         {
00617           /*
00618             No name match, scroll forward and try next.
00619           */
00620           info+=count;
00621           length-=count;
00622           continue;
00623         }
00624     if ((*name == '#') && (sub_number != 1))
00625       {
00626         /*
00627           No numbered match, scroll forward and try next.
00628         */
00629         sub_number--;
00630         info+=count;
00631         length-=count;
00632         continue;
00633       }
00634     /*
00635       We have the resource of interest.
00636     */
00637     attribute=(char *) NULL;
00638     if (~(1UL*count) >= MaxTextExtent)
00639       attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
00640         sizeof(*attribute));
00641     if (attribute != (char *) NULL)
00642       {
00643         (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
00644         attribute[count]='\0';
00645         info+=count;
00646         length-=count;
00647         if ((id <= 1999) || (id >= 2999))
00648           (void) SetImageProperty((Image *) image,key,(const char *)
00649             attribute);
00650         else
00651           {
00652             char
00653               *path;
00654 
00655             if (LocaleCompare(format,"svg") == 0)
00656               path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
00657                 image->columns,image->rows);
00658             else
00659               path=TracePSClippath((unsigned char *) attribute,(size_t) count,
00660                 image->columns,image->rows);
00661             (void) SetImageProperty((Image *) image,key,(const char *) path);
00662             path=DestroyString(path);
00663           }
00664         attribute=DestroyString(attribute);
00665         status=MagickTrue;
00666       }
00667   }
00668   if (resource != (char *) NULL)
00669     resource=DestroyString(resource);
00670   return(status);
00671 }
00672 
00673 static inline unsigned short ReadPropertyShort(const EndianType endian,
00674   const unsigned char *buffer)
00675 {
00676   unsigned short
00677     value;
00678 
00679   if (endian == MSBEndian)
00680     {
00681       value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
00682         ((unsigned char *) buffer)[1]);
00683       return((unsigned short) (value & 0xffff));
00684     }
00685   value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
00686   return((unsigned short) (value & 0xffff));
00687 }
00688 
00689 static inline unsigned long ReadPropertyLong(const EndianType endian,
00690   const unsigned char *buffer)
00691 {
00692   unsigned long
00693     value;
00694 
00695   if (endian == MSBEndian)
00696     {
00697       value=(unsigned long) ((buffer[0] << 24) | (buffer[1] << 16) |
00698         (buffer[2] << 8) | buffer[3]);
00699       return((unsigned long) (value & 0xffffffff));
00700     }
00701   value=(unsigned long) ((buffer[3] << 24) | (buffer[2] << 16) |
00702     (buffer[1] << 8 ) | (buffer[0]));
00703   return((unsigned long) (value & 0xffffffff));
00704 }
00705 
00706 static MagickBooleanType GetEXIFProperty(const Image *image,
00707   const char *property)
00708 {
00709 #define MaxDirectoryStack  16
00710 #define EXIF_DELIMITER  "\n"
00711 #define EXIF_NUM_FORMATS  12
00712 #define EXIF_FMT_BYTE  1
00713 #define EXIF_FMT_STRING  2
00714 #define EXIF_FMT_USHORT  3
00715 #define EXIF_FMT_ULONG  4
00716 #define EXIF_FMT_URATIONAL  5
00717 #define EXIF_FMT_SBYTE  6
00718 #define EXIF_FMT_UNDEFINED  7
00719 #define EXIF_FMT_SSHORT  8
00720 #define EXIF_FMT_SLONG  9
00721 #define EXIF_FMT_SRATIONAL  10
00722 #define EXIF_FMT_SINGLE  11
00723 #define EXIF_FMT_DOUBLE  12
00724 #define TAG_EXIF_OFFSET  0x8769
00725 #define TAG_GPS_OFFSET  0x8825
00726 #define TAG_INTEROP_OFFSET  0xa005
00727 
00728 #define EXIFMultipleValues(size, format, arg) \
00729 { \
00730     long component; \
00731     size_t used_space; \
00732     unsigned char *p1; \
00733     used_space=0; \
00734     p1=p; \
00735     for (component = 0; component < components; component++) \
00736     { \
00737        used_space+=FormatMagickString(buffer+used_space, \
00738           MaxTextExtent-used_space,format", ",arg); \
00739        if (used_space >= MaxTextExtent - 1) \
00740           used_space=MaxTextExtent-1; \
00741        p1+=size; \
00742     } \
00743     buffer[used_space-2]='\0'; \
00744     value=AcquireString(buffer); \
00745 }
00746 
00747 #define EXIFMultipleFractions(size, format, arg1, arg2) \
00748 { \
00749     long component; \
00750     size_t used_space; \
00751     unsigned char *p1; \
00752     used_space=0; \
00753     p1=p; \
00754     for (component = 0; component < components; component++) \
00755     { \
00756        used_space+=FormatMagickString(buffer+used_space, \
00757           MaxTextExtent-used_space,format", ",arg1, arg2); \
00758        if (used_space >= MaxTextExtent - 1) \
00759           used_space=MaxTextExtent-1; \
00760        p1+=size; \
00761     } \
00762     buffer[used_space-2]='\0'; \
00763     value=AcquireString(buffer); \
00764 }
00765 
00766   typedef struct _DirectoryInfo
00767   {
00768     const unsigned char
00769       *directory;
00770 
00771     unsigned long
00772       entry,
00773       offset;
00774   } DirectoryInfo;
00775 
00776   typedef struct _TagInfo
00777   {
00778     unsigned long
00779       tag;
00780 
00781     const char
00782       *description;
00783   } TagInfo;
00784 
00785   static TagInfo
00786     EXIFTag[] =
00787     {
00788       {  0x001, "exif:InteroperabilityIndex" },
00789       {  0x002, "exif:InteroperabilityVersion" },
00790       {  0x100, "exif:ImageWidth" },
00791       {  0x101, "exif:ImageLength" },
00792       {  0x102, "exif:BitsPerSample" },
00793       {  0x103, "exif:Compression" },
00794       {  0x106, "exif:PhotometricInterpretation" },
00795       {  0x10a, "exif:FillOrder" },
00796       {  0x10d, "exif:DocumentName" },
00797       {  0x10e, "exif:ImageDescription" },
00798       {  0x10f, "exif:Make" },
00799       {  0x110, "exif:Model" },
00800       {  0x111, "exif:StripOffsets" },
00801       {  0x112, "exif:Orientation" },
00802       {  0x115, "exif:SamplesPerPixel" },
00803       {  0x116, "exif:RowsPerStrip" },
00804       {  0x117, "exif:StripByteCounts" },
00805       {  0x11a, "exif:XResolution" },
00806       {  0x11b, "exif:YResolution" },
00807       {  0x11c, "exif:PlanarConfiguration" },
00808       {  0x11d, "exif:PageName" },
00809       {  0x11e, "exif:XPosition" },
00810       {  0x11f, "exif:YPosition" },
00811       {  0x118, "exif:MinSampleValue" },
00812       {  0x119, "exif:MaxSampleValue" },
00813       {  0x120, "exif:FreeOffsets" },
00814       {  0x121, "exif:FreeByteCounts" },
00815       {  0x122, "exif:GrayResponseUnit" },
00816       {  0x123, "exif:GrayResponseCurve" },
00817       {  0x124, "exif:T4Options" },
00818       {  0x125, "exif:T6Options" },
00819       {  0x128, "exif:ResolutionUnit" },
00820       {  0x12d, "exif:TransferFunction" },
00821       {  0x131, "exif:Software" },
00822       {  0x132, "exif:DateTime" },
00823       {  0x13b, "exif:Artist" },
00824       {  0x13e, "exif:WhitePoint" },
00825       {  0x13f, "exif:PrimaryChromaticities" },
00826       {  0x140, "exif:ColorMap" },
00827       {  0x141, "exif:HalfToneHints" },
00828       {  0x142, "exif:TileWidth" },
00829       {  0x143, "exif:TileLength" },
00830       {  0x144, "exif:TileOffsets" },
00831       {  0x145, "exif:TileByteCounts" },
00832       {  0x14a, "exif:SubIFD" },
00833       {  0x14c, "exif:InkSet" },
00834       {  0x14d, "exif:InkNames" },
00835       {  0x14e, "exif:NumberOfInks" },
00836       {  0x150, "exif:DotRange" },
00837       {  0x151, "exif:TargetPrinter" },
00838       {  0x152, "exif:ExtraSample" },
00839       {  0x153, "exif:SampleFormat" },
00840       {  0x154, "exif:SMinSampleValue" },
00841       {  0x155, "exif:SMaxSampleValue" },
00842       {  0x156, "exif:TransferRange" },
00843       {  0x157, "exif:ClipPath" },
00844       {  0x158, "exif:XClipPathUnits" },
00845       {  0x159, "exif:YClipPathUnits" },
00846       {  0x15a, "exif:Indexed" },
00847       {  0x15b, "exif:JPEGTables" },
00848       {  0x15f, "exif:OPIProxy" },
00849       {  0x200, "exif:JPEGProc" },
00850       {  0x201, "exif:JPEGInterchangeFormat" },
00851       {  0x202, "exif:JPEGInterchangeFormatLength" },
00852       {  0x203, "exif:JPEGRestartInterval" },
00853       {  0x205, "exif:JPEGLosslessPredictors" },
00854       {  0x206, "exif:JPEGPointTransforms" },
00855       {  0x207, "exif:JPEGQTables" },
00856       {  0x208, "exif:JPEGDCTables" },
00857       {  0x209, "exif:JPEGACTables" },
00858       {  0x211, "exif:YCbCrCoefficients" },
00859       {  0x212, "exif:YCbCrSubSampling" },
00860       {  0x213, "exif:YCbCrPositioning" },
00861       {  0x214, "exif:ReferenceBlackWhite" },
00862       {  0x2bc, "exif:ExtensibleMetadataPlatform" },
00863       {  0x301, "exif:Gamma" },
00864       {  0x302, "exif:ICCProfileDescriptor" },
00865       {  0x303, "exif:SRGBRenderingIntent" },
00866       {  0x320, "exif:ImageTitle" },
00867       {  0x5001, "exif:ResolutionXUnit" },
00868       {  0x5002, "exif:ResolutionYUnit" },
00869       {  0x5003, "exif:ResolutionXLengthUnit" },
00870       {  0x5004, "exif:ResolutionYLengthUnit" },
00871       {  0x5005, "exif:PrintFlags" },
00872       {  0x5006, "exif:PrintFlagsVersion" },
00873       {  0x5007, "exif:PrintFlagsCrop" },
00874       {  0x5008, "exif:PrintFlagsBleedWidth" },
00875       {  0x5009, "exif:PrintFlagsBleedWidthScale" },
00876       {  0x500A, "exif:HalftoneLPI" },
00877       {  0x500B, "exif:HalftoneLPIUnit" },
00878       {  0x500C, "exif:HalftoneDegree" },
00879       {  0x500D, "exif:HalftoneShape" },
00880       {  0x500E, "exif:HalftoneMisc" },
00881       {  0x500F, "exif:HalftoneScreen" },
00882       {  0x5010, "exif:JPEGQuality" },
00883       {  0x5011, "exif:GridSize" },
00884       {  0x5012, "exif:ThumbnailFormat" },
00885       {  0x5013, "exif:ThumbnailWidth" },
00886       {  0x5014, "exif:ThumbnailHeight" },
00887       {  0x5015, "exif:ThumbnailColorDepth" },
00888       {  0x5016, "exif:ThumbnailPlanes" },
00889       {  0x5017, "exif:ThumbnailRawBytes" },
00890       {  0x5018, "exif:ThumbnailSize" },
00891       {  0x5019, "exif:ThumbnailCompressedSize" },
00892       {  0x501a, "exif:ColorTransferFunction" },
00893       {  0x501b, "exif:ThumbnailData" },
00894       {  0x5020, "exif:ThumbnailImageWidth" },
00895       {  0x5021, "exif:ThumbnailImageHeight" },
00896       {  0x5022, "exif:ThumbnailBitsPerSample" },
00897       {  0x5023, "exif:ThumbnailCompression" },
00898       {  0x5024, "exif:ThumbnailPhotometricInterp" },
00899       {  0x5025, "exif:ThumbnailImageDescription" },
00900       {  0x5026, "exif:ThumbnailEquipMake" },
00901       {  0x5027, "exif:ThumbnailEquipModel" },
00902       {  0x5028, "exif:ThumbnailStripOffsets" },
00903       {  0x5029, "exif:ThumbnailOrientation" },
00904       {  0x502a, "exif:ThumbnailSamplesPerPixel" },
00905       {  0x502b, "exif:ThumbnailRowsPerStrip" },
00906       {  0x502c, "exif:ThumbnailStripBytesCount" },
00907       {  0x502d, "exif:ThumbnailResolutionX" },
00908       {  0x502e, "exif:ThumbnailResolutionY" },
00909       {  0x502f, "exif:ThumbnailPlanarConfig" },
00910       {  0x5030, "exif:ThumbnailResolutionUnit" },
00911       {  0x5031, "exif:ThumbnailTransferFunction" },
00912       {  0x5032, "exif:ThumbnailSoftwareUsed" },
00913       {  0x5033, "exif:ThumbnailDateTime" },
00914       {  0x5034, "exif:ThumbnailArtist" },
00915       {  0x5035, "exif:ThumbnailWhitePoint" },
00916       {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
00917       {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
00918       {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
00919       {  0x5039, "exif:ThumbnailYCbCrPositioning" },
00920       {  0x503A, "exif:ThumbnailRefBlackWhite" },
00921       {  0x503B, "exif:ThumbnailCopyRight" },
00922       {  0x5090, "exif:LuminanceTable" },
00923       {  0x5091, "exif:ChrominanceTable" },
00924       {  0x5100, "exif:FrameDelay" },
00925       {  0x5101, "exif:LoopCount" },
00926       {  0x5110, "exif:PixelUnit" },
00927       {  0x5111, "exif:PixelPerUnitX" },
00928       {  0x5112, "exif:PixelPerUnitY" },
00929       {  0x5113, "exif:PaletteHistogram" },
00930       {  0x1000, "exif:RelatedImageFileFormat" },
00931       {  0x1001, "exif:RelatedImageLength" },
00932       {  0x1002, "exif:RelatedImageWidth" },
00933       {  0x800d, "exif:ImageID" },
00934       {  0x80e3, "exif:Matteing" },
00935       {  0x80e4, "exif:DataType" },
00936       {  0x80e5, "exif:ImageDepth" },
00937       {  0x80e6, "exif:TileDepth" },
00938       {  0x828d, "exif:CFARepeatPatternDim" },
00939       {  0x828e, "exif:CFAPattern2" },
00940       {  0x828f, "exif:BatteryLevel" },
00941       {  0x8298, "exif:Copyright" },
00942       {  0x829a, "exif:ExposureTime" },
00943       {  0x829d, "exif:FNumber" },
00944       {  0x83bb, "exif:IPTC/NAA" },
00945       {  0x84e3, "exif:IT8RasterPadding" },
00946       {  0x84e5, "exif:IT8ColorTable" },
00947       {  0x8649, "exif:ImageResourceInformation" },
00948       {  0x8769, "exif:ExifOffset" },
00949       {  0x8773, "exif:InterColorProfile" },
00950       {  0x8822, "exif:ExposureProgram" },
00951       {  0x8824, "exif:SpectralSensitivity" },
00952       {  0x8825, "exif:GPSInfo" },
00953       {  0x8827, "exif:ISOSpeedRatings" },
00954       {  0x8828, "exif:OECF" },
00955       {  0x8829, "exif:Interlace" },
00956       {  0x882a, "exif:TimeZoneOffset" },
00957       {  0x882b, "exif:SelfTimerMode" },
00958       {  0x9000, "exif:ExifVersion" },
00959       {  0x9003, "exif:DateTimeOriginal" },
00960       {  0x9004, "exif:DateTimeDigitized" },
00961       {  0x9101, "exif:ComponentsConfiguration" },
00962       {  0x9102, "exif:CompressedBitsPerPixel" },
00963       {  0x9201, "exif:ShutterSpeedValue" },
00964       {  0x9202, "exif:ApertureValue" },
00965       {  0x9203, "exif:BrightnessValue" },
00966       {  0x9204, "exif:ExposureBiasValue" },
00967       {  0x9205, "exif:MaxApertureValue" },
00968       {  0x9206, "exif:SubjectDistance" },
00969       {  0x9207, "exif:MeteringMode" },
00970       {  0x9208, "exif:LightSource" },
00971       {  0x9209, "exif:Flash" },
00972       {  0x920a, "exif:FocalLength" },
00973       {  0x920b, "exif:FlashEnergy" },
00974       {  0x920c, "exif:SpatialFrequencyResponse" },
00975       {  0x920d, "exif:Noise" },
00976       {  0x9211, "exif:ImageNumber" },
00977       {  0x9212, "exif:SecurityClassification" },
00978       {  0x9213, "exif:ImageHistory" },
00979       {  0x9214, "exif:SubjectArea" },
00980       {  0x9215, "exif:ExposureIndex" },
00981       {  0x9216, "exif:TIFF-EPStandardID" },
00982       {  0x927c, "exif:MakerNote" },
00983       {  0x9C9b, "exif:WinXP-Title" },
00984       {  0x9C9c, "exif:WinXP-Comments" },
00985       {  0x9C9d, "exif:WinXP-Author" },
00986       {  0x9C9e, "exif:WinXP-Keywords" },
00987       {  0x9C9f, "exif:WinXP-Subject" },
00988       {  0x9286, "exif:UserComment" },
00989       {  0x9290, "exif:SubSecTime" },
00990       {  0x9291, "exif:SubSecTimeOriginal" },
00991       {  0x9292, "exif:SubSecTimeDigitized" },
00992       {  0xa000, "exif:FlashPixVersion" },
00993       {  0xa001, "exif:ColorSpace" },
00994       {  0xa002, "exif:ExifImageWidth" },
00995       {  0xa003, "exif:ExifImageLength" },
00996       {  0xa004, "exif:RelatedSoundFile" },
00997       {  0xa005, "exif:InteroperabilityOffset" },
00998       {  0xa20b, "exif:FlashEnergy" },
00999       {  0xa20c, "exif:SpatialFrequencyResponse" },
01000       {  0xa20d, "exif:Noise" },
01001       {  0xa20e, "exif:FocalPlaneXResolution" },
01002       {  0xa20f, "exif:FocalPlaneYResolution" },
01003       {  0xa210, "exif:FocalPlaneResolutionUnit" },
01004       {  0xa214, "exif:SubjectLocation" },
01005       {  0xa215, "exif:ExposureIndex" },
01006       {  0xa216, "exif:TIFF/EPStandardID" },
01007       {  0xa217, "exif:SensingMethod" },
01008       {  0xa300, "exif:FileSource" },
01009       {  0xa301, "exif:SceneType" },
01010       {  0xa302, "exif:CFAPattern" },
01011       {  0xa401, "exif:CustomRendered" },
01012       {  0xa402, "exif:ExposureMode" },
01013       {  0xa403, "exif:WhiteBalance" },
01014       {  0xa404, "exif:DigitalZoomRatio" },
01015       {  0xa405, "exif:FocalLengthIn35mmFilm" },
01016       {  0xa406, "exif:SceneCaptureType" },
01017       {  0xa407, "exif:GainControl" },
01018       {  0xa408, "exif:Contrast" },
01019       {  0xa409, "exif:Saturation" },
01020       {  0xa40a, "exif:Sharpness" },
01021       {  0xa40b, "exif:DeviceSettingDescription" },
01022       {  0xa40c, "exif:SubjectDistanceRange" },
01023       {  0xa420, "exif:ImageUniqueID" },
01024       {  0xc4a5, "exif:PrintImageMatching" },
01025       { 0x10000, "exif:GPSVersionID" },
01026       { 0x10001, "exif:GPSLatitudeRef" },
01027       { 0x10002, "exif:GPSLatitude" },
01028       { 0x10003, "exif:GPSLongitudeRef" },
01029       { 0x10004, "exif:GPSLongitude" },
01030       { 0x10005, "exif:GPSAltitudeRef" },
01031       { 0x10006, "exif:GPSAltitude" },
01032       { 0x10007, "exif:GPSTimeStamp" },
01033       { 0x10008, "exif:GPSSatellites" },
01034       { 0x10009, "exif:GPSStatus" },
01035       { 0x1000a, "exif:GPSMeasureMode" },
01036       { 0x1000b, "exif:GPSDop" },
01037       { 0x1000c, "exif:GPSSpeedRef" },
01038       { 0x1000d, "exif:GPSSpeed" },
01039       { 0x1000e, "exif:GPSTrackRef" },
01040       { 0x1000f, "exif:GPSTrack" },
01041       { 0x10010, "exif:GPSImgDirectionRef" },
01042       { 0x10011, "exif:GPSImgDirection" },
01043       { 0x10012, "exif:GPSMapDatum" },
01044       { 0x10013, "exif:GPSDestLatitudeRef" },
01045       { 0x10014, "exif:GPSDestLatitude" },
01046       { 0x10015, "exif:GPSDestLongitudeRef" },
01047       { 0x10016, "exif:GPSDestLongitude" },
01048       { 0x10017, "exif:GPSDestBearingRef" },
01049       { 0x10018, "exif:GPSDestBearing" },
01050       { 0x10019, "exif:GPSDestDistanceRef" },
01051       { 0x1001a, "exif:GPSDestDistance" },
01052       { 0x1001b, "exif:GPSProcessingMethod" },
01053       { 0x1001c, "exif:GPSAreaInformation" },
01054       { 0x1001d, "exif:GPSDateStamp" },
01055       { 0x1001e, "exif:GPSDifferential" },
01056       {  0x0000, NULL}
01057     };
01058 
01059   const StringInfo
01060     *profile;
01061 
01062   const unsigned char
01063     *directory,
01064     *exif;
01065 
01066   DirectoryInfo
01067     directory_stack[MaxDirectoryStack];
01068 
01069   EndianType
01070     endian;
01071 
01072   long
01073     all,
01074     id,
01075     level,
01076     tag_value;
01077 
01078   register long
01079     i;
01080 
01081   size_t
01082     length;
01083 
01084   ssize_t
01085     offset;
01086 
01087   static int
01088     tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
01089 
01090   unsigned long
01091     entry,
01092     number_entries,
01093     tag_offset,
01094     tag;
01095 
01096   /*
01097     If EXIF data exists, then try to parse the request for a tag.
01098   */
01099   profile=GetImageProfile(image,"exif");
01100   if (profile == (StringInfo *) NULL)
01101     return(MagickFalse);
01102   if ((property == (const char *) NULL) || (*property == '\0'))
01103     return(MagickFalse);
01104   while (isspace((int) ((unsigned char) *property)) != 0)
01105     property++;
01106   all=0;
01107   tag=(~0UL);
01108   switch (*(property+5))
01109   {
01110     case '*':
01111     {
01112       /*
01113         Caller has asked for all the tags in the EXIF data.
01114       */
01115       tag=0;
01116       all=1; /* return the data in description=value format */
01117       break;
01118     }
01119     case '!':
01120     {
01121       tag=0;
01122       all=2; /* return the data in tagid=value format */
01123       break;
01124     }
01125     case '#':
01126     case '@':
01127     {
01128       int
01129         c;
01130 
01131       size_t
01132         n;
01133 
01134       /*
01135         Check for a hex based tag specification first.
01136       */
01137       tag=(*(property+5) == '@') ? 1 : 0;
01138       property+=6;
01139       n=strlen(property);
01140       if (n != 4)
01141         return(MagickFalse);
01142       /*
01143         Parse tag specification as a hex number.
01144       */
01145       n/=4;
01146       do
01147       {
01148         for (i=(long) n-1L; i >= 0; i--)
01149         {
01150           c=(*property++);
01151           tag<<=4;
01152           if ((c >= '0') && (c <= '9'))
01153             tag|=(c-'0');
01154           else
01155             if ((c >= 'A') && (c <= 'F'))
01156               tag|=(c-('A'-10));
01157             else
01158               if ((c >= 'a') && (c <= 'f'))
01159                 tag|=(c-('a'-10));
01160               else
01161                 return(MagickFalse);
01162         }
01163       } while (*property != '\0');
01164       break;
01165     }
01166     default:
01167     {
01168       /*
01169         Try to match the text with a tag name instead.
01170       */
01171       for (i=0; ; i++)
01172       {
01173         <