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-2009 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/attribute.h"
00045 #include "magick/cache.h"
00046 #include "magick/color.h"
00047 #include "magick/compare.h"
00048 #include "magick/constitute.h"
00049 #include "magick/draw.h"
00050 #include "magick/effect.h"
00051 #include "magick/exception.h"
00052 #include "magick/exception-private.h"
00053 #include "magick/fx.h"
00054 #include "magick/fx-private.h"
00055 #include "magick/gem.h"
00056 #include "magick/geometry.h"
00057 #include "magick/histogram.h"
00058 #include "magick/image.h"
00059 #include "magick/image.h"
00060 #include "magick/layer.h"
00061 #include "magick/list.h"
00062 #include "magick/memory_.h"
00063 #include "magick/monitor.h"
00064 #include "magick/montage.h"
00065 #include "magick/option.h"
00066 #include "magick/profile.h"
00067 #include "magick/property.h"
00068 #include "magick/quantum.h"
00069 #include "magick/resource_.h"
00070 #include "magick/splay-tree.h"
00071 #include "magick/signature-private.h"
00072 #include "magick/statistic.h"
00073 #include "magick/string_.h"
00074 #include "magick/token.h"
00075 #include "magick/utility.h"
00076 #include "magick/xml-tree.h"
00077 
00078 /*
00079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00080 %                                                                             %
00081 %                                                                             %
00082 %                                                                             %
00083 %   C l o n e I m a g e P r o p e r t i e s                                   %
00084 %                                                                             %
00085 %                                                                             %
00086 %                                                                             %
00087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00088 %
00089 %  CloneImageProperties() clones one or more image properties.
00090 %
00091 %  The format of the CloneImageProperties method is:
00092 %
00093 %      MagickBooleanType CloneImageProperties(Image *image,
00094 %        const Image *clone_image)
00095 %
00096 %  A description of each parameter follows:
00097 %
00098 %    o image: the image.
00099 %
00100 %    o clone_image: the clone image.
00101 %
00102 */
00103 MagickExport MagickBooleanType CloneImageProperties(Image *image,
00104   const Image *clone_image)
00105 {
00106   assert(image != (Image *) NULL);
00107   assert(image->signature == MagickSignature);
00108   if (image->debug != MagickFalse)
00109     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00110   assert(clone_image != (const Image *) NULL);
00111   assert(clone_image->signature == MagickSignature);
00112   if (clone_image->debug != MagickFalse)
00113     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00114       clone_image->filename);
00115   (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
00116   (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
00117     MaxTextExtent);
00118   image->compression=clone_image->compression;
00119   image->quality=clone_image->quality;
00120   image->depth=clone_image->depth;
00121   image->background_color=clone_image->background_color;
00122   image->border_color=clone_image->border_color;
00123   image->matte_color=clone_image->matte_color;
00124   image->transparent_color=clone_image->transparent_color;
00125   image->gamma=clone_image->gamma;
00126   image->chromaticity=clone_image->chromaticity;
00127   image->rendering_intent=clone_image->rendering_intent;
00128   image->black_point_compensation=clone_image->black_point_compensation;
00129   image->units=clone_image->units;
00130   image->montage=(char *) NULL;
00131   image->directory=(char *) NULL;
00132   (void) CloneString(&image->geometry,clone_image->geometry);
00133   image->offset=clone_image->offset;
00134   image->x_resolution=clone_image->x_resolution;
00135   image->y_resolution=clone_image->y_resolution;
00136   image->page=clone_image->page;
00137   image->tile_offset=clone_image->tile_offset;
00138   image->extract_info=clone_image->extract_info;
00139   image->bias=clone_image->bias;
00140   image->filter=clone_image->filter;
00141   image->blur=clone_image->blur;
00142   image->fuzz=clone_image->fuzz;
00143   image->interlace=clone_image->interlace;
00144   image->interpolate=clone_image->interpolate;
00145   image->endian=clone_image->endian;
00146   image->gravity=clone_image->gravity;
00147   image->compose=clone_image->compose;
00148   image->scene=clone_image->scene;
00149   image->orientation=clone_image->orientation;
00150   image->dispose=clone_image->dispose;
00151   image->delay=clone_image->delay;
00152   image->ticks_per_second=clone_image->ticks_per_second;
00153   image->iterations=clone_image->iterations;
00154   image->total_colors=clone_image->total_colors;
00155   image->taint=clone_image->taint;
00156   image->progress_monitor=clone_image->progress_monitor;
00157   image->client_data=clone_image->client_data;
00158   image->start_loop=clone_image->start_loop;
00159   image->error=clone_image->error;
00160   image->signature=clone_image->signature;
00161   if (clone_image->properties != (void *) NULL)
00162     {
00163       if (image->properties != (void *) NULL)
00164         DestroyImageProperties(image);
00165       image->properties=CloneSplayTree((SplayTreeInfo *)
00166         clone_image->properties,(void *(*)(void *)) ConstantString,
00167         (void *(*)(void *)) ConstantString);
00168     }
00169   return(MagickTrue);
00170 }
00171 
00172 /*
00173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00174 %                                                                             %
00175 %                                                                             %
00176 %                                                                             %
00177 %   D e f i n e I m a g e P r o p e r t y                                     %
00178 %                                                                             %
00179 %                                                                             %
00180 %                                                                             %
00181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00182 %
00183 %  DefineImageProperty() associates a key/value pair with an image property.
00184 %
00185 %  The format of the DefineImageProperty method is:
00186 %
00187 %      MagickBooleanType DefineImageProperty(Image *image,
00188 %        const char *property)
00189 %
00190 %  A description of each parameter follows:
00191 %
00192 %    o image: the image.
00193 %
00194 %    o property: the image property.
00195 %
00196 */
00197 MagickExport MagickBooleanType DefineImageProperty(Image *image,
00198   const char *property)
00199 {
00200   char
00201     key[MaxTextExtent],
00202     value[MaxTextExtent];
00203 
00204   register char
00205     *p;
00206 
00207   assert(image != (Image *) NULL);
00208   assert(property != (const char *) NULL);
00209   (void) CopyMagickString(key,property,MaxTextExtent-1);
00210   for (p=key; *p != '\0'; p++)
00211     if (*p == '=')
00212       break;
00213   *value='\0';
00214   if (*p == '=')
00215     (void) CopyMagickString(value,p+1,MaxTextExtent);
00216   *p='\0';
00217   return(SetImageProperty(image,key,value));
00218 }
00219 
00220 /*
00221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00222 %                                                                             %
00223 %                                                                             %
00224 %                                                                             %
00225 %   D e l e t e I m a g e P r o p e r t y                                     %
00226 %                                                                             %
00227 %                                                                             %
00228 %                                                                             %
00229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00230 %
00231 %  DeleteImageProperty() deletes an image property.
00232 %
00233 %  The format of the DeleteImageProperty method is:
00234 %
00235 %      MagickBooleanType DeleteImageProperty(Image *image,const char *property)
00236 %
00237 %  A description of each parameter follows:
00238 %
00239 %    o image: the image.
00240 %
00241 %    o property: the image property.
00242 %
00243 */
00244 MagickExport MagickBooleanType DeleteImageProperty(Image *image,
00245   const char *property)
00246 {
00247   assert(image != (Image *) NULL);
00248   assert(image->signature == MagickSignature);
00249   if (image->debug != MagickFalse)
00250     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00251       image->filename);
00252   if (image->properties == (void *) NULL)
00253     return(MagickFalse);
00254   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
00255 }
00256 
00257 /*
00258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00259 %                                                                             %
00260 %                                                                             %
00261 %                                                                             %
00262 %   D e s t r o y I m a g e P r o p e r t i e s                               %
00263 %                                                                             %
00264 %                                                                             %
00265 %                                                                             %
00266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00267 %
00268 %  DestroyImageProperties() releases memory associated with image property
00269 %  values.
00270 %
00271 %  The format of the DestroyDefines method is:
00272 %
00273 %      void DestroyImageProperties(Image *image)
00274 %
00275 %  A description of each parameter follows:
00276 %
00277 %    o image: the image.
00278 %
00279 */
00280 MagickExport void DestroyImageProperties(Image *image)
00281 {
00282   assert(image != (Image *) NULL);
00283   assert(image->signature == MagickSignature);
00284   if (image->debug != MagickFalse)
00285     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00286       image->filename);
00287   if (image->properties != (void *) NULL)
00288     image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
00289       image->properties);
00290 }
00291 
00292 /*
00293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00294 %                                                                             %
00295 %                                                                             %
00296 %                                                                             %
00297 %  F o r m a t I m a g e P r o p e r t y                                      %
00298 %                                                                             %
00299 %                                                                             %
00300 %                                                                             %
00301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00302 %
00303 %  FormatImageProperty() permits formatted property/value pairs to be saved as
00304 %  an image proporty.
00305 %
00306 %  The format of the FormatImageProperty method is:
00307 %
00308 %      MagickBooleanType FormatImageProperty(Image *image,const char *property,
00309 %        const char *format,...)
00310 %
00311 %  A description of each parameter follows.
00312 %
00313 %   o  image:  The image.
00314 %
00315 %   o  property:  The attribute property.
00316 %
00317 %   o  format:  A string describing the format to use to write the remaining
00318 %      arguments.
00319 %
00320 */
00321 
00322 MagickExport MagickBooleanType FormatImagePropertyList(Image *image,
00323   const char *property,const char *format,va_list operands)
00324 {
00325   char
00326     value[MaxTextExtent];
00327 
00328   int
00329     n;
00330 
00331 #if defined(MAGICKCORE_HAVE_VSNPRINTF)
00332   n=vsnprintf(value,MaxTextExtent,format,operands);
00333 #else
00334   n=vsprintf(value,format,operands);
00335 #endif
00336   if (n < 0)
00337     value[MaxTextExtent-1]='\0';
00338   return(SetImageProperty(image,property,value));
00339 }
00340 
00341 MagickExport MagickBooleanType FormatImageProperty(Image *image,
00342   const char *property,const char *format,...)
00343 {
00344   MagickBooleanType
00345     status;
00346 
00347   va_list
00348     operands;
00349 
00350   va_start(operands,format);
00351   status=FormatImagePropertyList(image,property,format,operands);
00352   va_end(operands);
00353   return(status);
00354 }
00355 
00356 /*
00357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00358 %                                                                             %
00359 %                                                                             %
00360 %                                                                             %
00361 %   G e t I m a g e P r o p e r t y                                           %
00362 %                                                                             %
00363 %                                                                             %
00364 %                                                                             %
00365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00366 %
00367 %  GetImageProperty() gets a value associated with an image property.
00368 %
00369 %  The format of the GetImageProperty method is:
00370 %
00371 %      const char *GetImageProperty(const Image *image,const char *key)
00372 %
00373 %  A description of each parameter follows:
00374 %
00375 %    o image: the image.
00376 %
00377 %    o key: the key.
00378 %
00379 */
00380 
00381 static char
00382   *TracePSClippath(const unsigned char *,size_t,const unsigned long,
00383     const unsigned long),
00384   *TraceSVGClippath(const unsigned char *,size_t,const unsigned long,
00385     const unsigned long);
00386 
00387 static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
00388 {
00389   char
00390     *attribute,
00391     *message;
00392 
00393   const StringInfo
00394     *profile;
00395 
00396   long
00397     count,
00398     dataset,
00399     record;
00400 
00401   register long
00402     i;
00403 
00404   size_t
00405     length;
00406 
00407   profile=GetImageProfile(image,"iptc");
00408   if (profile == (StringInfo *) NULL)
00409     profile=GetImageProfile(image,"8bim");
00410   if (profile == (StringInfo *) NULL)
00411     return(MagickFalse);
00412   count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
00413   if (count != 2)
00414     return(MagickFalse);
00415   attribute=(char *) NULL;
00416   for (i=0; i < (long) GetStringInfoLength(profile); i+=(long) length)
00417   {
00418     length=1;
00419     if ((long) GetStringInfoDatum(profile)[i] != 0x1c)
00420       continue;
00421     length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
00422     length|=GetStringInfoDatum(profile)[i+4];
00423     if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
00424         ((long) GetStringInfoDatum(profile)[i+2] == record))
00425       {
00426         message=(char *) NULL;
00427         if (~length >= 1)
00428           message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
00429         if (message != (char *) NULL)
00430           {
00431             (void) CopyMagickString(message,(char *) GetStringInfoDatum(
00432               profile)+i+5,length+1);
00433             (void) ConcatenateString(&attribute,message);
00434             (void) ConcatenateString(&attribute,";");
00435             message=DestroyString(message);
00436           }
00437       }
00438     i+=5;
00439   }
00440   if ((attribute == (char *) NULL) || (*attribute == ';'))
00441     {
00442       if (attribute != (char *) NULL)
00443         attribute=DestroyString(attribute);
00444       return(MagickFalse);
00445     }
00446   attribute[strlen(attribute)-1]='\0';
00447   (void) SetImageProperty((Image *) image,key,(const char *) attribute);
00448   attribute=DestroyString(attribute);
00449   return(MagickTrue);
00450 }
00451 
00452 static inline long MagickMax(const long x,const long y)
00453 {
00454   if (x > y)
00455     return(x);
00456   return(y);
00457 }
00458 
00459 static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
00460 {
00461   int
00462     c;
00463 
00464   if (*length < 1)
00465     return(EOF);
00466   c=(int) (*(*p)++);
00467   (*length)--;
00468   return(c);
00469 }
00470 
00471 static inline unsigned long ReadPropertyMSBLong(const unsigned char **p,
00472   size_t *length)
00473 {
00474   int
00475     c;
00476 
00477   register long
00478     i;
00479 
00480   unsigned char
00481     buffer[4];
00482 
00483   unsigned long
00484     value;
00485 
00486   if (*length < 4)
00487     return(~0UL);
00488   for (i=0; i < 4; i++)
00489   {
00490     c=(int) (*(*p)++);
00491     (*length)--;
00492     buffer[i]=(unsigned char) c;
00493   }
00494   value=(unsigned long) (buffer[0] << 24);
00495   value|=buffer[1] << 16;
00496   value|=buffer[2] << 8;
00497   value|=buffer[3];
00498   return(value & 0xffffffff);
00499 }
00500 
00501 static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
00502   size_t *length)
00503 {
00504   int
00505     c;
00506 
00507   register long
00508     i;
00509 
00510   unsigned char
00511     buffer[2];
00512 
00513   unsigned short
00514     value;
00515 
00516   if (*length < 2)
00517     return((unsigned short) ~0U);
00518   for (i=0; i < 2; i++)
00519   {
00520     c=(int) (*(*p)++);
00521     (*length)--;
00522     buffer[i]=(unsigned char) c;
00523   }
00524   value=(unsigned short) (buffer[0] << 8);
00525   value|=buffer[1];
00526   return((unsigned short) (value & 0xffff));
00527 }
00528 
00529 static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
00530 {
00531   char
00532     *attribute,
00533     format[MaxTextExtent],
00534     name[MaxTextExtent],
00535     *resource;
00536 
00537   const StringInfo
00538     *profile;
00539 
00540   const unsigned char
00541     *info;
00542 
00543   long
00544     id,
00545     start,
00546     stop,
00547     sub_number;
00548 
00549   MagickBooleanType
00550     status;
00551 
00552   register long
00553     i;
00554 
00555   ssize_t
00556     count;
00557 
00558   size_t
00559     length;
00560 
00561   /*
00562     There's no newlines in path names, so it's safe as terminator.
00563   */
00564   profile=GetImageProfile(image,"8bim");
00565   if (profile == (StringInfo *) NULL)
00566     return(MagickFalse);
00567   count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
00568     format);
00569   if ((count != 2) && (count != 3) && (count != 4))
00570     return(MagickFalse);
00571   if (count < 4)
00572     (void) CopyMagickString(format,"SVG",MaxTextExtent);
00573   if (count < 3)
00574     *name='\0';
00575   sub_number=1;
00576   if (*name == '#')
00577     sub_number=atol(&name[1]);
00578   sub_number=MagickMax(sub_number,1L);
00579   resource=(char *) NULL;
00580   status=MagickFalse;
00581   length=GetStringInfoLength(profile);
00582   info=GetStringInfoDatum(profile);
00583   while ((length > 0) && (status == MagickFalse))
00584   {
00585     if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
00586       continue;
00587     if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
00588       continue;
00589     if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
00590       continue;
00591     if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
00592       continue;
00593     id=(long) ReadPropertyMSBShort(&info,&length);
00594     if (id < start)
00595       continue;
00596     if (id > stop)
00597       continue;
00598     if (resource != (char *) NULL)
00599       resource=DestroyString(resource);
00600     count=(ssize_t) ReadPropertyByte(&info,&length);
00601     if ((count != 0) && ((size_t) count <= length))
00602       {
00603         resource=(char *) NULL;
00604         if (~(1UL*count) >= MaxTextExtent)
00605           resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
00606             sizeof(*resource));
00607         if (resource != (char *) NULL)
00608           {
00609             for (i=0; i < (long) count; i++)
00610               resource[i]=(char) ReadPropertyByte(&info,&length);
00611             resource[count]='\0';
00612           }
00613       }
00614     if ((count & 0x01) == 0)
00615       (void) ReadPropertyByte(&info,&length);
00616     count=(ssize_t) ReadPropertyMSBLong(&info,&length);
00617     if ((*name != '\0') && (*name != '#'))
00618       if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
00619         {
00620           /*
00621             No name match, scroll forward and try next.
00622           */
00623           info+=count;
00624           length-=count;
00625           continue;
00626         }
00627     if ((*name == '#') && (sub_number != 1))
00628       {
00629         /*
00630           No numbered match, scroll forward and try next.
00631         */
00632         sub_number--;
00633         info+=count;
00634         length-=count;
00635         continue;
00636       }
00637     /*
00638       We have the resource of interest.
00639     */
00640     attribute=(char *) NULL;
00641     if (~(1UL*count) >= MaxTextExtent)
00642       attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
00643         sizeof(*attribute));
00644     if (attribute != (char *) NULL)
00645       {
00646         (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
00647         attribute[count]='\0';
00648         info+=count;
00649         length-=count;
00650         if ((id <= 1999) || (id >= 2999))
00651           (void) SetImageProperty((Image *) image,key,(const char *)
00652             attribute);
00653         else
00654           {
00655             char
00656               *path;
00657 
00658             if (LocaleCompare(format,"svg") == 0)
00659               path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
00660                 image->columns,image->rows);
00661             else
00662               path=TracePSClippath((unsigned char *) attribute,(size_t) count,
00663                 image->columns,image->rows);
00664             (void) SetImageProperty((Image *) image,key,(const char *) path);
00665             path=DestroyString(path);
00666           }
00667         attribute=DestroyString(attribute);
00668         status=MagickTrue;
00669       }
00670   }
00671   if (resource != (char *) NULL)
00672     resource=DestroyString(resource);
00673   return(status);
00674 }
00675 
00676 static inline unsigned short ReadPropertyShort(const EndianType endian,
00677   const unsigned char *buffer)
00678 {
00679   unsigned short
00680     value;
00681 
00682   if (endian == MSBEndian)
00683     {
00684       value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
00685         ((unsigned char *) buffer)[1]);
00686       return((unsigned short) (value & 0xffff));
00687     }
00688   value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
00689   return((unsigned short) (value & 0xffff));
00690 }
00691 
00692 static inline unsigned long ReadPropertyLong(const EndianType endian,
00693   const unsigned char *buffer)
00694 {
00695   unsigned long
00696     value;
00697 
00698   if (endian == MSBEndian)
00699     {
00700       value=(unsigned long) ((buffer[0] << 24) | (buffer[1] << 16) |
00701         (buffer[2] << 8) | buffer[3]);
00702       return((unsigned long) (value & 0xffffffff));
00703     }
00704   value=(unsigned long) ((buffer[3] << 24) | (buffer[2] << 16) |
00705     (buffer[1] << 8 ) | (buffer[0]));
00706   return((unsigned long) (value & 0xffffffff));
00707 }
00708 
00709 static MagickBooleanType GetEXIFProperty(const Image *image,
00710   const char *property)
00711 {
00712 #define MaxDirectoryStack  16
00713 #define EXIF_DELIMITER  "\n"
00714 #define EXIF_NUM_FORMATS  12
00715 #define EXIF_FMT_BYTE  1
00716 #define EXIF_FMT_STRING  2
00717 #define EXIF_FMT_USHORT  3
00718 #define EXIF_FMT_ULONG  4
00719 #define EXIF_FMT_URATIONAL  5
00720 #define EXIF_FMT_SBYTE  6
00721 #define EXIF_FMT_UNDEFINED  7
00722 #define EXIF_FMT_SSHORT  8
00723 #define EXIF_FMT_SLONG  9
00724 #define EXIF_FMT_SRATIONAL  10
00725 #define EXIF_FMT_SINGLE  11
00726 #define EXIF_FMT_DOUBLE  12
00727 #define TAG_EXIF_OFFSET  0x8769
00728 #define TAG_GPS_OFFSET  0x8825
00729 #define TAG_INTEROP_OFFSET  0xa005
00730 
00731 #define EXIFMultipleValues(size, format, arg) \
00732 { \
00733    long \
00734      component; \
00735  \
00736    size_t \
00737      length; \
00738  \
00739    unsigned char \
00740      *p1; \
00741  \
00742    length=0; \
00743    p1=p; \
00744    for (component=0; component < components; component++) \
00745    { \
00746      length+=FormatMagickString(buffer+length,MaxTextExtent-length, \
00747        format", ",arg); \
00748      if (length >= MaxTextExtent - 1) \
00749        length=MaxTextExtent-1; \
00750      p1+=size; \
00751    } \
00752    if (length > 1) \
00753      buffer[length-2]='\0'; \
00754    value=AcquireString(buffer); \
00755 }
00756 
00757 #define EXIFMultipleFractions(size, format, arg1, arg2) \
00758 { \
00759    long \
00760      component; \
00761  \
00762    size_t \
00763      length; \
00764  \
00765    unsigned char \
00766      *p1; \
00767  \
00768    length=0; \
00769    p1=p; \
00770    for (component=0; component < components; component++) \
00771    { \
00772      length+=FormatMagickString(buffer+length,MaxTextExtent-length, \
00773        format", ",arg1, arg2); \
00774      if (length >= MaxTextExtent - 1) \
00775        length=MaxTextExtent-1; \
00776      p1+=size; \
00777    } \
00778    if (length > 1) \
00779      buffer[length-2]='\0'; \
00780    value=AcquireString(buffer); \
00781 }
00782 
00783   typedef struct _DirectoryInfo
00784   {
00785     const unsigned char
00786       *directory;
00787 
00788     unsigned long
00789       entry,
00790       offset;
00791   } DirectoryInfo;
00792 
00793   typedef struct _TagInfo
00794   {
00795     unsigned long
00796       tag;
00797 
00798     const char
00799       *description;
00800   } TagInfo;
00801 
00802   static TagInfo
00803     EXIFTag[] =
00804     {
00805       {  0x001, "exif:InteroperabilityIndex" },
00806       {  0x002, "exif:InteroperabilityVersion" },
00807       {  0x100, "exif:ImageWidth" },
00808       {  0x101, "exif:ImageLength" },
00809       {  0x102, "exif:BitsPerSample" },
00810       {  0x103, "exif:Compression" },
00811       {  0x106, "exif:PhotometricInterpretation" },
00812       {  0x10a, "exif:FillOrder" },
00813       {  0x10d, "exif:DocumentName" },
00814       {  0x10e, "exif:ImageDescription" },
00815       {  0x10f, "exif:Make" },
00816       {  0x110, "exif:Model" },
00817       {  0x111, "exif:StripOffsets" },
00818       {  0x112, "exif:Orientation" },
00819       {  0x115, "exif:SamplesPerPixel" },
00820       {  0x116, "exif:RowsPerStrip" },
00821       {  0x117, "exif:StripByteCounts" },
00822       {  0x11a, "exif:XResolution" },
00823       {  0x11b, "exif:YResolution" },
00824       {  0x11c, "exif:PlanarConfiguration" },
00825       {  0x11d, "exif:PageName" },
00826       {  0x11e, "exif:XPosition" },
00827       {  0x11f, "exif:YPosition" },
00828       {  0x118, "exif:MinSampleValue" },
00829       {  0x119, "exif:MaxSampleValue" },
00830       {  0x120, "exif:FreeOffsets" },
00831       {  0x121, "exif:FreeByteCounts" },
00832       {  0x122, "exif:GrayResponseUnit" },
00833       {  0x123, "exif:GrayResponseCurve" },
00834       {  0x124, "exif:T4Options" },
00835       {  0x125, "exif:T6Options" },
00836       {  0x128, "exif:ResolutionUnit" },
00837       {  0x12d, "exif:TransferFunction" },
00838       {  0x131, "exif:Software" },
00839       {  0x132, "exif:DateTime" },
00840       {  0x13b, "exif:Artist" },
00841       {  0x13e, "exif:WhitePoint" },
00842       {  0x13f, "exif:PrimaryChromaticities" },
00843       {  0x140, "exif:ColorMap" },
00844       {  0x141, "exif:HalfToneHints" },
00845       {  0x142, "exif:TileWidth" },
00846       {  0x143, "exif:TileLength" },
00847       {  0x144, "exif:TileOffsets" },
00848       {  0x145, "exif:TileByteCounts" },
00849       {  0x14a, "exif:SubIFD" },
00850       {  0x14c, "exif:InkSet" },
00851       {  0x14d, "exif:InkNames" },
00852       {  0x14e, "exif:NumberOfInks" },
00853       {  0x150, "exif:DotRange" },
00854       {  0x151, "exif:TargetPrinter" },
00855       {  0x152, "exif:ExtraSample" },
00856       {  0x153, "exif:SampleFormat" },
00857       {  0x154, "exif:SMinSampleValue" },
00858       {  0x155, "exif:SMaxSampleValue" },
00859       {  0x156, "exif:TransferRange" },
00860       {  0x157, "exif:ClipPath" },
00861       {  0x158, "exif:XClipPathUnits" },
00862       {  0x159, "exif:YClipPathUnits" },
00863       {  0x15a, "exif:Indexed" },
00864       {  0x15b, "exif:JPEGTables" },
00865       {  0x15f, "exif:OPIProxy" },
00866       {  0x200, "exif:JPEGProc" },
00867       {  0x201, "exif:JPEGInterchangeFormat" },
00868       {  0x202, "exif:JPEGInterchangeFormatLength" },
00869       {  0x203, "exif:JPEGRestartInterval" },
00870       {  0x205, "exif:JPEGLosslessPredictors" },
00871       {  0x206, "exif:JPEGPointTransforms" },
00872       {  0x207, "exif:JPEGQTables" },
00873       {  0x208, "exif:JPEGDCTables" },
00874       {  0x209, "exif:JPEGACTables" },
00875       {  0x211, "exif:YCbCrCoefficients" },
00876       {  0x212, "exif:YCbCrSubSampling" },
00877       {  0x213, "exif:YCbCrPositioning" },
00878       {  0x214, "exif:ReferenceBlackWhite" },
00879       {  0x2bc, "exif:ExtensibleMetadataPlatform" },
00880       {  0x301, "exif:Gamma" },
00881       {  0x302, "exif:ICCProfileDescriptor" },
00882       {  0x303, "exif:SRGBRenderingIntent" },
00883       {  0x320, "exif:ImageTitle" },
00884       {  0x5001, "exif:ResolutionXUnit" },
00885       {  0x5002, "exif:ResolutionYUnit" },
00886       {  0x5003, "exif:ResolutionXLengthUnit" },
00887       {  0x5004, "exif:ResolutionYLengthUnit" },
00888       {  0x5005, "exif:PrintFlags" },
00889       {  0x5006, "exif:PrintFlagsVersion" },
00890       {  0x5007, "exif:PrintFlagsCrop" },
00891       {  0x5008, "exif:PrintFlagsBleedWidth" },
00892       {  0x5009, "exif:PrintFlagsBleedWidthScale" },
00893       {  0x500A, "exif:HalftoneLPI" },
00894       {  0x500B, "exif:HalftoneLPIUnit" },
00895       {  0x500C, "exif:HalftoneDegree" },
00896       {  0x500D, "exif:HalftoneShape" },
00897       {  0x500E, "exif:HalftoneMisc" },
00898       {  0x500F, "exif:HalftoneScreen" },
00899       {  0x5010, "exif:JPEGQuality" },
00900       {  0x5011, "exif:GridSize" },
00901       {  0x5012, "exif:ThumbnailFormat" },
00902       {  0x5013, "exif:ThumbnailWidth" },
00903       {  0x5014, "exif:ThumbnailHeight" },
00904       {  0x5015, "exif:ThumbnailColorDepth" },
00905       {  0x5016, "exif:ThumbnailPlanes" },
00906       {  0x5017, "exif:ThumbnailRawBytes" },
00907       {  0x5018, "exif:ThumbnailSize" },
00908       {  0x5019, "exif:ThumbnailCompressedSize" },
00909       {  0x501a, "exif:ColorTransferFunction" },
00910       {  0x501b, "exif:ThumbnailData" },
00911       {  0x5020, "exif:ThumbnailImageWidth" },
00912       {  0x5021, "exif:ThumbnailImageHeight" },
00913       {  0x5022, "exif:ThumbnailBitsPerSample" },
00914       {  0x5023, "exif:ThumbnailCompression" },
00915       {  0x5024, "exif:ThumbnailPhotometricInterp" },
00916       {  0x5025, "exif:ThumbnailImageDescription" },
00917       {  0x5026, "exif:ThumbnailEquipMake" },
00918       {  0x5027, "exif:ThumbnailEquipModel" },
00919       {  0x5028, "exif:ThumbnailStripOffsets" },
00920       {  0x5029, "exif:ThumbnailOrientation" },
00921       {  0x502a, "exif:ThumbnailSamplesPerPixel" },
00922       {  0x502b, "exif:ThumbnailRowsPerStrip" },
00923       {  0x502c, "exif:ThumbnailStripBytesCount" },
00924       {  0x502d, "exif:ThumbnailResolutionX" },
00925       {  0x502e, "exif:ThumbnailResolutionY" },
00926       {  0x502f, "exif:ThumbnailPlanarConfig" },
00927       {  0x5030, "exif:ThumbnailResolutionUnit" },
00928       {  0x5031, "exif:ThumbnailTransferFunction" },
00929       {  0x5032, "exif:ThumbnailSoftwareUsed" },
00930       {  0x5033, "exif:ThumbnailDateTime" },
00931       {  0x5034, "exif:ThumbnailArtist" },
00932       {  0x5035, "exif:ThumbnailWhitePoint" },
00933       {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
00934       {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
00935       {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
00936       {  0x5039, "exif:ThumbnailYCbCrPositioning" },
00937       {  0x503A, "exif:ThumbnailRefBlackWhite" },
00938       {  0x503B, "exif:ThumbnailCopyRight" },
00939       {  0x5090, "exif:LuminanceTable" },
00940       {  0x5091, "exif:ChrominanceTable" },
00941       {  0x5100, "exif:FrameDelay" },
00942       {  0x5101, "exif:LoopCount" },
00943       {  0x5110, "exif:PixelUnit" },
00944       {  0x5111, "exif:PixelPerUnitX" },
00945       {  0x5112, "exif:PixelPerUnitY" },
00946       {  0x5113, "exif:PaletteHistogram" },
00947       {  0x1000, "exif:RelatedImageFileFormat" },
00948       {  0x1001, "exif:RelatedImageLength" },
00949       {  0x1002, "exif:RelatedImageWidth" },
00950       {  0x800d, "exif:ImageID" },
00951       {  0x80e3, "exif:Matteing" },
00952       {  0x80e4, "exif:DataType" },
00953       {  0x80e5, "exif:ImageDepth" },
00954       {  0x80e6, "exif:TileDepth" },
00955       {  0x828d, "exif:CFARepeatPatternDim" },
00956       {  0x828e, "exif:CFAPattern2" },
00957       {  0x828f, "exif:BatteryLevel" },
00958       {  0x8298, "exif:Copyright" },
00959       {  0x829a, "exif:ExposureTime" },
00960       {  0x829d, "exif:FNumber" },
00961       {  0x83bb, "exif:IPTC/NAA" },
00962       {  0x84e3, "exif:IT8RasterPadding" },
00963       {  0x84e5, "exif:IT8ColorTable" },
00964       {  0x8649, "exif:ImageResourceInformation" },
00965       {  0x8769, "exif:ExifOffset" },
00966       {  0x8773, "exif:InterColorProfile" },
00967       {  0x8822, "exif:ExposureProgram" },
00968       {  0x8824, "exif:SpectralSensitivity" },
00969       {  0x8825, "exif:GPSInfo" },
00970       {  0x8827, "exif:ISOSpeedRatings" },
00971       {  0x8828, "exif:OECF" },
00972       {  0x8829, "exif:Interlace" },
00973       {  0x882a, "exif:TimeZoneOffset" },
00974       {  0x882b, "exif:SelfTimerMode" },
00975       {  0x9000, "exif:ExifVersion" },
00976       {  0x9003, "exif:DateTimeOriginal" },
00977       {  0x9004, "exif:DateTimeDigitized" },
00978       {  0x9101, "exif:ComponentsConfiguration" },
00979       {  0x9102, "exif:CompressedBitsPerPixel" },
00980       {  0x9201, "exif:ShutterSpeedValue" },
00981       {  0x9202, "exif:ApertureValue" },
00982       {  0x9203, "exif:BrightnessValue" },
00983       {  0x9204, "exif:ExposureBiasValue" },
00984       {  0x9205, "exif:MaxApertureValue" },
00985       {  0x9206, "exif:SubjectDistance" },
00986       {  0x9207, "exif:MeteringMode" },
00987       {  0x9208, "exif:LightSource" },
00988       {  0x9209, "exif:Flash" },
00989       {  0x920a, "exif:FocalLength" },
00990       {  0x920b, "exif:FlashEnergy" },
00991       {  0x920c, "exif:SpatialFrequencyResponse" },
00992       {  0x920d, "exif:Noise" },
00993       {  0x9211, "exif:ImageNumber" },
00994       {  0x9212, "exif:SecurityClassification" },
00995       {  0x9213, "exif:ImageHistory" },
00996       {  0x9214, "exif:SubjectArea" },
00997       {  0x9215, "exif:ExposureIndex" },
00998       {  0x9216, "exif:TIFF-EPStandardID" },
00999       {  0x927c, "exif:MakerNote" },
01000       {  0x9C9b, "exif:WinXP-Title" },
01001       {  0x9C9c, "exif:WinXP-Comments" },
01002       {  0x9C9d, "exif:WinXP-Author" },
01003       {  0x9C9e, "exif:WinXP-Keywords" },
01004       {  0x9C9f, "exif:WinXP-Subject" },
01005       {  0x9286, "exif:UserComment" },
01006       {  0x9290, "exif:SubSecTime" },
01007       {  0x9291, "exif:SubSecTimeOriginal" },
01008       {  0x9292, "exif:SubSecTimeDigitized" },
01009       {  0xa000, "exif:FlashPixVersion" },
01010       {  0xa001, "exif:ColorSpace" },
01011       {  0xa002, "exif:ExifImageWidth" },
01012       {  0xa003, "exif:ExifImageLength" },
01013       {  0xa004, "exif:RelatedSoundFile" },
01014       {  0xa005, "exif:InteroperabilityOffset" },
01015       {  0xa20b, "exif:FlashEnergy" },
01016       {  0xa20c, "exif:SpatialFrequencyResponse" },
01017       {  0xa20d, "exif:Noise" },
01018       {  0xa20e, "exif:FocalPlaneXResolution" },
01019       {  0xa20f, "exif:FocalPlaneYResolution" },
01020       {  0xa210, "exif:FocalPlaneResolutionUnit" },
01021       {  0xa214, "exif:SubjectLocation" },
01022       {  0xa215, "exif:ExposureIndex" },
01023       {  0xa216, "exif:TIFF/EPStandardID" },
01024       {  0xa217, "exif:SensingMethod" },
01025       {  0xa300, "exif:FileSource" },
01026       {  0xa301, "exif:SceneType" },
01027       {  0xa302, "exif:CFAPattern" },
01028       {  0xa401, "exif:CustomRendered" },
01029       {  0xa402, "exif:ExposureMode" },
01030       {  0xa403, "exif:WhiteBalance" },
01031       {  0xa404, "exif:DigitalZoomRatio" },
01032       {  0xa405, "exif:FocalLengthIn35mmFilm" },
01033       {  0xa406, "exif:SceneCaptureType" },
01034       {  0xa407, "exif:GainControl" },
01035       {  0xa408, "exif:Contrast" },
01036       {  0xa409, "exif:Saturation" },
01037       {  0xa40a, "exif:Sharpness" },
01038       {  0xa40b, "exif:DeviceSettingDescription" },
01039       {  0xa40c, "exif:SubjectDistanceRange" },
01040       {  0xa420, "exif:ImageUniqueID" },
01041       {  0xc4a5, "exif:PrintImageMatching" },
01042       { 0x10000, "exif:GPSVersionID" },
01043       { 0x10001, "exif:GPSLatitudeRef" },
01044       { 0x10002, "exif:GPSLatitude" },
01045       { 0x10003, "exif:GPSLongitudeRef" },
01046       { 0x10004, "exif:GPSLongitude" },
01047       { 0x10005, "exif:GPSAltitudeRef" },
01048       { 0x10006, "exif:GPSAltitude" },
01049       { 0x10007, "exif:GPSTimeStamp" },
01050       { 0x10008, "exif:GPSSatellites" },
01051       { 0x10009, "exif:GPSStatus" },
01052       { 0x1000a, "exif:GPSMeasureMode" },
01053       { 0x1000b, "exif:GPSDop" },
01054       { 0x1000c, "exif:GPSSpeedRef" },
01055       { 0x1000d, "exif:GPSSpeed" },
01056       { 0x1000e, "exif:GPSTrackRef" },
01057       { 0x1000f, "exif:GPSTrack" },
01058       { 0x10010, "exif:GPSImgDirectionRef" },
01059       { 0x10011, "exif:GPSImgDirection" },
01060       { 0x10012, "exif:GPSMapDatum" },
01061       { 0x10013, "exif:GPSDestLatitudeRef" },
01062       { 0x10014, "exif:GPSDestLatitude" },
01063       { 0x10015, "exif:GPSDestLongitudeRef" },
01064       { 0x10016, "exif:GPSDestLongitude" },
01065       { 0x10017, "exif:GPSDestBearingRef" },
01066       { 0x10018, "exif:GPSDestBearing" },
01067       { 0x10019, "exif:GPSDestDistanceRef" },
01068       { 0x1001a, "exif:GPSDestDistance" },
01069       { 0x1001b, "exif:GPSProcessingMethod" },
01070       { 0x1001c, "exif:GPSAreaInformation" },
01071       { 0x1001d, "exif:GPSDateStamp" },
01072       { 0x1001e, "exif:GPSDifferential" },
01073       {  0x0000, NULL}
01074     };
01075 
01076   const StringInfo
01077     *profile;
01078 
01079   const unsigned char
01080     *directory,
01081     *exif;
01082 
01083   DirectoryInfo
01084     directory_stack[MaxDirectoryStack];
01085 
01086   EndianType
01087     endian;
01088 
01089   long
01090     all,
01091     id,
01092     level,
01093     tag_value;
01094 
01095   register long
01096     i;
01097 
01098   size_t
01099     length;
01100 
01101   ssize_t
01102     offset;
01103 
01104   static int
01105     tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
01106 
01107   unsigned long
01108     entry,
01109     number_entries,
01110     tag_offset,
01111     tag;
01112 
01113   /*
01114     If EXIF data exists, then try to parse the request for a tag.
01115   */
01116   profile=GetImageProfile(image,"exif");
01117   if (profile == (StringInfo *) NULL)
01118     return(MagickFalse);
01119   if ((property == (const char *) NULL) || (*property == '\0'))
01120     return(MagickFalse);
01121   while (isspace((int) ((unsigned char) *property)) != 0)
01122     property++;
01123   all=0;
01124   tag=(~0UL);
01125   switch (*(property+5))
01126   {
01127     case '*':
01128     {
01129       /*
01130         Caller has asked for all the tags in the EXIF data.
01131       */
01132       tag=0;
01133       all=1; /* return the data in description=value format */
01134       break;
01135     }
01136     case '!':
01137     {
01138       tag=0;
01139       all=2; /* return the data in tagid=value format */
01140       break;
01141     }
01142     case '#':
01143     case '@':
01144     {
01145       int
01146         c;
01147 
01148       size_t
01149         n;
01150 
01151       /*
01152         Check for a hex based tag specification first.
01153       */
01154       tag=(*(property+5) == '@') ? 1UL : 0UL;
01155       property+=6;
01156       n=strlen(property);
01157       if (n != 4)
01158         return(MagickFalse);
01159       /*
01160         Parse tag specification as a hex number.
01161       */
01162       n/=4;
01163       do
01164       {
01165         for (i=(long) n-1L; i >= 0; i--)
01166         {
01167           c=(*property++);
01168           tag<<=4;
01169           if ((c >= '0') && (c <= '9'))
01170             tag|=(c-'0');
01171           else
01172             if ((c >= 'A') && (c <= 'F'))
01173               tag|=(c-('A'-10));
01174             else
01175               if ((c >= 'a') && (c <= 'f'))
01176                 tag|=(c-('a'-10));
01177               else
01178                 return(MagickFalse);
01179         }
01180       } while (*property != '\0');
01181       break;
01182     }
01183     default:
01184     {
01185       /*
01186         Try to match the text with a tag name instead.
01187       */
01188       for (i=0; ; i++)
01189       {
01190         if (EXIFTag[i].tag == 0)
01191           break;
01192         if (LocaleCompare(EXIFTag[i].description,property) == 0)
01193           {
01194             tag=(unsigned long) EXIFTag[i].tag;
01195             break;
01196           }
01197       }
01198       break;
01199     }
01200   }
01201   if (tag == (~0UL))
01202     return(MagickFalse);
01203   length=GetStringInfoLength(profile);
01204   exif=GetStringInfoDatum(profile);
01205   while (length != 0)
01206   {
01207     if (ReadPropertyByte(&exif,&length) != 0x45)
01208       continue;
01209     if (ReadPropertyByte(&exif,&length) != 0x78)
01210       continue;
01211     if (ReadPropertyByte(&exif,&length) != 0x69)
01212       continue;
01213     if (ReadPropertyByte(&exif,&length) != 0x66)
01214       continue;
01215     if (ReadPropertyByte(&exif,&length) != 0x00)
01216       continue;
01217     if (ReadPropertyByte(&exif,&length) != 0x00)
01218       continue;
01219     break;
01220   }
01221   if (length < 16)
01222     return(MagickFalse);
01223   id=(long) ReadPropertyShort(LSBEndian,exif);
01224   endian=LSBEndian;
01225   if (id == 0x4949)
01226     endian=LSBEndian;
01227   else
01228     if (id == 0x4D4D)
01229       endian=MSBEndian;
01230     else
01231       return(MagickFalse);
01232   if (ReadPropertyShort(endian,exif+2) != 0x002a)
01233     return(MagickFalse);
01234   /*
01235     This the offset to the first IFD.
01236   */
01237   offset=(ssize_t) ReadPropertyLong(endian,exif+4);
01238   if ((size_t) offset >= length)
01239     return(MagickFalse);
01240   /*
01241     Set the pointer to the first IFD and follow it were it leads.
01242   */
01243   directory=exif+offset;
01244   level=0;
01245   entry=0;
01246   tag_offset=0;
01247   do
01248   {
01249     /*
01250       If there is anything on the stack then pop it off.
01251     */
01252     if (level > 0)
01253       {
01254         level--;
01255         directory=directory_stack[level].directory;
01256         entry=directory_stack[level].entry;
01257         tag_offset=directory_stack[level].offset;
01258       }
01259     /*
01260       Determine how many entries there are in the current IFD.
01261     */
01262     number_entries=ReadPropertyShort(endian,directory);
01263     for ( ; entry < number_entries; entry++)
01264     {
01265       long
01266         components;
01267 
01268       register unsigned char
01269         *p,
01270         *q;
01271 
01272       size_t
01273         number_bytes;
01274 
01275       unsigned long
01276         format;
01277 
01278       q=(unsigned char *) (directory+2+(12*entry));
01279       tag_value=(long) ReadPropertyShort(endian,q)+tag_offset;
01280       format=(unsigned long) ReadPropertyShort(endian,q+2);
01281       if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
01282         break;
01283       components=(long) ReadPropertyLong(endian,q+4);
01284       number_bytes=(size_t) components*tag_bytes[format];
01285       if (number_bytes <= 4)
01286         p=q+8;
01287       else
01288         {
01289           ssize_t
01290             offset;
01291 
01292           /*
01293             The directory entry contains an offset.
01294           */
01295           offset=(ssize_t) ReadPropertyLong(endian,q+8);
01296           if ((size_t) (offset+number_bytes) > length)
01297             continue;
01298           p=(unsigned char *) (exif+offset);
01299         }
01300       if ((all != 0) || (tag == (unsigned long) tag_value))
01301         {
01302           char
01303             buffer[MaxTextExtent],
01304             *value;
01305 
01306           switch (format)
01307           {
01308             case EXIF_FMT_BYTE:
01309             case EXIF_FMT_UNDEFINED:
01310             {
01311               EXIFMultipleValues(1,"%lu",(unsigned long)
01312                 (*(unsigned char *) p1));
01313               break;
01314             }
01315             case EXIF_FMT_SBYTE:
01316             {
01317               EXIFMultipleValues(1,"%ld",(long) (*(signed char *) p1));
01318               break;
01319             }
01320             case EXIF_FMT_SSHORT:
01321             {
01322               EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
01323               break;
01324             }
01325             case EXIF_FMT_USHORT:
01326             {
01327               EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
01328               break;
01329             }
01330             case EXIF_FMT_ULONG:
01331             {
01332               EXIFMultipleValues(4,"%lu",ReadPropertyLong(endian,p1));
01333               break;
01334             }
01335             case EXIF_FMT_SLONG:
01336             {
01337               EXIFMultipleValues(4,"%ld",ReadPropertyLong(endian,p1));
01338               break;
01339             }
01340             case EXIF_FMT_URATIONAL:
01341             {
01342               EXIFMultipleFractions(8,"%ld/%ld",ReadPropertyLong(endian,p1),
01343                 ReadPropertyLong(endian,p1+4));
01344               break;
01345             }
01346             case EXIF_FMT_SRATIONAL:
01347             {
01348               EXIFMultipleFractions(8,"%ld/%ld",ReadPropertyLong(endian,p1),
01349                 ReadPropertyLong(endian,p1+4));
01350               break;
01351             }
01352             case EXIF_FMT_SINGLE:
01353             {
01354               EXIFMultipleValues(4,"%f",(double) *(float *) p1);
01355               break;
01356             }
01357             case EXIF_FMT_DOUBLE:
01358             {
01359               EXIFMultipleValues(8,"%f",*(double *) p1);
01360               break;
01361             }
01362             default:
01363             case EXIF_FMT_STRING:
01364             {
01365               value=(char *) NULL;
01366               if (~(1UL*number_bytes) >= 1)
01367                 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
01368                   sizeof(*value));
01369               if (value != (char *) NULL)
01370                 {
01371                   register long
01372                     i;
01373 
01374                   for (i=0; i < (long) number_bytes; i++)
01375                   {
01376                     value[i]='.';
01377                     if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
01378                       value[i]=(char) p[i];
01379                   }
01380                   value[i]='\0';
01381                 }
01382               break;
01383             }
01384           }
01385           if (value != (char *) NULL)
01386             {
01387               char
01388                 key[MaxTextExtent];
01389 
01390               register const char
01391                 *p;
01392 
01393               (void) CopyMagickString(key,property,MaxTextExtent);
01394               switch (all)
01395               {
01396                 case 1:
01397                 {
01398                   const char
01399                     *description;
01400 
01401                   register long
01402                     i;
01403 
01404                   description="unknown";
01405                   for (i=0; ; i++)
01406                   {
01407                     if (EXIFTag[i].tag == 0)
01408                       break;
01409                     if ((long) EXIFTag[i].tag == tag_value)
01410                       {
01411                         description=EXIFTag[i].description;
01412                         break;
01413                       }
01414                   }
01415                   (void) FormatMagickString(key,MaxTextExtent,"%s",
01416                     description);
01417                   break;
01418                 }
01419                 case 2:
01420                 {
01421                   if (tag_value < 0x10000)
01422                     (void) FormatMagickString(key,MaxTextExtent,"#%04lx",
01423                       tag_value);
01424                   else
01425                     if (tag_value < 0x20000)
01426                       (void) FormatMagickString(key,MaxTextExtent,"@%04lx",
01427                         tag_value & 0xffff);
01428                     else
01429                       (void) FormatMagickString(key,MaxTextExtent,"unknown");
01430                   break;
01431                 }
01432               }
01433               p=(const char *) NULL;
01434               if (image->properties != (void *) NULL)
01435                 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
01436                   image->properties,key);
01437               if (p == (const char *) NULL)
01438                 (void) SetImageProperty((Image *) image,key,value);
01439               value=DestroyString(value);
01440             }
01441         }
01442         if ((tag_value == TAG_EXIF_OFFSET) ||
01443             (tag_value == TAG_INTEROP_OFFSET) ||
01444             (tag_value == TAG_GPS_OFFSET))
01445           {
01446             size_t
01447               offset;
01448 
01449             offset=(size_t) ReadPropertyLong(endian,p);
01450             if ((offset < length) && (level < (MaxDirectoryStack-2)))
01451               {
01452                 unsigned long
01453                   tag_offset1;
01454 
01455                 tag_offset1=(tag_value == TAG_GPS_OFFSET) ? 0x10000UL : 0UL;
01456                 directory_stack[level].directory=directory;
01457                 entry++;
01458                 directory_stack[level].entry=entry;
01459                 directory_stack[level].offset=tag_offset;
01460                 level++;
01461                 directory_stack[level].directory=exif+offset;
01462                 directory_stack[level].offset=tag_offset1;
01463                 directory_stack[level].entry=0;
01464                 level++;
01465                 if ((directory+2+(12*number_entries)) > (exif+length))
01466                   break;
01467                 offset=(size_t) ReadPropertyLong(endian,directory+2+(12*
01468                   number_entries));
01469                 if ((offset != 0) && (offset < length) &&
01470                     (level < (MaxDirectoryStack-2)))
01471                   {
01472                     directory_stack[level].directory=exif+offset;
01473                     directory_stack[level].entry=0;
01474                     directory_stack[level].offset=tag_offset1;
01475                     level++;
01476                   }
01477               }
01478             break;
01479           }
01480     }
01481   } while (level > 0);
01482   return(MagickTrue);
01483 }
01484 
01485 static MagickBooleanType GetXMPProperty(const Image *image,
01486   const char *property)
01487 {
01488   char
01489     *xmp_profile;
01490 
01491   const StringInfo
01492     *profile;
01493 
01494   ExceptionInfo
01495     *exception;
01496 
01497   MagickBooleanType
01498     status;
01499 
01500   register const char
01501     *p;
01502 
01503   XMLTreeInfo
01504     *child,
01505     *description,
01506     *node,
01507     *rdf,
01508     *xmp;
01509 
01510   profile=GetImageProfile(image,"xmp");
01511   if (profile == (StringInfo *) NULL)
01512     return(MagickFalse);
01513   if ((property == (const char *) NULL) || (*property == '\0'))
01514     return(MagickFalse);
01515   xmp_profile=StringInfoToString(profile);
01516   if (xmp_profile == (char *) NULL)
01517     return(MagickFalse);
01518   for (p=xmp_profile; *p != '\0'; p++)
01519     if ((*p == '<') && (*(p+1) == 'x'))
01520       break;
01521   exception=AcquireExceptionInfo();
01522   xmp=NewXMLTree((char *) p,exception);
01523   xmp_profile=DestroyString(xmp_profile);
01524   exception=DestroyExceptionInfo(exception);
01525   if (xmp == (XMLTreeInfo *) NULL)
01526     return(MagickFalse);
01527   status=MagickFalse;
01528   rdf=GetXMLTreeChild(xmp,"rdf:RDF");
01529   if (rdf != (XMLTreeInfo *) NULL)
01530     {
01531       if (image->properties == (void *) NULL)
01532         ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
01533           RelinquishMagickMemory,RelinquishMagickMemory);
01534       description=GetXMLTreeChild(rdf,"rdf:Description");
01535       while (description != (XMLTreeInfo *) NULL)
01536       {
01537         node=GetXMLTreeChild(description,(const char *) NULL);
01538         while (node != (XMLTreeInfo *) NULL)
01539         {
01540           child=GetXMLTreeChild(node,(const char *) NULL);
01541           if (child == (XMLTreeInfo *) NULL)
01542             (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
01543               ConstantString(GetXMLTreeTag(node)),
01544               ConstantString(GetXMLTreeContent(node)));
01545           while (child != (XMLTreeInfo *) NULL)
01546           {
01547             if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
01548               (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
01549                 ConstantString(GetXMLTreeTag(child)),
01550                 ConstantString(GetXMLTreeContent(child)));
01551             child=GetXMLTreeSibling(child);
01552           }
01553           node=GetXMLTreeSibling(node);
01554         }
01555         description=GetNextXMLTreeTag(description);
01556       }
01557     }
01558   xmp=DestroyXMLTree(xmp);
01559   return(status);
01560 }
01561 
01562 static char *TracePSClippath(const unsigned char *blob,size_t length,
01563   const unsigned long magick_unused(columns),
01564   const unsigned long magick_unused(rows))
01565 {
01566   char
01567     *path,
01568     *message;
01569 
01570   long
01571     knot_count,
01572     selector,
01573     y;
01574 
01575   MagickBooleanType
01576     in_subpath;
01577 
01578   PointInfo
01579     first[3],
01580     last[3],
01581     point[3];
01582 
01583   register long
01584     i,
01585     x;
01586 
01587   path=AcquireString((char *) NULL);
01588   if (path == (char *) NULL)
01589     return((char *) NULL);
01590   message=AcquireString((char *) NULL);
01591   (void) FormatMagickString(message,MaxTextExtent,"/ClipImage\n");
01592   (void) ConcatenateString(&path,message);
01593   (void) FormatMagickString(message,MaxTextExtent,"{\n");
01594   (void) ConcatenateString(&path,message);
01595   (void) FormatMagickString(message,MaxTextExtent,"  /c {curveto} bind def\n");
01596   (void) ConcatenateString(&path,message);
01597   (void) FormatMagickString(message,MaxTextExtent,"  /l {lineto} bind def\n");
01598   (void) ConcatenateString(&path,message);
01599   (void) FormatMagickString(message,MaxTextExtent,"  /m {moveto} bind def\n");
01600   (void) ConcatenateString(&path,message);
01601   (void) FormatMagickString(message,MaxTextExtent,
01602     "  /v {currentpoint 6 2 roll curveto} bind def\n");
01603   (void) ConcatenateString(&path,message);
01604   (void) FormatMagickString(message,MaxTextExtent,
01605     "  /y {2 copy curveto} bind def\n");
01606   (void) ConcatenateString(&path,message);
01607   (void) FormatMagickString(message,MaxTextExtent,
01608     "  /z {closepath} bind def\n");
01609   (void) ConcatenateString(&path,message);
01610   (void) FormatMagickString(message,MaxTextExtent,"  newpath\n");
01611   (void) ConcatenateString(&path,message);
01612   /*
01613     The clipping path format is defined in "Adobe Photoshop File
01614     Formats Specification" version 6.0 downloadable from adobe.com.
01615   */
01616   (void) ResetMagickMemory(point,0,sizeof(point));
01617   (void) ResetMagickMemory(first,0,sizeof(first));
01618   (void) ResetMagickMemory(last,0,sizeof(last));
01619   knot_count=0;
01620   in_subpath=MagickFalse;
01621   while (length > 0)
01622   {
01623     selector=(long) ReadPropertyMSBShort(&blob,&length);
01624     switch (selector)
01625     {
01626       case 0:
01627       case 3:
01628       {
01629         if (knot_count != 0)
01630           {
01631             blob+=24;
01632             length-=24;
01633             break;
01634           }
01635         /*
01636           Expected subpath length record.
01637         */
01638         knot_count=(long) ReadPropertyMSBShort(&blob,&length);
01639         blob+=22;
01640         length-=22;
01641         break;
01642       }
01643       case 1:
01644       case 2:
01645       case 4:
01646       case 5:
01647       {
01648         if (knot_count == 0)
01649           {
01650             /*
01651               Unexpected subpath knot
01652             */
01653             blob+=24;
01654             length-=24;
01655             break;
01656           }
01657         /*
01658           Add sub-path knot
01659         */
01660         for (i=0; i < 3; i++)
01661         {
01662           unsigned long 
01663             xx,
01664             yy;
01665 
01666           yy=ReadPropertyMSBLong(&blob,&length);
01667           xx=ReadPropertyMSBLong(&blob,&length);
01668           x=(long) xx;
01669           if (xx > 2147483647)
01670             x=xx-4294967295-1;
01671           y=(long) yy;
01672           if (yy > 2147483647)
01673             y=yy-4294967295-1;
01674           point[i].x=(double) x/4096/4096;
01675           point[i].y=1.0-(double) y/4096/4096;
01676         }
01677         if (in_subpath == MagickFalse)
01678           {
01679             (void) FormatMagickString(message,MaxTextExtent,"  %g %g m\n",
01680               point[1].x,point[1].y);
01681             for (i=0; i < 3; i++)
01682             {
01683               first[i]=point[i];
01684               last[i]=point[i];
01685             }
01686           }
01687         else
01688           {
01689             /*
01690               Handle special cases when Bezier curves are used to describe
01691               corners and straight lines.
01692             */
01693             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01694                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
01695               (void) FormatMagickString(message,MaxTextExtent,"  %g %g l\n",
01696                 point[1].x,point[1].y);
01697             else
01698               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
01699                 (void) FormatMagickString(message,MaxTextExtent,
01700                   "  %g %g %g %g v\n",point[0].x,point[0].y,point[1].x,
01701                   point[1].y);
01702               else
01703                 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
01704                   (void) FormatMagickString(message,MaxTextExtent,
01705                     "  %g %g %g %g y\n",last[2].x,last[2].y,point[1].x,
01706                     point[1].y);
01707                 else
01708                   (void) FormatMagickString(message,MaxTextExtent,
01709                     "  %g %g %g %g %g %g c\n",last[2].x,last[2].y,point[0].x,
01710                     point[0].y,point[1].x,point[1].y);
01711             for (i=0; i < 3; i++)
01712               last[i]=point[i];
01713           }
01714         (void) ConcatenateString(&path,message);
01715         in_subpath=MagickTrue;
01716         knot_count--;
01717         /*
01718           Close the subpath if there are no more knots.
01719         */
01720         if (knot_count == 0)
01721           {
01722             /*
01723               Same special handling as above except we compare to the
01724               first point in the path and close the path.
01725             */
01726             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01727                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
01728               (void) FormatMagickString(message,MaxTextExtent,"  %g %g l z\n",
01729                 first[1].x,first[1].y);
01730             else
01731               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
01732                 (void) FormatMagickString(message,MaxTextExtent,
01733                   "  %g %g %g %g v z\n",first[0].x,first[0].y,first[1].x,
01734                   first[1].y);
01735               else
01736                 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
01737                   (void) FormatMagickString(message,MaxTextExtent,
01738                     "  %g %g %g %g y z\n",last[2].x,last[2].y,first[1].x,
01739                     first[1].y);
01740                 else
01741                   (void) FormatMagickString(message,MaxTextExtent,
01742                     "  %g %g %g %g %g %g c z\n",last[2].x,last[2].y,first[0].x,
01743                     first[0].y,first[1].x,first[1].y);
01744             (void) ConcatenateString(&path,message);
01745             in_subpath=MagickFalse;
01746           }
01747         break;
01748       }
01749       case 6:
01750       case 7:
01751       case 8:
01752       default:
01753       {
01754         blob+=24;
01755         length-=24;
01756         break;
01757       }
01758     }
01759   }
01760   /*
01761     Returns an empty PS path if the path has no knots.
01762   */
01763   (void) FormatMagickString(message,MaxTextExtent,"  eoclip\n");
01764   (void) ConcatenateString(&path,message);
01765   (void) FormatMagickString(message,MaxTextExtent,"} bind def");
01766   (void) ConcatenateString(&path,message);
01767   message=DestroyString(message);
01768   return(path);
01769 }
01770 
01771 static char *TraceSVGClippath(const unsigned char *blob,size_t length,
01772   const unsigned long columns,const unsigned long rows)
01773 {
01774   char
01775     *path,
01776     *message;
01777 
01778   long
01779     knot_count,
01780     selector,
01781     x,
01782     y;
01783 
01784   MagickBooleanType
01785     in_subpath;
01786 
01787   PointInfo
01788     first[3],
01789     last[3],
01790     point[3];
01791 
01792   register long
01793     i;
01794 
01795   path=AcquireString((char *) NULL);
01796   if (path == (char *) NULL)
01797     return((char *) NULL);
01798   message=AcquireString((char *) NULL);
01799   (void) FormatMagickString(message,MaxTextExtent,
01800     "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
01801   (void) ConcatenateString(&path,message);
01802   (void) FormatMagickString(message,MaxTextExtent,
01803     "<svg width=\"%lu\" height=\"%lu\">\n",columns,rows);
01804   (void) ConcatenateString(&path,message);
01805   (void) FormatMagickString(message,MaxTextExtent,"<g>\n");
01806   (void) ConcatenateString(&path,message);
01807   (void) FormatMagickString(message,MaxTextExtent,
01808     "<path style=\"fill:#00000000;stroke:#00000000;");
01809   (void) ConcatenateString(&path,message);
01810   (void) FormatMagickString(message,MaxTextExtent,
01811     "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
01812   (void) ConcatenateString(&path,message);
01813   (void) ResetMagickMemory(point,0,sizeof(point));
01814   (void) ResetMagickMemory(first,0,sizeof(first));
01815   (void) ResetMagickMemory(last,0,sizeof(last));
01816   knot_count=0;
01817   in_subpath=MagickFalse;
01818   while (length != 0)
01819   {
01820     selector=(long) ReadPropertyMSBShort(&blob,&length);
01821     switch (selector)
01822     {
01823       case 0:
01824       case 3:
01825       {
01826         if (knot_count != 0)
01827           {
01828             blob+=24;
01829             length-=24;
01830             break;
01831           }
01832         /*
01833           Expected subpath length record.
01834         */
01835         knot_count=(long) ReadPropertyMSBShort(&blob,&length);
01836         blob+=22;
01837         length-=22;
01838         break;
01839       }
01840       case 1:
01841       case 2:
01842       case 4:
01843       case 5:
01844       {
01845         if (knot_count == 0)
01846           {
01847             /*
01848               Unexpected subpath knot.
01849             */
01850             blob+=24;
01851             length-=24;
01852             break;
01853           }
01854         /*
01855           Add sub-path knot
01856         */
01857         for (i=0; i < 3; i++)
01858         {
01859           unsigned long 
01860             xx,
01861             yy;
01862 
01863           yy=ReadPropertyMSBLong(&blob,&length);
01864           xx=ReadPropertyMSBLong(&blob,&length);
01865           x=(long) xx;
01866           if (xx > 2147483647)
01867             x=xx-4294967295-1;
01868           y=(long) yy;
01869           if (yy > 2147483647)
01870             y=yy-4294967295-1;
01871           point[i].x=(double) x*columns/4096/4096;
01872           point[i].y=(double) y*rows/4096/4096;
01873         }
01874         if (in_subpath == MagickFalse)
01875           {
01876             (void) FormatMagickString(message,MaxTextExtent,"M %g,%g\n",
01877               point[1].x,point[1].y);
01878             for (i=0; i < 3; i++)
01879             {
01880               first[i]=point[i];
01881               last[i]=point[i];
01882             }
01883           }
01884         else
01885           {
01886             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01887                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
01888               (void) FormatMagickString(message,MaxTextExtent,"L %g,%g\n",
01889                 point[1].x,point[1].y);
01890             else
01891               (void) FormatMagickString(message,MaxTextExtent,
01892                 "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
01893                 point[0].x,point[0].y,point[1].x,point[1].y);
01894             for (i=0; i < 3; i++)
01895               last[i]=point[i];
01896           }
01897         (void) ConcatenateString(&path,message);
01898         in_subpath=MagickTrue;
01899         knot_count--;
01900         /*
01901           Close the subpath if there are no more knots.
01902         */
01903         if (knot_count == 0)
01904           {
01905             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01906                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
01907               (void) FormatMagickString(message,MaxTextExtent,"L %g,%g Z\n",
01908                 first[1].x,first[1].y);
01909             else
01910               {
01911                 (void) FormatMagickString(message,MaxTextExtent,
01912                   "C %g,%g %g,%g %g,%g Z\n",last[2].x,last[2].y,
01913                   first[0].x,first[0].y,first[1].x,first[1].y);
01914                 (void) ConcatenateString(&path,message);
01915               }
01916             in_subpath=MagickFalse;
01917           }
01918         break;
01919       }
01920       case 6:
01921       case 7:
01922       case 8:
01923       default:
01924       {
01925         blob+=24;
01926         length-=24;
01927         break;
01928       }
01929     }
01930   }
01931   /*
01932     Return an empty SVG image if the path does not have knots.
01933   */
01934   (void) FormatMagickString(message,MaxTextExtent,"\"/>\n");
01935   (void) ConcatenateString(&path,message);
01936   (void) FormatMagickString(message,MaxTextExtent,"</g>\n");
01937   (void) ConcatenateString(&path,message);
01938   (void) FormatMagickString(message,MaxTextExtent,"</svg>\n");
01939   (void) ConcatenateString(&path,message);
01940   message=DestroyString(message);
01941   return(path);
01942 }
01943 
01944 MagickExport const char *GetImageProperty(const Image *image,
01945   const char *property)
01946 {
01947   ExceptionInfo
01948     *exception;
01949 
01950   FxInfo
01951     *fx_info;
01952 
01953   MagickRealType
01954     alpha;
01955 
01956   MagickStatusType
01957     status;
01958 
01959   register const char
01960     *p;
01961 
01962   assert(image != (Image *) NULL);
01963   assert(image->signature == MagickSignature);
01964   if (image->debug != MagickFalse)
01965     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01966   p=(const char *) NULL;
01967   if (property == (const char *) NULL)
01968     {
01969       ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
01970       p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
01971         image->properties);
01972       return(p);
01973     }
01974   if ((image->properties != (void *) NULL) &&
01975       (LocaleNCompare("fx:",property,3) != 0))
01976     {
01977       p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
01978         image->properties,property);
01979       if (p != (const char *) NULL)
01980         return(p);
01981     }
01982   if (strchr(property,':') == (char *) NULL)
01983     return(p);
01984   exception=(&((Image *) image)->exception);
01985   switch (*property)
01986   {
01987     case '8':
01988     {
01989       if (LocaleNCompare("8bim:",property,5) == 0)
01990         {
01991           if (Get8BIMProperty(image,property) != MagickFalse)
01992             {
01993               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
01994                 image->properties,property);
01995               return(p);
01996             }
01997         }
01998       break;
01999     }
02000     case 'E':
02001     case 'e':
02002     {
02003       if (LocaleNCompare("exif:",property,5) == 0)
02004         {
02005           if (GetEXIFProperty(image,property) != MagickFalse)
02006             {
02007               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02008                 image->properties,property);
02009               return(p);
02010             }
02011         }
02012       break;
02013     }
02014     case 'F':
02015     case 'f':
02016     {
02017       if (LocaleNCompare("fx:",property,3) == 0)
02018         {
02019           fx_info=AcquireFxInfo(image,property+3);
02020           status=FxEvaluateExpression(fx_info,&alpha,exception);
02021           fx_info=DestroyFxInfo(fx_info);
02022           if (status != MagickFalse)
02023             {
02024               char
02025                 value[MaxTextExtent];
02026 
02027               (void) FormatMagickString(value,MaxTextExtent,"%g",(double)
02028                 alpha);
02029               (void) SetImageProperty((Image *) image,property,value);
02030             }
02031           p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02032             image->properties,property);
02033           return(p);
02034         }
02035       break;
02036     }
02037     case 'I':
02038     case 'i':
02039     {
02040       if (LocaleNCompare("iptc:",property,5) == 0)
02041         {
02042           if (GetIPTCProperty(image,property) != MagickFalse)
02043             {
02044               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02045                 image->properties,property);
02046               return(p);
02047             }
02048         }
02049       break;
02050     }
02051     case 'P':
02052     case 'p':
02053     {
02054       if (LocaleNCompare("pixel:",property,6) == 0)
02055         {
02056           MagickPixelPacket
02057             pixel;
02058 
02059           GetMagickPixelPacket(image,&pixel);
02060           fx_info=AcquireFxInfo(image,property+6);
02061           status=FxEvaluateChannelExpression(fx_info,RedChannel,0,0,&alpha,
02062             exception);
02063           pixel.red=(MagickRealType) QuantumRange*alpha;
02064           status|=FxEvaluateChannelExpression(fx_info,GreenChannel,0,0,&alpha,
02065             exception);
02066           pixel.green=(MagickRealType) QuantumRange*alpha;
02067           status|=FxEvaluateChannelExpression(fx_info,BlueChannel,0,0,&alpha,
02068             exception);
02069           pixel.blue=(MagickRealType) QuantumRange*alpha;
02070           status|=FxEvaluateChannelExpression(fx_info,OpacityChannel,0,0,&alpha,
02071             exception);
02072           pixel.opacity=(MagickRealType) QuantumRange*(1.0-alpha);
02073           if (image->colorspace == CMYKColorspace)
02074             {
02075               status|=FxEvaluateChannelExpression(fx_info,BlackChannel,0,0,
02076                 &alpha,exception);
02077               pixel.index=(MagickRealType) QuantumRange*alpha;
02078             }
02079           fx_info=DestroyFxInfo(fx_info);
02080           if (status != MagickFalse)
02081             {
02082               char
02083                 name[MaxTextExtent];
02084 
02085               (void) QueryMagickColorname(image,&pixel,SVGCompliance,name,
02086                 exception);
02087               (void) SetImageProperty((Image *) image,property,name);
02088               return(GetImageProperty(image,property));
02089             }
02090         }
02091       break;
02092     }
02093     case 'X':
02094     case 'x':
02095     {
02096       if (LocaleNCompare("xmp:",property,4) == 0)
02097         {
02098           if (GetXMPProperty(image,property) != MagickFalse)
02099             {
02100               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02101                 image->properties,property);
02102               return(p);
02103             }
02104         }
02105       break;
02106     }
02107   }
02108   return(p);
02109 }
02110 
02111 /*
02112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02113 %                                                                             %
02114 %                                                                             %
02115 %                                                                             %
02116 +   G e t M a g i c k P r o p e r t y                                         %
02117 %                                                                             %
02118 %                                                                             %
02119 %                                                                             %
02120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02121 %
02122 %  GetMagickProperty() gets a value associated with an image property.
02123 %
02124 %  The format of the GetMagickProperty method is:
02125 %
02126 %      const char *GetMagickProperty(const ImageInfo *image_info,
02127 %        Image *image,const char *key)
02128 %
02129 %  A description of each parameter follows:
02130 %
02131 %    o image_info: the image info.
02132 %
02133 %    o image: the image.
02134 %
02135 %    o key: the key.
02136 %
02137 */
02138 MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
02139   Image *image,const char *property)
02140 {
02141   char
02142     value[MaxTextExtent],
02143     filename[MaxTextExtent];
02144 
02145   *value='\0';
02146   switch (*(property))
02147   {
02148     case 'b':
02149     {
02150       if (LocaleNCompare("base",property,4) == 0)
02151         {
02152           GetPathComponent(image->magick_filename,BasePath,filename);
02153           (void) CopyMagickString(value,filename,MaxTextExtent);
02154           break;
02155         }
02156       break;
02157     }
02158     case 'c':
02159     {
02160       if (LocaleNCompare("channels",property,8) == 0)
02161         {
02162           /*
02163             Image channels.
02164           */
02165           (void) FormatMagickString(value,MaxTextExtent,"%s",
02166             MagickOptionToMnemonic(MagickColorspaceOptions,(long)
02167             image->colorspace));
02168           LocaleLower(value);
02169           if (image->matte != MagickFalse)
02170             (void) ConcatenateMagickString(value,"a",MaxTextExtent);
02171           break;
02172         }
02173       if (LocaleNCompare("colorspace",property,10) == 0)
02174         {
02175           ColorspaceType
02176             colorspace;
02177 
02178           /*
02179             Image storage class and colorspace.
02180           */
02181           colorspace=image->colorspace;
02182           if (IsGrayImage(image,&image->exception) != MagickFalse)
02183             colorspace=GRAYColorspace;
02184           (void) FormatMagickString(value,MaxTextExtent,"%s",
02185             MagickOptionToMnemonic(MagickColorspaceOptions,(long) colorspace));
02186           break;
02187         }
02188       break;
02189     }
02190     case 'd':
02191     {
02192       if (LocaleNCompare("depth",property,5) == 0)
02193         {
02194           (void) FormatMagickString(value,MaxTextExtent,"%lu",image->depth);
02195           break;
02196         }
02197       if (LocaleNCompare("directory",property,9) == 0)
02198         {
02199           GetPathComponent(image->magick_filename,HeadPath,filename);
02200           (void) CopyMagickString(value,filename,MaxTextExtent);
02201           break;
02202         }
02203       break;
02204     }
02205     case 'e':
02206     {
02207       if (LocaleNCompare("extension",property,9) == 0)
02208         {
02209           GetPathComponent(image->magick_filename,ExtensionPath,filename);
02210           (void) CopyMagickString(value,filename,MaxTextExtent);
02211           break;
02212         }
02213       break;
02214     }
02215     case 'g':
02216     {
02217       if (LocaleNCompare("group",property,5) == 0)
02218         {
02219           (void) FormatMagickString(value,MaxTextExtent,"0x%lx",
02220             image_info->group);
02221           break;
02222         }
02223       break;
02224     }
02225     case 'h':
02226     {
02227       if (LocaleNCompare("height",property,6) == 0)
02228         {
02229           (void) FormatMagickString(value,MaxTextExtent,"%lu",
02230             image->magick_rows != 0 ? image->magick_rows : 256UL);
02231           break;
02232         }
02233       break;
02234     }
02235     case 'i':
02236     {
02237       if (LocaleNCompare("input",property,5) == 0)
02238         {
02239           (void) CopyMagickString(value,image->filename,MaxTextExtent);
02240           break;
02241         }
02242       break;
02243     }
02244     case 'k':
02245     {
02246       if (LocaleNCompare("kurtosis",property,8) == 0)
02247         {
02248           double
02249             kurtosis,
02250             skewness;
02251 
02252           (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
02253             &skewness,&image->exception);
02254           (void) FormatMagickString(value,MaxTextExtent,"%g",kurtosis);
02255           break;
02256         }
02257       break;
02258     }
02259     case 'm':
02260     {
02261       if (LocaleNCompare("magick",property,6) == 0)
02262         {
02263           (void) CopyMagickString(value,image->magick,MaxTextExtent);
02264           break;
02265         }
02266       if (LocaleNCompare("max",property,3) == 0)
02267         {
02268           double
02269             maximum,
02270             minimum;
02271 
02272           (void) GetImageChannelRange(image,image_info->channel,&minimum,
02273             &maximum,&image->exception);
02274           (void) FormatMagickString(value,MaxTextExtent,"%g",maximum);
02275           break;
02276         }
02277       if (LocaleNCompare("mean",property,4) == 0)
02278         {
02279           double
02280             mean,
02281             standard_deviation;
02282 
02283           (void) GetImageChannelMean(image,image_info->channel,&mean,
02284             &standard_deviation,&image->exception);
02285           (void) FormatMagickString(value,MaxTextExtent,"%g",mean);
02286           break;
02287         }
02288       if (LocaleNCompare("min",property,3) == 0)
02289         {
02290           double
02291             maximum,
02292             minimum;
02293 
02294           (void) GetImageChannelRange(image,image_info->channel,&minimum,
02295             &maximum,&image->exception);
02296           (void) FormatMagickString(value,MaxTextExtent,"%g",minimum);
02297           break;
02298         }
02299       break;
02300     }
02301     case 'n':
02302     {
02303       if (LocaleNCompare("name",property,4) == 0)
02304         {
02305           (void) CopyMagickString(value,filename,MaxTextExtent);
02306           break;
02307         }
02308      break;
02309     }
02310     case 'o':
02311     {
02312       if (LocaleNCompare("output",property,6) == 0)
02313         {
02314           (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
02315           break;
02316         }
02317      break;
02318     }
02319     case 'p':
02320     {
02321       if (LocaleNCompare("page",property,4) == 0)
02322         {
02323           register const Image
02324             *p;
02325 
02326           unsigned long
02327             page;
02328 
02329           p=image;
02330           for (page=1; GetPreviousImageInList(p) != (Image *) NULL; page++)
02331             p=GetPreviousImageInList(p);
02332           (void) FormatMagickString(value,MaxTextExtent,"%lu",page);
02333           break;
02334         }
02335       break;
02336     }
02337     case 's':
02338     {
02339       if (LocaleNCompare("size",property,4) == 0)
02340         {
02341           char
02342             format[MaxTextExtent];
02343 
02344           (void) FormatMagickSize(GetBlobSize(image),format);
02345           (void) FormatMagickString(value,MaxTextExtent,"%s",format);
02346           break;
02347         }
02348       if (LocaleNCompare("scenes",property,6) == 0)
02349         {
02350           (void) FormatMagickString(value,MaxTextExtent,"%lu",
02351             (unsigned long) GetImageListLength(image));
02352           break;
02353         }
02354       if (LocaleNCompare("scene",property,5) == 0)
02355         {
02356           (void) FormatMagickString(value,MaxTextExtent,"%lu",image->scene);
02357           if (image_info->number_scenes != 0)
02358             (void) FormatMagickString(value,MaxTextExtent,"%lu",
02359               image_info->scene);
02360           break;
02361         }
02362       if (LocaleNCompare("skewness",property,8) == 0)
02363         {
02364           double
02365             kurtosis,
02366             skewness;
02367 
02368           (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
02369             &skewness,&image->exception);
02370           (void) FormatMagickString(value,MaxTextExtent,"%g",skewness);
02371           break;
02372         }
02373       if ((LocaleNCompare("standard-deviation",property,18) == 0) ||
02374           (LocaleNCompare("standard_deviation",property,18) == 0))
02375         {
02376           double
02377             mean,
02378             standard_deviation;
02379 
02380           (void) GetImageChannelMean(image,image_info->channel,&mean,
02381             &standard_deviation,&image->exception);
02382           (void) FormatMagickString(value,MaxTextExtent,"%g",
02383             standard_deviation);
02384           break;
02385         }
02386        break;
02387     }
02388     case 'u':
02389     {
02390       if (LocaleNCompare("unique",property,6) == 0)
02391         {
02392           (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
02393           (void) CopyMagickString(value,filename,MaxTextExtent);
02394           break;
02395         }
02396       break;
02397     }
02398     case 'w':
02399     {
02400       if (LocaleNCompare("width",property,5) == 0)
02401         {
02402           (void) FormatMagickString(value,MaxTextExtent,"%lu",
02403             image->magick_columns != 0 ? image->magick_columns : 256UL);
02404           break;
02405         }
02406       break;
02407     }
02408     case 'x':
02409     {
02410       if (LocaleNCompare("xresolution",property,11) == 0)
02411         {
02412           (void) FormatMagickString(value,MaxTextExtent,"%g",
02413             image->x_resolution);
02414           break;
02415         }
02416       break;
02417     }
02418     case 'y':
02419     {
02420       if (LocaleNCompare("yresolution",property,11) == 0)
02421         {
02422           (void) FormatMagickString(value,MaxTextExtent,"%g",
02423             image->y_resolution);
02424           break;
02425         }
02426       break;
02427     }
02428     case 'z':
02429     {
02430       if (LocaleNCompare("zero",property,4) == 0)
02431         {
02432           (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
02433           (void) CopyMagickString(value,filename,MaxTextExtent);
02434           break;
02435         }
02436       break;
02437     }
02438   }
02439   if (*value != '\0')
02440    {
02441      if (image->properties == (void *) NULL)
02442        image->properties=NewSplayTree(CompareSplayTreeString,
02443          RelinquishMagickMemory,RelinquishMagickMemory);
02444      (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
02445        ConstantString(property),ConstantString(value));
02446    }
02447   return(GetImageProperty(image,property));
02448 }
02449 
02450 /*
02451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02452 %                                                                             %
02453 %                                                                             %
02454 %                                                                             %
02455 %   G e t N e x t I m a g e P r o p e r t y                                   %
02456 %                                                                             %
02457 %                                                                             %
02458 %                                                                             %
02459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02460 %
02461 %  GetNextImageProperty() gets the next image property value.
02462 %
02463 %  The format of the GetNextImageProperty method is:
02464 %
02465 %      char *GetNextImageProperty(const Image *image)
02466 %
02467 %  A description of each parameter follows:
02468 %
02469 %    o image: the image.
02470 %
02471 */
02472 MagickExport char *GetNextImageProperty(const Image *image)
02473 {
02474   assert(image != (Image *) NULL);
02475   assert(image->signature == MagickSignature);
02476   if (image->debug != MagickFalse)
02477     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
02478       image->filename);
02479   if (image->properties == (void *) NULL)
02480     return((char *) NULL);
02481   return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
02482 }
02483 
02484 /*
02485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02486 %                                                                             %
02487 %                                                                             %
02488 %                                                                             %
02489 %   I n t e r p r e t I m a g e P r o p e r t i e s                           %
02490 %                                                                             %
02491 %                                                                             %
02492 %                                                                             %
02493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02494 %
02495 %  InterpretImageProperties() replaces any embedded formatting characters with
02496 %  the appropriate image property and returns the interpretted text.
02497 %
02498 %  The format of the InterpretImageProperties method is:
02499 %
02500 %      char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
02501 %        const char *embed_text)
02502 %
02503 %  A description of each parameter follows:
02504 %
02505 %    o image_info: the image info.
02506 %
02507 %    o image: the image.
02508 %
02509 %    o embed_text: the address of a character string containing the embedded
02510 %      formatting characters.
02511 %
02512 */
02513 MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
02514   Image *image,const char *embed_text)
02515 {
02516   char
02517     filename[MaxTextExtent],
02518     *interpret_text,
02519     *text;
02520 
02521   const char
02522     *value;
02523 
02524   ImageInfo
02525     *text_info;
02526 
02527   register char
02528     *q;
02529 
02530   register const char
02531     *p;
02532 
02533   register long
02534     i;
02535 
02536   size_t
02537     extent,
02538     length;
02539 
02540   assert(image != (Image *) NULL);
02541   assert(image->signature == MagickSignature);
02542   if (image->debug != MagickFalse)
02543     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02544   if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
02545     return((char *) NULL);
02546   text=(char *) embed_text;
02547   if ((*text == '@') && ((*(text+1) == '-') ||
02548       (IsPathAccessible(text+1) != MagickFalse)))
02549     return(FileToString(embed_text+1,~0,&image->exception));
02550   /*
02551     Translate any embedded format characters.
02552   */
02553   text_info=CloneImageInfo(image_info);
02554   interpret_text=AcquireString(text);
02555   extent=MaxTextExtent;
02556   p=text;
02557   for (q=interpret_text; *p != '\0'; p++)
02558   {
02559     *q='\0';
02560     if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
02561       {
02562         extent+=MaxTextExtent;
02563         interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
02564           MaxTextExtent+1,sizeof(*interpret_text));
02565         if (interpret_text == (char *) NULL)
02566           break;
02567         q=interpret_text+strlen(interpret_text);
02568       }
02569     /*
02570       Process formatting characters in text.
02571     */
02572     if ((*p == '\\') && (*(p+1) == 'r'))
02573       {
02574         *q++='\r';
02575         p++;
02576         continue;
02577       }
02578     if ((*p == '\\') && (*(p+1) == 'n'))
02579       {
02580         *q++='\n';
02581         p++;
02582         continue;
02583       }
02584     if (*p == '\\')
02585       {
02586         p++;
02587         *q++=(*p);
02588         continue;
02589       }
02590     if (*p != '%')
02591       {
02592         *q++=(*p);
02593         continue;
02594       }
02595     p++;
02596     switch (*p)
02597     {
02598       case 'b':
02599       {
02600         char
02601           format[MaxTextExtent];
02602 
02603         MagickSizeType
02604           length;
02605 
02606         /*
02607           File size.
02608         */
02609         length=GetBlobSize(image);
02610         (void) FormatMagickString(format,MaxTextExtent,"%lu",(unsigned long)
02611           length);
02612         if (length != (MagickSizeType) ((size_t) length))
02613           (void) FormatMagickSize(length,format);
02614         q+=ConcatenateMagickString(q,format,extent);
02615         break;
02616       }
02617       case 'c':
02618       {
02619         /*
02620           Image comment.
02621         */
02622         value=GetImageProperty(image,"comment");
02623         if (value == (const char *) NULL)
02624           break;
02625         length=strlen(value);
02626         if ((size_t) (q-interpret_text+length+1) >= extent)
02627           {
02628             extent+=length;
02629             interpret_text=(char *) ResizeQuantumMemory(interpret_text,
02630               extent+MaxTextExtent,sizeof(*interpret_text));
02631             if (interpret_text == (char *) NULL)
02632               break;
02633             q=interpret_text+strlen(interpret_text);
02634           }
02635         (void) CopyMagickString(q,value,extent);
02636         q+=length;
02637         break;
02638       }
02639       case 'd':
02640       case 'e':
02641       case 'f':
02642       case 't':
02643       {
02644         /*
02645           Label segment is the base of the filename.
02646         */
02647         if (*image->magick_filename == '\0')
02648           break;
02649         switch (*p)
02650         {
02651           case 'd':
02652           {
02653             /*
02654               Directory.
02655             */
02656             GetPathComponent(image->magick_filename,HeadPath,filename);
02657             q+=CopyMagickString(q,filename,extent);
02658             break;
02659           }
02660           case 'e':
02661           {
02662             /*
02663               Filename extension.
02664             */
02665             GetPathComponent(image->magick_filename,ExtensionPath,filename);
02666             q+=CopyMagickString(q,filename,extent);
02667             break;
02668           }
02669           case 'f':
02670           {
02671             /*
02672               Filename.
02673             */
02674             GetPathComponent(image->magick_filename,TailPath,filename);
02675             q+=CopyMagickString(q,filename,extent);
02676             break;
02677           }
02678           case 't':
02679           {
02680             /*
02681               Base filename.
02682             */
02683             GetPathComponent(image->magick_filename,BasePath,filename);
02684             q+=CopyMagickString(q,filename,extent);
02685             break;
02686           }
02687         }
02688         break;
02689       }
02690       case 'g':
02691       {
02692         /*
02693           Image geometry.
02694         */
02695         q+=FormatMagickString(q,extent,"%lux%lu%+ld%+ld",image->page.width,
02696           image->page.height,image->page.x,image->page.y);
02697         break;
02698       }
02699       case 'h':
02700       {
02701         /*
02702           Image height.
02703         */
02704         q+=FormatMagickString(q,extent,"%lu",image->rows != 0 ? image->rows :
02705           image->magick_rows);
02706         break;
02707       }
02708       case 'i':
02709       {
02710         /*
02711           Image filename.
02712         */
02713         q+=CopyMagickString(q,image->filename,extent);
02714         break;
02715       }
02716       case 'k':
02717       {
02718         /*
02719           Number of unique colors.
02720         */
02721         q+=FormatMagickString(q,extent,"%lu",GetNumberColors(image,
02722           (FILE *) NULL,&image->exception));
02723         break;
02724       }
02725       case 'l':
02726       {
02727         /*
02728           Image label.
02729         */
02730         value=GetImageProperty(image,"label");
02731         if (value == (const char *) NULL)
02732           break;
02733         length=strlen(value);
02734         if ((size_t) (q-interpret_text+length+1) >= extent)
02735           {
02736             extent+=length;
02737             interpret_text=(char *) ResizeQuantumMemory(interpret_text,
02738               extent+MaxTextExtent,sizeof(*interpret_text));
02739             if (interpret_text == (char *) NULL)
02740               break;
02741             q=interpret_text+strlen(interpret_text);
02742           }
02743         q+=CopyMagickString(q,value,extent);
02744         break;
02745       }
02746       case 'm':
02747       {
02748         /*
02749           Image format.
02750         */
02751         q+=CopyMagickString(q,image->magick,extent);
02752         break;
02753       }
02754       case 'M':
02755       {
02756         /*
02757           Image magick filename.
02758         */
02759         q+=CopyMagickString(q,image->magick_filename,extent);
02760         break;
02761       }
02762       case 'n':
02763       {
02764         /*
02765           Number of images in the list.
02766         */
02767         q+=FormatMagickString(q,extent,"%lu",(unsigned long)
02768           GetImageListLength(image));
02769         break;
02770       }
02771       case 'o':
02772       {
02773         /*
02774           Image output filename.
02775         */
02776         q+=CopyMagickString(q,text_info->filename,extent);
02777         break;
02778       }
02779       case 'p':
02780       {
02781         register const Image
02782           *p;
02783 
02784         unsigned long
02785           page;
02786 
02787         /*
02788           Image page number.
02789         */
02790         p=image;
02791         for (page=1; GetPreviousImageInList(p) != (Image *) NULL; page++)
02792           p=GetPreviousImageInList(p);
02793         q+=FormatMagickString(q,extent,"%lu",page);
02794         break;
02795       }
02796       case 'q':
02797       {
02798         /*
02799           Image depth.
02800         */
02801         q+=FormatMagickString(q,extent,"%lu",image->depth);
02802         break;
02803       }
02804       case 'r':
02805       {
02806         ColorspaceType
02807           colorspace;
02808 
02809         /*
02810           Image storage class and colorspace.
02811         */
02812         colorspace=image->colorspace;
02813         if (IsGrayImage(image,&image->exception) != MagickFalse)
02814           colorspace=GRAYColorspace;
02815         q+=FormatMagickString(q,extent,"%s%s%s",MagickOptionToMnemonic(
02816           MagickClassOptions,(long) image->storage_class),
02817           MagickOptionToMnemonic(MagickColorspaceOptions,(long) colorspace),
02818           image->matte != MagickFalse ? "Matte" : "");
02819         break;
02820       }
02821       case 's':
02822       {
02823         /*
02824           Image scene number.
02825         */
02826         if (text_info->number_scenes == 0)
02827           q+=FormatMagickString(q,extent,"%lu",image->scene);
02828         else
02829           q+=FormatMagickString(q,extent,"%lu",text_info->scene);
02830         break;
02831       }
02832       case 'u':
02833       {
02834         /*
02835           Unique filename.
02836         */
02837         (void) CopyMagickString(filename,text_info->unique,extent);
02838         q+=CopyMagickString(q,filename,extent);
02839         break;
02840       }
02841       case 'w':
02842       {
02843         /*
02844           Image width.
02845         */
02846         q+=FormatMagickString(q,extent,"%lu",image->columns != 0 ?
02847           image->columns : image->magick_columns);
02848         break;
02849       }
02850       case 'x':
02851       {
02852         /*
02853           Image horizontal resolution.
02854         */
02855         q+=FormatMagickString(q,extent,"%g %s",image->x_resolution,
02856           MagickOptionToMnemonic(MagickResolutionOptions,(long) image->units));
02857         break;
02858       }
02859       case 'y':
02860       {
02861         /*
02862           Image vertical resolution.
02863         */
02864         q+=FormatMagickString(q,extent,"%g %s",image->y_resolution,
02865           MagickOptionToMnemonic(MagickResolutionOptions,(long) image->units));
02866         break;
02867       }
02868       case 'z':
02869       {
02870         /*
02871           Image depth.
02872         */
02873         q+=FormatMagickString(q,extent,"%lu",image->depth);
02874         break;
02875       }
02876       case 'A':
02877       {
02878         /*
02879           Image alpha channel.
02880         */
02881         q+=FormatMagickString(q,extent,"%s",MagickOptionToMnemonic(
02882           MagickBooleanOptions,(long) image->matte));
02883         break;
02884       }
02885       case 'C':
02886       {
02887         /*
02888           Image compression method.
02889         */
02890         q+=FormatMagickString(q,extent,"%s",MagickOptionToMnemonic(
02891           MagickCompressOptions,(long) image->compression));
02892         break;
02893       }
02894       case 'D':
02895       {
02896         /*
02897           Image dispose method.
02898         */
02899         q+=FormatMagickString(q,extent,"%s",MagickOptionToMnemonic(
02900           MagickDisposeOptions,(long) image->dispose));
02901         break;
02902       }
02903       case 'G':
02904       {
02905         q+=FormatMagickString(q,extent,"%lux%lu",image->magick_columns,
02906           image->magick_rows);
02907         break;
02908       }
02909       case 'H':
02910       {
02911         q+=FormatMagickString(q,extent,"%ld",image->page.height);
02912         break;
02913       }
02914       case 'O':
02915       {
02916         q+=FormatMagickString(q,extent,"%+ld%+ld",image->page.x,image->page.y);
02917         break;
02918       }
02919       case 'P':
02920       {
02921         q+=FormatMagickString(q,extent,"%lux%lu",image->page.width,
02922           image->page.height);
02923         break;
02924       }
02925       case 'Q':
02926       {
02927         q+=FormatMagickString(q,extent,"%lu",image->quality);
02928         break;
02929       }
02930       case 'S':
02931       {
02932         /*
02933           Image scenes.
02934         */
02935         if (text_info->number_scenes == 0)
02936           q+=CopyMagickString(q,"2147483647",extent);
02937         else
02938           q+=FormatMagickString(q,extent,"%lu",text_info->scene+
02939             text_info->number_scenes);
02940         break;
02941       }
02942       case 'T':
02943       {
02944         q+=FormatMagickString(q,extent,"%lu",image->delay);
02945         break;
02946       }
02947       case 'W':
02948       {
02949         q+=FormatMagickString(q,extent,"%ld",image->page.width);
02950         break;
02951       }
02952       case 'X':
02953       {
02954         q+=FormatMagickString(q,extent,"%+ld",image->page.x);
02955         break;
02956       }
02957       case 'Y':
02958       {
02959         q+=FormatMagickString(q,extent,"%+ld",image->page.y);
02960         break;
02961       }
02962       case 'Z':
02963       {
02964         /*
02965           Unique filename.
02966         */
02967         (void) CopyMagickString(filename,text_info->zero,extent);
02968         q+=CopyMagickString(q,filename,extent);
02969         break;
02970       }
02971       case '[':
02972       {
02973         char
02974           pattern[MaxTextExtent];
02975 
02976         const char
02977           *key,
02978           *value;
02979 
02980         long
02981           depth;
02982 
02983         /*
02984           Image value.
02985         */
02986         if (strchr(p,']') == (char *) NULL)
02987           break;
02988         depth=1;
02989         p++;
02990         for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
02991         {
02992           if (*p == '[')
02993             depth++;
02994           if (*p == ']')
02995             depth--;
02996           if (depth <= 0)
02997             break;
02998           pattern[i]=(*p++);
02999         }
03000         pattern[i]='\0';
03001         value=GetImageProperty(image,pattern);
03002         if (value != (const char *) NULL)
03003           {
03004             length=strlen(value);
03005             if ((size_t) (q-interpret_text+length+1) >= extent)
03006               {
03007                 extent+=length;
03008                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
03009                   extent+MaxTextExtent,sizeof(*interpret_text));
03010                 if (interpret_text == (char *) NULL)
03011                   break;
03012                 q=interpret_text+strlen(interpret_text);
03013               }
03014             (void) CopyMagickString(q,value,extent);
03015             q+=length;
03016             break;
03017           }
03018         else
03019           if (IsGlob(pattern) != MagickFalse)
03020             {
03021               /*
03022                 Iterate over image properties.
03023               */
03024               ResetImagePropertyIterator(image);
03025               key=GetNextImageProperty(image);
03026               while (key != (const char *) NULL)
03027               {
03028                 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
03029                   {
03030                     value=GetImageProperty(image,key);
03031                     if (value != (const char *) NULL)
03032                       {
03033                         length=strlen(key)+strlen(value)+2;
03034                         if ((size_t) (q-interpret_text+length+1) >= extent)
03035                           {
03036                             extent+=length;
03037                             interpret_text=(char *) ResizeQuantumMemory(
03038                               interpret_text,extent+MaxTextExtent,
03039                               sizeof(*interpret_text));
03040                             if (interpret_text == (char *) NULL)
03041                               break;
03042                             q=interpret_text+strlen(interpret_text);
03043                           }
03044                         q+=FormatMagickString(q,extent,"%s=%s\n",key,value);
03045                       }
03046                   }
03047                 key=GetNextImageProperty(image);
03048               }
03049             }
03050         value=GetMagickProperty(text_info,image,pattern);
03051         if (value != (const char *) NULL)
03052           {
03053             length=strlen(value);
03054             if ((size_t) (q-interpret_text+length+1) >= extent)
03055               {
03056                 extent+=length;
03057                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
03058                   extent+MaxTextExtent,sizeof(*interpret_text));
03059                 if (interpret_text == (char *) NULL)
03060                   break;
03061                 q=interpret_text+strlen(interpret_text);
03062               }
03063             (void) CopyMagickString(q,value,extent);
03064             q+=length;
03065           }
03066         if (image_info == (ImageInfo *) NULL)
03067           break;
03068         value=GetImageOption(image_info,pattern);
03069         if (value != (char *) NULL)
03070           {
03071             length=strlen(value);
03072             if ((size_t) (q-interpret_text+length+1) >= extent)
03073               {
03074                 extent+=length;
03075                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
03076                   extent+MaxTextExtent,sizeof(*interpret_text));
03077                 if (interpret_text == (char *) NULL)
03078                   break;
03079                 q=interpret_text+strlen(interpret_text);
03080               }
03081             (void) CopyMagickString(q,value,extent);
03082             q+=length;
03083           }
03084         break;
03085       }
03086       case '@':
03087       {
03088         RectangleInfo
03089           page;
03090 
03091         /*
03092           Image bounding box.
03093         */
03094         page=GetImageBoundingBox(image,&image->exception);
03095         q+=FormatMagickString(q,MaxTextExtent,"%lux%lu%+ld%+ld",page.width,
03096           page.height,page.x,page.y);
03097         break;
03098       }
03099       case '#':
03100       {
03101         /*
03102           Image signature.
03103         */
03104         (void) SignatureImage(image);
03105         value=GetImageProperty(image,"signature");
03106         if (value == (const char *) NULL)
03107           break;
03108         q+=CopyMagickString(q,value,extent);
03109         break;
03110       }
03111       case '%':
03112       {
03113         *q++=(*p);
03114         break;
03115       }
03116       default:
03117       {
03118         *q++='%';
03119         *q++=(*p);
03120         break;
03121       }
03122     }
03123   }
03124   *q='\0';
03125   text_info=DestroyImageInfo(text_info);
03126   if (text != (const char *) embed_text)
03127     text=DestroyString(text);
03128   (void) SubstituteString(&interpret_text,"&lt;","<");
03129   (void) SubstituteString(&interpret_text,"&gt;",">");
03130   return(interpret_text);
03131 }
03132 
03133 /*
03134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03135 %                                                                             %
03136 %                                                                             %
03137 %                                                                             %
03138 %   R e m o v e I m a g e P r o p e r t y                                     %
03139 %                                                                             %
03140 %                                                                             %
03141 %                                                                             %
03142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03143 %
03144 %  RemoveImageProperty() removes a property from the image and returns its
03145 %  value.
03146 %
03147 %  The format of the RemoveImageProperty method is:
03148 %
03149 %      char *RemoveImageProperty(Image *image,const char *property)
03150 %
03151 %  A description of each parameter follows:
03152 %
03153 %    o image: the image.
03154 %
03155 %    o property: the image property.
03156 %
03157 */
03158 MagickExport char *RemoveImageProperty(Image *image,
03159   const char *property)
03160 {
03161   char
03162     *value;
03163 
03164   assert(image != (Image *) NULL);
03165   assert(image->signature == MagickSignature);
03166   if (image->debug != MagickFalse)
03167     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
03168       image->filename);
03169   if (image->properties == (void *) NULL)
03170     return((char *) NULL);
03171   value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
03172     property);
03173   return(value);
03174 }
03175 
03176 /*
03177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03178 %                                                                             %
03179 %                                                                             %
03180 %                                                                             %
03181 %   R e s e t I m a g e P r o p e r t y I t e r a t o r                       %
03182 %                                                                             %
03183 %                                                                             %
03184 %                                                                             %
03185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03186 %
03187 %  ResetImagePropertyIterator() resets the image properties iterator.  Use it
03188 %  in conjunction with GetNextImageProperty() to iterate over all the values
03189 %  associated with an image property.
03190 %
03191 %  The format of the ResetImagePropertyIterator method is:
03192 %
03193 %      ResetImagePropertyIterator(Image *image)
03194 %
03195 %  A description of each parameter follows:
03196 %
03197 %    o image: the image.
03198 %
03199 */
03200 MagickExport void ResetImagePropertyIterator(const Image *image)
03201 {
03202   assert(image != (Image *) NULL);
03203   assert(image->signature == MagickSignature);
03204   if (image->debug != MagickFalse)
03205     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
03206       image->filename);
03207   if (image->properties == (void *) NULL)
03208     return;
03209   ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
03210 }
03211 
03212 /*
03213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03214 %                                                                             %
03215 %                                                                             %
03216 %                                                                             %
03217 %   S e t I m a g e P r o p e r t y                                           %
03218 %                                                                             %
03219 %                                                                             %
03220 %                                                                             %
03221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03222 %
03223 %  SetImageProperty() associates an value with an image property.
03224 %
03225 %  The format of the SetImageProperty method is:
03226 %
03227 %      MagickBooleanType SetImageProperty(Image *image,const char *property,
03228 %        const char *value)
03229 %
03230 %  A description of each parameter follows:
03231 %
03232 %    o image: the image.
03233 %
03234 %    o property: the image property.
03235 %
03236 %    o values: the image property values.
03237 %
03238 */
03239 MagickExport MagickBooleanType SetImageProperty(Image *image,
03240   const char *property,const char *value)
03241 {
03242   MagickBooleanType
03243     status;
03244 
03245   MagickStatusType
03246     flags;
03247 
03248   assert(image != (Image *) NULL);
03249   assert(image->signature == MagickSignature);
03250   if (image->debug != MagickFalse)
03251     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
03252       image->filename);
03253   if (image->properties == (void *) NULL)
03254     image->properties=NewSplayTree(CompareSplayTreeString,
03255       RelinquishMagickMemory,RelinquishMagickMemory);
03256   if ((value == (const char *) NULL) || (*value == '\0'))
03257     return(DeleteImageProperty(image,property));
03258   status=MagickTrue;
03259   switch (*property)
03260   {
03261     case 'B':
03262     case 'b':
03263     {
03264       if (LocaleCompare(property,"bias") == 0)
03265         {
03266           image->bias=StringToDouble(value,QuantumRange);
03267           break;
03268         }
03269       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03270         ConstantString(property),ConstantString(value));
03271       break;
03272     }
03273     case 'C':
03274     case 'c':
03275     {
03276       if (LocaleCompare(property,"colorspace") == 0)
03277         {
03278           long
03279             colorspace;
03280 
03281           colorspace=ParseMagickOption(MagickColorspaceOptions,MagickFalse,
03282             value);
03283           if (colorspace < 0)
03284             break;
03285           (void) SetImageColorspace(image,(ColorspaceType) colorspace);
03286           break;
03287         }
03288       if (LocaleCompare(property,"compose") == 0)
03289         {
03290           long
03291             compose;
03292 
03293           compose=ParseMagickOption(MagickComposeOptions,MagickFalse,value);
03294           if (compose < 0)
03295             break;
03296           image->compose=(CompositeOperator) compose;
03297           break;
03298         }
03299       if (LocaleCompare(property,"compress") == 0)
03300         {
03301           long
03302             compression;
03303 
03304           compression=ParseMagickOption(MagickCompressOptions,MagickFalse,
03305             value);
03306           if (compression < 0)
03307             break;
03308           image->compression=(CompressionType) compression;
03309           break;
03310         }
03311       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03312         ConstantString(property),ConstantString(value));
03313       break;
03314     }
03315     case 'D':
03316     case 'd':
03317     {
03318       if (LocaleCompare(property,"delay") == 0)
03319         {
03320           GeometryInfo
03321             geometry_info;
03322 
03323           flags=ParseGeometry(value,&geometry_info);
03324           if ((flags & GreaterValue) != 0)
03325             {
03326               if (image->delay > (unsigned long) (geometry_info.rho+0.5))
03327                 image->delay=(unsigned long) (geometry_info.rho+0.5);
03328             }
03329           else
03330             if ((flags & LessValue) != 0)
03331               {
03332                 if (image->delay < (unsigned long) (geometry_info.rho+0.5))
03333                   image->ticks_per_second=(long) (geometry_info.sigma+0.5);
03334               }
03335             else
03336               image->delay=(unsigned long) (geometry_info.rho+0.5);
03337           if ((flags & SigmaValue) != 0)
03338             image->ticks_per_second=(long) (geometry_info.sigma+0.5);
03339           break;
03340         }
03341       if (LocaleCompare(property,"depth") == 0)
03342         {
03343           image->depth=(unsigned long) atol(value);
03344           break;
03345         }
03346       if (LocaleCompare(property,"dispose") == 0)
03347         {
03348           long
03349             dispose;
03350 
03351           dispose=ParseMagickOption(MagickDisposeOptions,MagickFalse,value);
03352           if (dispose < 0)
03353             break;
03354           image->dispose=(DisposeType) dispose;
03355           break;
03356         }
03357       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03358         ConstantString(property),ConstantString(value));
03359       break;
03360     }
03361     case 'G':
03362     case 'g':
03363     {
03364       if (LocaleCompare(property,"gravity") == 0)
03365         {
03366           long
03367             gravity;
03368 
03369           gravity=ParseMagickOption(MagickGravityOptions,MagickFalse,value);
03370           if (gravity < 0)
03371             break;
03372           image->gravity=(GravityType) gravity;
03373           break;
03374         }
03375       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03376         ConstantString(property),ConstantString(value));
03377       break;
03378     }
03379     case 'I':
03380     case 'i':
03381     {
03382       if (LocaleCompare(property,"intent") == 0)
03383         {
03384           long
03385             rendering_intent;
03386 
03387           rendering_intent=ParseMagickOption(MagickIntentOptions,MagickFalse,
03388             value);
03389           if (rendering_intent < 0)
03390             break;
03391           image->rendering_intent=(RenderingIntent) rendering_intent;
03392           break;
03393         }
03394       if (LocaleCompare(property,"interpolate") == 0)
03395         {
03396           long
03397             interpolate;
03398 
03399           interpolate=ParseMagickOption(MagickInterpolateOptions,MagickFalse,
03400             value);
03401           if (interpolate < 0)
03402             break;
03403           image->interpolate=(InterpolatePixelMethod) interpolate;
03404           break;
03405         }
03406       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03407         ConstantString(property),ConstantString(value));
03408       break;
03409     }
03410     case 'L':
03411     case 'l':
03412     {
03413       if (LocaleCompare(property,"loop") == 0)
03414         {
03415           image->iterations=(unsigned long) atol(value);
03416           break;
03417         }
03418       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03419         ConstantString(property),ConstantString(value));
03420       break;
03421     }
03422     case 'P':
03423     case 'p':
03424     {
03425       if (LocaleCompare(property,"page") == 0)
03426         {
03427           char
03428             *geometry;
03429 
03430           geometry=GetPageGeometry(value);
03431           flags=ParseAbsoluteGeometry(geometry,&image->page);
03432           geometry=DestroyString(geometry);
03433           break;
03434         }
03435       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03436         ConstantString(property),ConstantString(value));
03437       break;
03438     }
03439     case 'R':
03440     case 'r':
03441     {
03442       if (LocaleCompare(property,"rendering-intent") == 0)
03443         {
03444           long
03445             rendering_intent;
03446 
03447           rendering_intent=ParseMagickOption(MagickIntentOptions,MagickFalse,
03448             value);
03449           if (rendering_intent < 0)
03450             break;
03451           image->rendering_intent=(RenderingIntent) rendering_intent;
03452           break;
03453         }
03454       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03455         ConstantString(property),ConstantString(value));
03456       break;
03457     }
03458     case 'T':
03459     case 't':
03460     {
03461       if (LocaleCompare(property,"tile-offset") == 0)
03462         {
03463           char
03464             *geometry;
03465 
03466           geometry=GetPageGeometry(value);
03467           flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
03468           geometry=DestroyString(geometry);
03469           break;
03470         }
03471       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03472         ConstantString(property),ConstantString(value));
03473       break;
03474     }
03475     default:
03476     {
03477       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03478         ConstantString(property),ConstantString(value));
03479       break;
03480     }
03481   }
03482   return(status);
03483 }

Generated on 19 Nov 2009 for MagickCore by  doxygen 1.6.1