Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %            PPPP    RRRR    OOO   PPPP   EEEEE  RRRR   TTTTT  Y   Y          %
      7 %            P   P   R   R  O   O  P   P  E      R   R    T     Y Y           %
      8 %            PPPP    RRRR   O   O  PPPP   EEE    RRRR     T      Y            %
      9 %            P       R R    O   O  P      E      R R      T      Y            %
     10 %            P       R  R    OOO   P      EEEEE  R  R     T      Y            %
     11 %                                                                             %
     12 %                                                                             %
     13 %                         MagickCore Property Methods                         %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 March 2000                                  %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 %
     38 */
     39 
     40 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/artifact.h"
     45 #include "MagickCore/attribute.h"
     46 #include "MagickCore/cache.h"
     47 #include "MagickCore/cache-private.h"
     48 #include "MagickCore/color.h"
     49 #include "MagickCore/color-private.h"
     50 #include "MagickCore/colorspace-private.h"
     51 #include "MagickCore/compare.h"
     52 #include "MagickCore/constitute.h"
     53 #include "MagickCore/draw.h"
     54 #include "MagickCore/effect.h"
     55 #include "MagickCore/exception.h"
     56 #include "MagickCore/exception-private.h"
     57 #include "MagickCore/fx.h"
     58 #include "MagickCore/fx-private.h"
     59 #include "MagickCore/gem.h"
     60 #include "MagickCore/geometry.h"
     61 #include "MagickCore/histogram.h"
     62 #include "MagickCore/image.h"
     63 #include "MagickCore/layer.h"
     64 #include "MagickCore/locale-private.h"
     65 #include "MagickCore/list.h"
     66 #include "MagickCore/magick.h"
     67 #include "MagickCore/memory_.h"
     68 #include "MagickCore/monitor.h"
     69 #include "MagickCore/montage.h"
     70 #include "MagickCore/option.h"
     71 #include "MagickCore/policy.h"
     72 #include "MagickCore/profile.h"
     73 #include "MagickCore/property.h"
     74 #include "MagickCore/quantum.h"
     75 #include "MagickCore/resource_.h"
     76 #include "MagickCore/splay-tree.h"
     77 #include "MagickCore/signature.h"
     78 #include "MagickCore/statistic.h"
     79 #include "MagickCore/string_.h"
     80 #include "MagickCore/string-private.h"
     81 #include "MagickCore/token.h"
     82 #include "MagickCore/token-private.h"
     83 #include "MagickCore/utility.h"
     84 #include "MagickCore/utility-private.h"
     85 #include "MagickCore/version.h"
     86 #include "MagickCore/xml-tree.h"
     87 #include "MagickCore/xml-tree-private.h"
     88 #if defined(MAGICKCORE_LCMS_DELEGATE)
     89 #if defined(MAGICKCORE_HAVE_LCMS2_LCMS2_H)
     90 #include <lcms2/lcms2.h>
     91 #elif defined(MAGICKCORE_HAVE_LCMS2_H)
     92 #include "lcms2.h"
     93 #elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
     94 #include <lcms/lcms.h>
     95 #else
     96 #include "lcms.h"
     97 #endif
     98 #endif
     99 
    100 /*
    102   Define declarations.
    103 */
    104 #if defined(MAGICKCORE_LCMS_DELEGATE)
    105 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
    106 #define cmsUInt32Number  DWORD
    107 #endif
    108 #endif
    109 
    110 /*
    112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    113 %                                                                             %
    114 %                                                                             %
    115 %                                                                             %
    116 %   C l o n e I m a g e P r o p e r t i e s                                   %
    117 %                                                                             %
    118 %                                                                             %
    119 %                                                                             %
    120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    121 %
    122 %  CloneImageProperties() clones all the image properties to another image.
    123 %
    124 %  The format of the CloneImageProperties method is:
    125 %
    126 %      MagickBooleanType CloneImageProperties(Image *image,
    127 %        const Image *clone_image)
    128 %
    129 %  A description of each parameter follows:
    130 %
    131 %    o image: the image.
    132 %
    133 %    o clone_image: the clone image.
    134 %
    135 */
    136 MagickExport MagickBooleanType CloneImageProperties(Image *image,
    137   const Image *clone_image)
    138 {
    139   assert(image != (Image *) NULL);
    140   assert(image->signature == MagickCoreSignature);
    141   if (image->debug != MagickFalse)
    142     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    143   assert(clone_image != (const Image *) NULL);
    144   assert(clone_image->signature == MagickCoreSignature);
    145   if (clone_image->debug != MagickFalse)
    146     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    147       clone_image->filename);
    148   (void) CopyMagickString(image->filename,clone_image->filename,
    149     MagickPathExtent);
    150   (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
    151     MagickPathExtent);
    152   image->compression=clone_image->compression;
    153   image->quality=clone_image->quality;
    154   image->depth=clone_image->depth;
    155   image->alpha_color=clone_image->alpha_color;
    156   image->background_color=clone_image->background_color;
    157   image->border_color=clone_image->border_color;
    158   image->transparent_color=clone_image->transparent_color;
    159   image->gamma=clone_image->gamma;
    160   image->chromaticity=clone_image->chromaticity;
    161   image->rendering_intent=clone_image->rendering_intent;
    162   image->black_point_compensation=clone_image->black_point_compensation;
    163   image->units=clone_image->units;
    164   image->montage=(char *) NULL;
    165   image->directory=(char *) NULL;
    166   (void) CloneString(&image->geometry,clone_image->geometry);
    167   image->offset=clone_image->offset;
    168   image->resolution.x=clone_image->resolution.x;
    169   image->resolution.y=clone_image->resolution.y;
    170   image->page=clone_image->page;
    171   image->tile_offset=clone_image->tile_offset;
    172   image->extract_info=clone_image->extract_info;
    173   image->filter=clone_image->filter;
    174   image->fuzz=clone_image->fuzz;
    175   image->intensity=clone_image->intensity;
    176   image->interlace=clone_image->interlace;
    177   image->interpolate=clone_image->interpolate;
    178   image->endian=clone_image->endian;
    179   image->gravity=clone_image->gravity;
    180   image->compose=clone_image->compose;
    181   image->orientation=clone_image->orientation;
    182   image->scene=clone_image->scene;
    183   image->dispose=clone_image->dispose;
    184   image->delay=clone_image->delay;
    185   image->ticks_per_second=clone_image->ticks_per_second;
    186   image->iterations=clone_image->iterations;
    187   image->total_colors=clone_image->total_colors;
    188   image->taint=clone_image->taint;
    189   image->progress_monitor=clone_image->progress_monitor;
    190   image->client_data=clone_image->client_data;
    191   image->start_loop=clone_image->start_loop;
    192   image->error=clone_image->error;
    193   image->signature=clone_image->signature;
    194   if (clone_image->properties != (void *) NULL)
    195     {
    196       if (image->properties != (void *) NULL)
    197         DestroyImageProperties(image);
    198       image->properties=CloneSplayTree((SplayTreeInfo *)
    199         clone_image->properties,(void *(*)(void *)) ConstantString,
    200         (void *(*)(void *)) ConstantString);
    201     }
    202   return(MagickTrue);
    203 }
    204 
    205 /*
    207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    208 %                                                                             %
    209 %                                                                             %
    210 %                                                                             %
    211 %   D e f i n e I m a g e P r o p e r t y                                     %
    212 %                                                                             %
    213 %                                                                             %
    214 %                                                                             %
    215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    216 %
    217 %  DefineImageProperty() associates an assignment string of the form
    218 %  "key=value" with an artifact or options. It is equivelent to
    219 %  SetImageProperty()
    220 %
    221 %  The format of the DefineImageProperty method is:
    222 %
    223 %      MagickBooleanType DefineImageProperty(Image *image,const char *property,
    224 %        ExceptionInfo *exception)
    225 %
    226 %  A description of each parameter follows:
    227 %
    228 %    o image: the image.
    229 %
    230 %    o property: the image property.
    231 %
    232 %    o exception: return any errors or warnings in this structure.
    233 %
    234 */
    235 MagickExport MagickBooleanType DefineImageProperty(Image *image,
    236   const char *property,ExceptionInfo *exception)
    237 {
    238   char
    239     key[MagickPathExtent],
    240     value[MagickPathExtent];
    241 
    242   register char
    243     *p;
    244 
    245   assert(image != (Image *) NULL);
    246   assert(property != (const char *) NULL);
    247   (void) CopyMagickString(key,property,MagickPathExtent-1);
    248   for (p=key; *p != '\0'; p++)
    249     if (*p == '=')
    250       break;
    251   *value='\0';
    252   if (*p == '=')
    253     (void) CopyMagickString(value,p+1,MagickPathExtent);
    254   *p='\0';
    255   return(SetImageProperty(image,key,value,exception));
    256 }
    257 
    258 /*
    260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    261 %                                                                             %
    262 %                                                                             %
    263 %                                                                             %
    264 %   D e l e t e I m a g e P r o p e r t y                                     %
    265 %                                                                             %
    266 %                                                                             %
    267 %                                                                             %
    268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    269 %
    270 %  DeleteImageProperty() deletes an image property.
    271 %
    272 %  The format of the DeleteImageProperty method is:
    273 %
    274 %      MagickBooleanType DeleteImageProperty(Image *image,const char *property)
    275 %
    276 %  A description of each parameter follows:
    277 %
    278 %    o image: the image.
    279 %
    280 %    o property: the image property.
    281 %
    282 */
    283 MagickExport MagickBooleanType DeleteImageProperty(Image *image,
    284   const char *property)
    285 {
    286   assert(image != (Image *) NULL);
    287   assert(image->signature == MagickCoreSignature);
    288   if (image->debug != MagickFalse)
    289     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    290   if (image->properties == (void *) NULL)
    291     return(MagickFalse);
    292   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
    293 }
    294 
    295 /*
    297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    298 %                                                                             %
    299 %                                                                             %
    300 %                                                                             %
    301 %   D e s t r o y I m a g e P r o p e r t i e s                               %
    302 %                                                                             %
    303 %                                                                             %
    304 %                                                                             %
    305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    306 %
    307 %  DestroyImageProperties() destroys all properties and associated memory
    308 %  attached to the given image.
    309 %
    310 %  The format of the DestroyDefines method is:
    311 %
    312 %      void DestroyImageProperties(Image *image)
    313 %
    314 %  A description of each parameter follows:
    315 %
    316 %    o image: the image.
    317 %
    318 */
    319 MagickExport void DestroyImageProperties(Image *image)
    320 {
    321   assert(image != (Image *) NULL);
    322   assert(image->signature == MagickCoreSignature);
    323   if (image->debug != MagickFalse)
    324     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    325   if (image->properties != (void *) NULL)
    326     image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
    327       image->properties);
    328 }
    329 
    330 /*
    332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    333 %                                                                             %
    334 %                                                                             %
    335 %                                                                             %
    336 %  F o r m a t I m a g e P r o p e r t y                                      %
    337 %                                                                             %
    338 %                                                                             %
    339 %                                                                             %
    340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    341 %
    342 %  FormatImageProperty() permits formatted property/value pairs to be saved as
    343 %  an image property.
    344 %
    345 %  The format of the FormatImageProperty method is:
    346 %
    347 %      MagickBooleanType FormatImageProperty(Image *image,const char *property,
    348 %        const char *format,...)
    349 %
    350 %  A description of each parameter follows.
    351 %
    352 %   o  image:  The image.
    353 %
    354 %   o  property:  The attribute property.
    355 %
    356 %   o  format:  A string describing the format to use to write the remaining
    357 %      arguments.
    358 %
    359 */
    360 MagickExport MagickBooleanType FormatImageProperty(Image *image,
    361   const char *property,const char *format,...)
    362 {
    363   char
    364     value[MagickPathExtent];
    365 
    366   ExceptionInfo
    367     *exception;
    368 
    369   MagickBooleanType
    370     status;
    371 
    372   ssize_t
    373     n;
    374 
    375   va_list
    376     operands;
    377 
    378   va_start(operands,format);
    379   n=FormatLocaleStringList(value,MagickPathExtent,format,operands);
    380   (void) n;
    381   va_end(operands);
    382   exception=AcquireExceptionInfo();
    383   status=SetImageProperty(image,property,value,exception);
    384   exception=DestroyExceptionInfo(exception);
    385   return(status);
    386 }
    387 
    388 /*
    390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    391 %                                                                             %
    392 %                                                                             %
    393 %                                                                             %
    394 %   G e t I m a g e P r o p e r t y                                           %
    395 %                                                                             %
    396 %                                                                             %
    397 %                                                                             %
    398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    399 %
    400 %  GetImageProperty() gets a value associated with an image property.
    401 %
    402 %  This includes,  profile prefixes, such as "exif:", "iptc:" and "8bim:"
    403 %  It does not handle non-prifile prefixes, such as "fx:", "option:", or
    404 %  "artifact:".
    405 %
    406 %  The returned string is stored as a properity of the same name for faster
    407 %  lookup later. It should NOT be freed by the caller.
    408 %
    409 %  The format of the GetImageProperty method is:
    410 %
    411 %      const char *GetImageProperty(const Image *image,const char *key,
    412 %        ExceptionInfo *exception)
    413 %
    414 %  A description of each parameter follows:
    415 %
    416 %    o image: the image.
    417 %
    418 %    o key: the key.
    419 %
    420 %    o exception: return any errors or warnings in this structure.
    421 %
    422 */
    423 
    424 static char
    425   *TracePSClippath(const unsigned char *,size_t),
    426   *TraceSVGClippath(const unsigned char *,size_t,const size_t,
    427     const size_t);
    428 
    429 static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
    430   ExceptionInfo *exception)
    431 {
    432   char
    433     *attribute,
    434     *message;
    435 
    436   const StringInfo
    437     *profile;
    438 
    439   long
    440     count,
    441     dataset,
    442     record;
    443 
    444   register ssize_t
    445     i;
    446 
    447   size_t
    448     length;
    449 
    450   profile=GetImageProfile(image,"iptc");
    451   if (profile == (StringInfo *) NULL)
    452     profile=GetImageProfile(image,"8bim");
    453   if (profile == (StringInfo *) NULL)
    454     return(MagickFalse);
    455   count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
    456   if (count != 2)
    457     return(MagickFalse);
    458   attribute=(char *) NULL;
    459   for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
    460   {
    461     length=1;
    462     if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
    463       continue;
    464     length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
    465     length|=GetStringInfoDatum(profile)[i+4];
    466     if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
    467         ((long) GetStringInfoDatum(profile)[i+2] == record))
    468       {
    469         message=(char *) NULL;
    470         if (~length >= 1)
    471           message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
    472         if (message != (char *) NULL)
    473           {
    474             (void) CopyMagickString(message,(char *) GetStringInfoDatum(
    475               profile)+i+5,length+1);
    476             (void) ConcatenateString(&attribute,message);
    477             (void) ConcatenateString(&attribute,";");
    478             message=DestroyString(message);
    479           }
    480       }
    481     i+=5;
    482   }
    483   if ((attribute == (char *) NULL) || (*attribute == ';'))
    484     {
    485       if (attribute != (char *) NULL)
    486         attribute=DestroyString(attribute);
    487       return(MagickFalse);
    488     }
    489   attribute[strlen(attribute)-1]='\0';
    490   (void) SetImageProperty((Image *) image,key,(const char *) attribute,
    491     exception);
    492   attribute=DestroyString(attribute);
    493   return(MagickTrue);
    494 }
    495 
    496 static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
    497 {
    498   int
    499     c;
    500 
    501   if (*length < 1)
    502     return(EOF);
    503   c=(int) (*(*p)++);
    504   (*length)--;
    505   return(c);
    506 }
    507 
    508 static inline signed int ReadPropertyMSBLong(const unsigned char **p,
    509   size_t *length)
    510 {
    511   union
    512   {
    513     unsigned int
    514       unsigned_value;
    515 
    516     signed int
    517       signed_value;
    518   } quantum;
    519 
    520   int
    521     c;
    522 
    523   register ssize_t
    524     i;
    525 
    526   unsigned char
    527     buffer[4];
    528 
    529   unsigned int
    530     value;
    531 
    532   if (*length < 4)
    533     return(-1);
    534   for (i=0; i < 4; i++)
    535   {
    536     c=(int) (*(*p)++);
    537     (*length)--;
    538     buffer[i]=(unsigned char) c;
    539   }
    540   value=(unsigned int) buffer[0] << 24;
    541   value|=(unsigned int) buffer[1] << 16;
    542   value|=(unsigned int) buffer[2] << 8;
    543   value|=(unsigned int) buffer[3];
    544   quantum.unsigned_value=value & 0xffffffff;
    545   return(quantum.signed_value);
    546 }
    547 
    548 static inline signed short ReadPropertyMSBShort(const unsigned char **p,
    549   size_t *length)
    550 {
    551   union
    552   {
    553     unsigned short
    554       unsigned_value;
    555 
    556     signed short
    557       signed_value;
    558   } quantum;
    559 
    560   int
    561     c;
    562 
    563   register ssize_t
    564     i;
    565 
    566   unsigned char
    567     buffer[2];
    568 
    569   unsigned short
    570     value;
    571 
    572   if (*length < 2)
    573     return((unsigned short) ~0);
    574   for (i=0; i < 2; i++)
    575   {
    576     c=(int) (*(*p)++);
    577     (*length)--;
    578     buffer[i]=(unsigned char) c;
    579   }
    580   value=(unsigned short) buffer[0] << 8;
    581   value|=(unsigned short) buffer[1];
    582   quantum.unsigned_value=value & 0xffff;
    583   return(quantum.signed_value);
    584 }
    585 
    586 static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
    587   ExceptionInfo *exception)
    588 {
    589   char
    590     *attribute,
    591     format[MagickPathExtent],
    592     name[MagickPathExtent],
    593     *resource;
    594 
    595   const StringInfo
    596     *profile;
    597 
    598   const unsigned char
    599     *info;
    600 
    601   long
    602     start,
    603     stop;
    604 
    605   MagickBooleanType
    606     status;
    607 
    608   register ssize_t
    609     i;
    610 
    611   size_t
    612     length;
    613 
    614   ssize_t
    615     count,
    616     id,
    617     sub_number;
    618 
    619   /*
    620     There are no newlines in path names, so it's safe as terminator.
    621   */
    622   profile=GetImageProfile(image,"8bim");
    623   if (profile == (StringInfo *) NULL)
    624     return(MagickFalse);
    625   count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%1024[^\n]\n%1024[^\n]",&start,&stop,
    626     name,format);
    627   if ((count != 2) && (count != 3) && (count != 4))
    628     return(MagickFalse);
    629   if (count < 4)
    630     (void) CopyMagickString(format,"SVG",MagickPathExtent);
    631   if (count < 3)
    632     *name='\0';
    633   sub_number=1;
    634   if (*name == '#')
    635     sub_number=(ssize_t) StringToLong(&name[1]);
    636   sub_number=MagickMax(sub_number,1L);
    637   resource=(char *) NULL;
    638   status=MagickFalse;
    639   length=GetStringInfoLength(profile);
    640   info=GetStringInfoDatum(profile);
    641   while ((length > 0) && (status == MagickFalse))
    642   {
    643     if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
    644       continue;
    645     if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
    646       continue;
    647     if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
    648       continue;
    649     if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
    650       continue;
    651     id=(ssize_t) ReadPropertyMSBShort(&info,&length);
    652     if (id < (ssize_t) start)
    653       continue;
    654     if (id > (ssize_t) stop)
    655       continue;
    656     if (resource != (char *) NULL)
    657       resource=DestroyString(resource);
    658     count=(ssize_t) ReadPropertyByte(&info,&length);
    659     if ((count != 0) && ((size_t) count <= length))
    660       {
    661         resource=(char *) NULL;
    662         if (~((size_t) count) >= (MagickPathExtent-1))
    663           resource=(char *) AcquireQuantumMemory((size_t) count+
    664             MagickPathExtent,sizeof(*resource));
    665         if (resource != (char *) NULL)
    666           {
    667             for (i=0; i < (ssize_t) count; i++)
    668               resource[i]=(char) ReadPropertyByte(&info,&length);
    669             resource[count]='\0';
    670           }
    671       }
    672     if ((count & 0x01) == 0)
    673       (void) ReadPropertyByte(&info,&length);
    674     count=(ssize_t) ReadPropertyMSBLong(&info,&length);
    675     if ((*name != '\0') && (*name != '#'))
    676       if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
    677         {
    678           /*
    679             No name match, scroll forward and try next.
    680           */
    681           info+=count;
    682           length-=MagickMin(count,(ssize_t) length);
    683           continue;
    684         }
    685     if ((*name == '#') && (sub_number != 1))
    686       {
    687         /*
    688           No numbered match, scroll forward and try next.
    689         */
    690         sub_number--;
    691         info+=count;
    692         length-=MagickMin(count,(ssize_t) length);
    693         continue;
    694       }
    695     /*
    696       We have the resource of interest.
    697     */
    698     attribute=(char *) NULL;
    699     if (~((size_t) count) >= (MagickPathExtent-1))
    700       attribute=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent,
    701         sizeof(*attribute));
    702     if (attribute != (char *) NULL)
    703       {
    704         (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
    705         attribute[count]='\0';
    706         info+=count;
    707         length-=MagickMin(count,(ssize_t) length);
    708         if ((id <= 1999) || (id >= 2999))
    709           (void) SetImageProperty((Image *) image,key,(const char *)
    710             attribute,exception);
    711         else
    712           {
    713             char
    714               *path;
    715 
    716             if (LocaleCompare(format,"svg") == 0)
    717               path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
    718                 image->columns,image->rows);
    719             else
    720               path=TracePSClippath((unsigned char *) attribute,(size_t) count);
    721             (void) SetImageProperty((Image *) image,key,(const char *) path,
    722               exception);
    723             path=DestroyString(path);
    724           }
    725         attribute=DestroyString(attribute);
    726         status=MagickTrue;
    727       }
    728   }
    729   if (resource != (char *) NULL)
    730     resource=DestroyString(resource);
    731   return(status);
    732 }
    733 
    734 static inline signed int ReadPropertySignedLong(const EndianType endian,
    735   const unsigned char *buffer)
    736 {
    737   union
    738   {
    739     unsigned int
    740       unsigned_value;
    741 
    742     signed int
    743       signed_value;
    744   } quantum;
    745 
    746   unsigned int
    747     value;
    748 
    749   if (endian == LSBEndian)
    750     {
    751       value=(unsigned int) buffer[3] << 24;
    752       value|=(unsigned int) buffer[2] << 16;
    753       value|=(unsigned int) buffer[1] << 8;
    754       value|=(unsigned int) buffer[0];
    755       quantum.unsigned_value=value & 0xffffffff;
    756       return(quantum.signed_value);
    757     }
    758   value=(unsigned int) buffer[0] << 24;
    759   value|=(unsigned int) buffer[1] << 16;
    760   value|=(unsigned int) buffer[2] << 8;
    761   value|=(unsigned int) buffer[3];
    762   quantum.unsigned_value=value & 0xffffffff;
    763   return(quantum.signed_value);
    764 }
    765 
    766 static inline unsigned int ReadPropertyUnsignedLong(const EndianType endian,
    767   const unsigned char *buffer)
    768 {
    769   unsigned int
    770     value;
    771 
    772   if (endian == LSBEndian)
    773     {
    774       value=(unsigned int) buffer[3] << 24;
    775       value|=(unsigned int) buffer[2] << 16;
    776       value|=(unsigned int) buffer[1] << 8;
    777       value|=(unsigned int) buffer[0];
    778       return(value & 0xffffffff);
    779     }
    780   value=(unsigned int) buffer[0] << 24;
    781   value|=(unsigned int) buffer[1] << 16;
    782   value|=(unsigned int) buffer[2] << 8;
    783   value|=(unsigned int) buffer[3];
    784   return(value & 0xffffffff);
    785 }
    786 
    787 static inline signed short ReadPropertySignedShort(const EndianType endian,
    788   const unsigned char *buffer)
    789 {
    790   union
    791   {
    792     unsigned short
    793       unsigned_value;
    794 
    795     signed short
    796       signed_value;
    797   } quantum;
    798 
    799   unsigned short
    800     value;
    801 
    802   if (endian == LSBEndian)
    803     {
    804       value=(unsigned short) buffer[1] << 8;
    805       value|=(unsigned short) buffer[0];
    806       quantum.unsigned_value=value & 0xffff;
    807       return(quantum.signed_value);
    808     }
    809   value=(unsigned short) buffer[0] << 8;
    810   value|=(unsigned short) buffer[1];
    811   quantum.unsigned_value=value & 0xffff;
    812   return(quantum.signed_value);
    813 }
    814 
    815 static inline unsigned short ReadPropertyUnsignedShort(const EndianType endian,
    816   const unsigned char *buffer)
    817 {
    818   unsigned short
    819     value;
    820 
    821   if (endian == LSBEndian)
    822     {
    823       value=(unsigned short) buffer[1] << 8;
    824       value|=(unsigned short) buffer[0];
    825       return(value & 0xffff);
    826     }
    827   value=(unsigned short) buffer[0] << 8;
    828   value|=(unsigned short) buffer[1];
    829   return(value & 0xffff);
    830 }
    831 
    832 static MagickBooleanType GetEXIFProperty(const Image *image,
    833   const char *property,ExceptionInfo *exception)
    834 {
    835 #define MaxDirectoryStack  16
    836 #define EXIF_DELIMITER  "\n"
    837 #define EXIF_NUM_FORMATS  12
    838 #define EXIF_FMT_BYTE  1
    839 #define EXIF_FMT_STRING  2
    840 #define EXIF_FMT_USHORT  3
    841 #define EXIF_FMT_ULONG  4
    842 #define EXIF_FMT_URATIONAL  5
    843 #define EXIF_FMT_SBYTE  6
    844 #define EXIF_FMT_UNDEFINED  7
    845 #define EXIF_FMT_SSHORT  8
    846 #define EXIF_FMT_SLONG  9
    847 #define EXIF_FMT_SRATIONAL  10
    848 #define EXIF_FMT_SINGLE  11
    849 #define EXIF_FMT_DOUBLE  12
    850 #define TAG_EXIF_OFFSET  0x8769
    851 #define TAG_GPS_OFFSET  0x8825
    852 #define TAG_INTEROP_OFFSET  0xa005
    853 
    854 #define EXIFMultipleValues(size,format,arg) \
    855 { \
    856    ssize_t \
    857      component; \
    858  \
    859    size_t \
    860      length; \
    861  \
    862    unsigned char \
    863      *p1; \
    864  \
    865    length=0; \
    866    p1=p; \
    867    for (component=0; component < components; component++) \
    868    { \
    869      length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \
    870        format", ",arg); \
    871      if (length >= (MagickPathExtent-1)) \
    872        length=MagickPathExtent-1; \
    873      p1+=size; \
    874    } \
    875    if (length > 1) \
    876      buffer[length-2]='\0'; \
    877    value=AcquireString(buffer); \
    878 }
    879 
    880 #define EXIFMultipleFractions(size,format,arg1,arg2) \
    881 { \
    882    ssize_t \
    883      component; \
    884  \
    885    size_t \
    886      length; \
    887  \
    888    unsigned char \
    889      *p1; \
    890  \
    891    length=0; \
    892    p1=p; \
    893    for (component=0; component < components; component++) \
    894    { \
    895      length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \
    896        format", ",(arg1),(arg2)); \
    897      if (length >= (MagickPathExtent-1)) \
    898        length=MagickPathExtent-1; \
    899      p1+=size; \
    900    } \
    901    if (length > 1) \
    902      buffer[length-2]='\0'; \
    903    value=AcquireString(buffer); \
    904 }
    905 
    906   typedef struct _DirectoryInfo
    907   {
    908     const unsigned char
    909       *directory;
    910 
    911     size_t
    912       entry;
    913 
    914     ssize_t
    915       offset;
    916   } DirectoryInfo;
    917 
    918   typedef struct _TagInfo
    919   {
    920     size_t
    921       tag;
    922 
    923     const char
    924       *description;
    925   } TagInfo;
    926 
    927   static TagInfo
    928     EXIFTag[] =
    929     {
    930       {  0x001, "exif:InteroperabilityIndex" },
    931       {  0x002, "exif:InteroperabilityVersion" },
    932       {  0x100, "exif:ImageWidth" },
    933       {  0x101, "exif:ImageLength" },
    934       {  0x102, "exif:BitsPerSample" },
    935       {  0x103, "exif:Compression" },
    936       {  0x106, "exif:PhotometricInterpretation" },
    937       {  0x10a, "exif:FillOrder" },
    938       {  0x10d, "exif:DocumentName" },
    939       {  0x10e, "exif:ImageDescription" },
    940       {  0x10f, "exif:Make" },
    941       {  0x110, "exif:Model" },
    942       {  0x111, "exif:StripOffsets" },
    943       {  0x112, "exif:Orientation" },
    944       {  0x115, "exif:SamplesPerPixel" },
    945       {  0x116, "exif:RowsPerStrip" },
    946       {  0x117, "exif:StripByteCounts" },
    947       {  0x11a, "exif:XResolution" },
    948       {  0x11b, "exif:YResolution" },
    949       {  0x11c, "exif:PlanarConfiguration" },
    950       {  0x11d, "exif:PageName" },
    951       {  0x11e, "exif:XPosition" },
    952       {  0x11f, "exif:YPosition" },
    953       {  0x118, "exif:MinSampleValue" },
    954       {  0x119, "exif:MaxSampleValue" },
    955       {  0x120, "exif:FreeOffsets" },
    956       {  0x121, "exif:FreeByteCounts" },
    957       {  0x122, "exif:GrayResponseUnit" },
    958       {  0x123, "exif:GrayResponseCurve" },
    959       {  0x124, "exif:T4Options" },
    960       {  0x125, "exif:T6Options" },
    961       {  0x128, "exif:ResolutionUnit" },
    962       {  0x12d, "exif:TransferFunction" },
    963       {  0x131, "exif:Software" },
    964       {  0x132, "exif:DateTime" },
    965       {  0x13b, "exif:Artist" },
    966       {  0x13e, "exif:WhitePoint" },
    967       {  0x13f, "exif:PrimaryChromaticities" },
    968       {  0x140, "exif:ColorMap" },
    969       {  0x141, "exif:HalfToneHints" },
    970       {  0x142, "exif:TileWidth" },
    971       {  0x143, "exif:TileLength" },
    972       {  0x144, "exif:TileOffsets" },
    973       {  0x145, "exif:TileByteCounts" },
    974       {  0x14a, "exif:SubIFD" },
    975       {  0x14c, "exif:InkSet" },
    976       {  0x14d, "exif:InkNames" },
    977       {  0x14e, "exif:NumberOfInks" },
    978       {  0x150, "exif:DotRange" },
    979       {  0x151, "exif:TargetPrinter" },
    980       {  0x152, "exif:ExtraSample" },
    981       {  0x153, "exif:SampleFormat" },
    982       {  0x154, "exif:SMinSampleValue" },
    983       {  0x155, "exif:SMaxSampleValue" },
    984       {  0x156, "exif:TransferRange" },
    985       {  0x157, "exif:ClipPath" },
    986       {  0x158, "exif:XClipPathUnits" },
    987       {  0x159, "exif:YClipPathUnits" },
    988       {  0x15a, "exif:Indexed" },
    989       {  0x15b, "exif:JPEGTables" },
    990       {  0x15f, "exif:OPIProxy" },
    991       {  0x200, "exif:JPEGProc" },
    992       {  0x201, "exif:JPEGInterchangeFormat" },
    993       {  0x202, "exif:JPEGInterchangeFormatLength" },
    994       {  0x203, "exif:JPEGRestartInterval" },
    995       {  0x205, "exif:JPEGLosslessPredictors" },
    996       {  0x206, "exif:JPEGPointTransforms" },
    997       {  0x207, "exif:JPEGQTables" },
    998       {  0x208, "exif:JPEGDCTables" },
    999       {  0x209, "exif:JPEGACTables" },
   1000       {  0x211, "exif:YCbCrCoefficients" },
   1001       {  0x212, "exif:YCbCrSubSampling" },
   1002       {  0x213, "exif:YCbCrPositioning" },
   1003       {  0x214, "exif:ReferenceBlackWhite" },
   1004       {  0x2bc, "exif:ExtensibleMetadataPlatform" },
   1005       {  0x301, "exif:Gamma" },
   1006       {  0x302, "exif:ICCProfileDescriptor" },
   1007       {  0x303, "exif:SRGBRenderingIntent" },
   1008       {  0x320, "exif:ImageTitle" },
   1009       {  0x5001, "exif:ResolutionXUnit" },
   1010       {  0x5002, "exif:ResolutionYUnit" },
   1011       {  0x5003, "exif:ResolutionXLengthUnit" },
   1012       {  0x5004, "exif:ResolutionYLengthUnit" },
   1013       {  0x5005, "exif:PrintFlags" },
   1014       {  0x5006, "exif:PrintFlagsVersion" },
   1015       {  0x5007, "exif:PrintFlagsCrop" },
   1016       {  0x5008, "exif:PrintFlagsBleedWidth" },
   1017       {  0x5009, "exif:PrintFlagsBleedWidthScale" },
   1018       {  0x500A, "exif:HalftoneLPI" },
   1019       {  0x500B, "exif:HalftoneLPIUnit" },
   1020       {  0x500C, "exif:HalftoneDegree" },
   1021       {  0x500D, "exif:HalftoneShape" },
   1022       {  0x500E, "exif:HalftoneMisc" },
   1023       {  0x500F, "exif:HalftoneScreen" },
   1024       {  0x5010, "exif:JPEGQuality" },
   1025       {  0x5011, "exif:GridSize" },
   1026       {  0x5012, "exif:ThumbnailFormat" },
   1027       {  0x5013, "exif:ThumbnailWidth" },
   1028       {  0x5014, "exif:ThumbnailHeight" },
   1029       {  0x5015, "exif:ThumbnailColorDepth" },
   1030       {  0x5016, "exif:ThumbnailPlanes" },
   1031       {  0x5017, "exif:ThumbnailRawBytes" },
   1032       {  0x5018, "exif:ThumbnailSize" },
   1033       {  0x5019, "exif:ThumbnailCompressedSize" },
   1034       {  0x501a, "exif:ColorTransferFunction" },
   1035       {  0x501b, "exif:ThumbnailData" },
   1036       {  0x5020, "exif:ThumbnailImageWidth" },
   1037       {  0x5021, "exif:ThumbnailImageHeight" },
   1038       {  0x5022, "exif:ThumbnailBitsPerSample" },
   1039       {  0x5023, "exif:ThumbnailCompression" },
   1040       {  0x5024, "exif:ThumbnailPhotometricInterp" },
   1041       {  0x5025, "exif:ThumbnailImageDescription" },
   1042       {  0x5026, "exif:ThumbnailEquipMake" },
   1043       {  0x5027, "exif:ThumbnailEquipModel" },
   1044       {  0x5028, "exif:ThumbnailStripOffsets" },
   1045       {  0x5029, "exif:ThumbnailOrientation" },
   1046       {  0x502a, "exif:ThumbnailSamplesPerPixel" },
   1047       {  0x502b, "exif:ThumbnailRowsPerStrip" },
   1048       {  0x502c, "exif:ThumbnailStripBytesCount" },
   1049       {  0x502d, "exif:ThumbnailResolutionX" },
   1050       {  0x502e, "exif:ThumbnailResolutionY" },
   1051       {  0x502f, "exif:ThumbnailPlanarConfig" },
   1052       {  0x5030, "exif:ThumbnailResolutionUnit" },
   1053       {  0x5031, "exif:ThumbnailTransferFunction" },
   1054       {  0x5032, "exif:ThumbnailSoftwareUsed" },
   1055       {  0x5033, "exif:ThumbnailDateTime" },
   1056       {  0x5034, "exif:ThumbnailArtist" },
   1057       {  0x5035, "exif:ThumbnailWhitePoint" },
   1058       {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
   1059       {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
   1060       {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
   1061       {  0x5039, "exif:ThumbnailYCbCrPositioning" },
   1062       {  0x503A, "exif:ThumbnailRefBlackWhite" },
   1063       {  0x503B, "exif:ThumbnailCopyRight" },
   1064       {  0x5090, "exif:LuminanceTable" },
   1065       {  0x5091, "exif:ChrominanceTable" },
   1066       {  0x5100, "exif:FrameDelay" },
   1067       {  0x5101, "exif:LoopCount" },
   1068       {  0x5110, "exif:PixelUnit" },
   1069       {  0x5111, "exif:PixelPerUnitX" },
   1070       {  0x5112, "exif:PixelPerUnitY" },
   1071       {  0x5113, "exif:PaletteHistogram" },
   1072       {  0x1000, "exif:RelatedImageFileFormat" },
   1073       {  0x1001, "exif:RelatedImageLength" },
   1074       {  0x1002, "exif:RelatedImageWidth" },
   1075       {  0x800d, "exif:ImageID" },
   1076       {  0x80e3, "exif:Matteing" },
   1077       {  0x80e4, "exif:DataType" },
   1078       {  0x80e5, "exif:ImageDepth" },
   1079       {  0x80e6, "exif:TileDepth" },
   1080       {  0x828d, "exif:CFARepeatPatternDim" },
   1081       {  0x828e, "exif:CFAPattern2" },
   1082       {  0x828f, "exif:BatteryLevel" },
   1083       {  0x8298, "exif:Copyright" },
   1084       {  0x829a, "exif:ExposureTime" },
   1085       {  0x829d, "exif:FNumber" },
   1086       {  0x83bb, "exif:IPTC/NAA" },
   1087       {  0x84e3, "exif:IT8RasterPadding" },
   1088       {  0x84e5, "exif:IT8ColorTable" },
   1089       {  0x8649, "exif:ImageResourceInformation" },
   1090       {  0x8769, "exif:ExifOffset" },
   1091       {  0x8773, "exif:InterColorProfile" },
   1092       {  0x8822, "exif:ExposureProgram" },
   1093       {  0x8824, "exif:SpectralSensitivity" },
   1094       {  0x8825, "exif:GPSInfo" },
   1095       {  0x8827, "exif:ISOSpeedRatings" },
   1096       {  0x8828, "exif:OECF" },
   1097       {  0x8829, "exif:Interlace" },
   1098       {  0x882a, "exif:TimeZoneOffset" },
   1099       {  0x882b, "exif:SelfTimerMode" },
   1100       {  0x9000, "exif:ExifVersion" },
   1101       {  0x9003, "exif:DateTimeOriginal" },
   1102       {  0x9004, "exif:DateTimeDigitized" },
   1103       {  0x9101, "exif:ComponentsConfiguration" },
   1104       {  0x9102, "exif:CompressedBitsPerPixel" },
   1105       {  0x9201, "exif:ShutterSpeedValue" },
   1106       {  0x9202, "exif:ApertureValue" },
   1107       {  0x9203, "exif:BrightnessValue" },
   1108       {  0x9204, "exif:ExposureBiasValue" },
   1109       {  0x9205, "exif:MaxApertureValue" },
   1110       {  0x9206, "exif:SubjectDistance" },
   1111       {  0x9207, "exif:MeteringMode" },
   1112       {  0x9208, "exif:LightSource" },
   1113       {  0x9209, "exif:Flash" },
   1114       {  0x920a, "exif:FocalLength" },
   1115       {  0x920b, "exif:FlashEnergy" },
   1116       {  0x920c, "exif:SpatialFrequencyResponse" },
   1117       {  0x920d, "exif:Noise" },
   1118       {  0x9211, "exif:ImageNumber" },
   1119       {  0x9212, "exif:SecurityClassification" },
   1120       {  0x9213, "exif:ImageHistory" },
   1121       {  0x9214, "exif:SubjectArea" },
   1122       {  0x9215, "exif:ExposureIndex" },
   1123       {  0x9216, "exif:TIFF-EPStandardID" },
   1124       {  0x927c, "exif:MakerNote" },
   1125       {  0x9C9b, "exif:WinXP-Title" },
   1126       {  0x9C9c, "exif:WinXP-Comments" },
   1127       {  0x9C9d, "exif:WinXP-Author" },
   1128       {  0x9C9e, "exif:WinXP-Keywords" },
   1129       {  0x9C9f, "exif:WinXP-Subject" },
   1130       {  0x9286, "exif:UserComment" },
   1131       {  0x9290, "exif:SubSecTime" },
   1132       {  0x9291, "exif:SubSecTimeOriginal" },
   1133       {  0x9292, "exif:SubSecTimeDigitized" },
   1134       {  0xa000, "exif:FlashPixVersion" },
   1135       {  0xa001, "exif:ColorSpace" },
   1136       {  0xa002, "exif:ExifImageWidth" },
   1137       {  0xa003, "exif:ExifImageLength" },
   1138       {  0xa004, "exif:RelatedSoundFile" },
   1139       {  0xa005, "exif:InteroperabilityOffset" },
   1140       {  0xa20b, "exif:FlashEnergy" },
   1141       {  0xa20c, "exif:SpatialFrequencyResponse" },
   1142       {  0xa20d, "exif:Noise" },
   1143       {  0xa20e, "exif:FocalPlaneXResolution" },
   1144       {  0xa20f, "exif:FocalPlaneYResolution" },
   1145       {  0xa210, "exif:FocalPlaneResolutionUnit" },
   1146       {  0xa214, "exif:SubjectLocation" },
   1147       {  0xa215, "exif:ExposureIndex" },
   1148       {  0xa216, "exif:TIFF/EPStandardID" },
   1149       {  0xa217, "exif:SensingMethod" },
   1150       {  0xa300, "exif:FileSource" },
   1151       {  0xa301, "exif:SceneType" },
   1152       {  0xa302, "exif:CFAPattern" },
   1153       {  0xa401, "exif:CustomRendered" },
   1154       {  0xa402, "exif:ExposureMode" },
   1155       {  0xa403, "exif:WhiteBalance" },
   1156       {  0xa404, "exif:DigitalZoomRatio" },
   1157       {  0xa405, "exif:FocalLengthIn35mmFilm" },
   1158       {  0xa406, "exif:SceneCaptureType" },
   1159       {  0xa407, "exif:GainControl" },
   1160       {  0xa408, "exif:Contrast" },
   1161       {  0xa409, "exif:Saturation" },
   1162       {  0xa40a, "exif:Sharpness" },
   1163       {  0xa40b, "exif:DeviceSettingDescription" },
   1164       {  0xa40c, "exif:SubjectDistanceRange" },
   1165       {  0xa420, "exif:ImageUniqueID" },
   1166       {  0xc4a5, "exif:PrintImageMatching" },
   1167       {  0xa500, "exif:Gamma" },
   1168       {  0xc640, "exif:CR2Slice" },
   1169       { 0x10000, "exif:GPSVersionID" },
   1170       { 0x10001, "exif:GPSLatitudeRef" },
   1171       { 0x10002, "exif:GPSLatitude" },
   1172       { 0x10003, "exif:GPSLongitudeRef" },
   1173       { 0x10004, "exif:GPSLongitude" },
   1174       { 0x10005, "exif:GPSAltitudeRef" },
   1175       { 0x10006, "exif:GPSAltitude" },
   1176       { 0x10007, "exif:GPSTimeStamp" },
   1177       { 0x10008, "exif:GPSSatellites" },
   1178       { 0x10009, "exif:GPSStatus" },
   1179       { 0x1000a, "exif:GPSMeasureMode" },
   1180       { 0x1000b, "exif:GPSDop" },
   1181       { 0x1000c, "exif:GPSSpeedRef" },
   1182       { 0x1000d, "exif:GPSSpeed" },
   1183       { 0x1000e, "exif:GPSTrackRef" },
   1184       { 0x1000f, "exif:GPSTrack" },
   1185       { 0x10010, "exif:GPSImgDirectionRef" },
   1186       { 0x10011, "exif:GPSImgDirection" },
   1187       { 0x10012, "exif:GPSMapDatum" },
   1188       { 0x10013, "exif:GPSDestLatitudeRef" },
   1189       { 0x10014, "exif:GPSDestLatitude" },
   1190       { 0x10015, "exif:GPSDestLongitudeRef" },
   1191       { 0x10016, "exif:GPSDestLongitude" },
   1192       { 0x10017, "exif:GPSDestBearingRef" },
   1193       { 0x10018, "exif:GPSDestBearing" },
   1194       { 0x10019, "exif:GPSDestDistanceRef" },
   1195       { 0x1001a, "exif:GPSDestDistance" },
   1196       { 0x1001b, "exif:GPSProcessingMethod" },
   1197       { 0x1001c, "exif:GPSAreaInformation" },
   1198       { 0x1001d, "exif:GPSDateStamp" },
   1199       { 0x1001e, "exif:GPSDifferential" },
   1200       { 0x00000, (const char *) NULL }
   1201     };
   1202 
   1203   const StringInfo
   1204     *profile;
   1205 
   1206   const unsigned char
   1207     *directory,
   1208     *exif;
   1209 
   1210   DirectoryInfo
   1211     directory_stack[MaxDirectoryStack];
   1212 
   1213   EndianType
   1214     endian;
   1215 
   1216   MagickBooleanType
   1217     status;
   1218 
   1219   register ssize_t
   1220     i;
   1221 
   1222   size_t
   1223     entry,
   1224     length,
   1225     number_entries,
   1226     tag,
   1227     tag_value;
   1228 
   1229   SplayTreeInfo
   1230     *exif_resources;
   1231 
   1232   ssize_t
   1233     all,
   1234     id,
   1235     level,
   1236     offset,
   1237     tag_offset;
   1238 
   1239   static int
   1240     tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
   1241 
   1242   /*
   1243     If EXIF data exists, then try to parse the request for a tag.
   1244   */
   1245   profile=GetImageProfile(image,"exif");
   1246   if (profile == (const StringInfo *) NULL)
   1247     return(MagickFalse);
   1248   if ((property == (const char *) NULL) || (*property == '\0'))
   1249     return(MagickFalse);
   1250   while (isspace((int) ((unsigned char) *property)) != 0)
   1251     property++;
   1252   if (strlen(property) <= 5)
   1253     return(MagickFalse);
   1254   all=0;
   1255   tag=(~0UL);
   1256   switch (*(property+5))
   1257   {
   1258     case '*':
   1259     {
   1260       /*
   1261         Caller has asked for all the tags in the EXIF data.
   1262       */
   1263       tag=0;
   1264       all=1; /* return the data in description=value format */
   1265       break;
   1266     }
   1267     case '!':
   1268     {
   1269       tag=0;
   1270       all=2; /* return the data in tagid=value format */
   1271       break;
   1272     }
   1273     case '#':
   1274     case '@':
   1275     {
   1276       int
   1277         c;
   1278 
   1279       size_t
   1280         n;
   1281 
   1282       /*
   1283         Check for a hex based tag specification first.
   1284       */
   1285       tag=(*(property+5) == '@') ? 1UL : 0UL;
   1286       property+=6;
   1287       n=strlen(property);
   1288       if (n != 4)
   1289         return(MagickFalse);
   1290       /*
   1291         Parse tag specification as a hex number.
   1292       */
   1293       n/=4;
   1294       do
   1295       {
   1296         for (i=(ssize_t) n-1L; i >= 0; i--)
   1297         {
   1298           c=(*property++);
   1299           tag<<=4;
   1300           if ((c >= '0') && (c <= '9'))
   1301             tag|=(c-'0');
   1302           else
   1303             if ((c >= 'A') && (c <= 'F'))
   1304               tag|=(c-('A'-10));
   1305             else
   1306               if ((c >= 'a') && (c <= 'f'))
   1307                 tag|=(c-('a'-10));
   1308               else
   1309                 return(MagickFalse);
   1310         }
   1311       } while (*property != '\0');
   1312       break;
   1313     }
   1314     default:
   1315     {
   1316       /*
   1317         Try to match the text with a tag name instead.
   1318       */
   1319       for (i=0; ; i++)
   1320       {
   1321         if (EXIFTag[i].tag == 0)
   1322           break;
   1323         if (LocaleCompare(EXIFTag[i].description,property) == 0)
   1324           {
   1325             tag=(size_t) EXIFTag[i].tag;
   1326             break;
   1327           }
   1328       }
   1329       break;
   1330     }
   1331   }
   1332   if (tag == (~0UL))
   1333     return(MagickFalse);
   1334   length=GetStringInfoLength(profile);
   1335   exif=GetStringInfoDatum(profile);
   1336   while (length != 0)
   1337   {
   1338     if (ReadPropertyByte(&exif,&length) != 0x45)
   1339       continue;
   1340     if (ReadPropertyByte(&exif,&length) != 0x78)
   1341       continue;
   1342     if (ReadPropertyByte(&exif,&length) != 0x69)
   1343       continue;
   1344     if (ReadPropertyByte(&exif,&length) != 0x66)
   1345       continue;
   1346     if (ReadPropertyByte(&exif,&length) != 0x00)
   1347       continue;
   1348     if (ReadPropertyByte(&exif,&length) != 0x00)
   1349       continue;
   1350     break;
   1351   }
   1352   if (length < 16)
   1353     return(MagickFalse);
   1354   id=(ssize_t) ReadPropertySignedShort(LSBEndian,exif);
   1355   endian=LSBEndian;
   1356   if (id == 0x4949)
   1357     endian=LSBEndian;
   1358   else
   1359     if (id == 0x4D4D)
   1360       endian=MSBEndian;
   1361     else
   1362       return(MagickFalse);
   1363   if (ReadPropertyUnsignedShort(endian,exif+2) != 0x002a)
   1364     return(MagickFalse);
   1365   /*
   1366     This the offset to the first IFD.
   1367   */
   1368   offset=(ssize_t) ReadPropertySignedLong(endian,exif+4);
   1369   if ((offset < 0) || (size_t) offset >= length)
   1370     return(MagickFalse);
   1371   /*
   1372     Set the pointer to the first IFD and follow it were it leads.
   1373   */
   1374   status=MagickFalse;
   1375   directory=exif+offset;
   1376   level=0;
   1377   entry=0;
   1378   tag_offset=0;
   1379   exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
   1380     (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
   1381   do
   1382   {
   1383     /*
   1384       If there is anything on the stack then pop it off.
   1385     */
   1386     if (level > 0)
   1387       {
   1388         level--;
   1389         directory=directory_stack[level].directory;
   1390         entry=directory_stack[level].entry;
   1391         tag_offset=directory_stack[level].offset;
   1392       }
   1393     if ((directory < exif) || (directory > (exif+length-2)))
   1394       break;
   1395     /*
   1396       Determine how many entries there are in the current IFD.
   1397     */
   1398     number_entries=(size_t) ReadPropertyUnsignedShort(endian,directory);
   1399     for ( ; entry < number_entries; entry++)
   1400     {
   1401       register unsigned char
   1402         *p,
   1403         *q;
   1404 
   1405       size_t
   1406         format;
   1407 
   1408       ssize_t
   1409         number_bytes,
   1410         components;
   1411 
   1412       q=(unsigned char *) (directory+(12*entry)+2);
   1413       if (q > (exif+length-12))
   1414         break;  /* corrupt EXIF */
   1415       if (GetValueFromSplayTree(exif_resources,q) == q)
   1416         break;
   1417       (void) AddValueToSplayTree(exif_resources,q,q);
   1418       tag_value=(size_t) ReadPropertyUnsignedShort(endian,q)+tag_offset;
   1419       format=(size_t) ReadPropertyUnsignedShort(endian,q+2);
   1420       if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
   1421         break;
   1422       components=(ssize_t) ReadPropertySignedLong(endian,q+4);
   1423       if (components < 0)
   1424         break;  /* corrupt EXIF */
   1425       number_bytes=(size_t) components*tag_bytes[format];
   1426       if (number_bytes < components)
   1427         break;  /* prevent overflow */
   1428       if (number_bytes <= 4)
   1429         p=q+8;
   1430       else
   1431         {
   1432           ssize_t
   1433             offset;
   1434 
   1435           /*
   1436             The directory entry contains an offset.
   1437           */
   1438           offset=(ssize_t) ReadPropertySignedLong(endian,q+8);
   1439           if ((offset < 0) || (size_t) offset >= length)
   1440             continue;
   1441           if ((ssize_t) (offset+number_bytes) < offset)
   1442             continue;  /* prevent overflow */
   1443           if ((size_t) (offset+number_bytes) > length)
   1444             continue;
   1445           p=(unsigned char *) (exif+offset);
   1446         }
   1447       if ((all != 0) || (tag == (size_t) tag_value))
   1448         {
   1449           char
   1450             buffer[MagickPathExtent],
   1451             *value;
   1452 
   1453           value=(char *) NULL;
   1454           *buffer='\0';
   1455           switch (format)
   1456           {
   1457             case EXIF_FMT_BYTE:
   1458             case EXIF_FMT_UNDEFINED:
   1459             {
   1460               EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
   1461               break;
   1462             }
   1463             case EXIF_FMT_SBYTE:
   1464             {
   1465               EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
   1466               break;
   1467             }
   1468             case EXIF_FMT_SSHORT:
   1469             {
   1470               EXIFMultipleValues(2,"%hd",ReadPropertySignedShort(endian,p1));
   1471               break;
   1472             }
   1473             case EXIF_FMT_USHORT:
   1474             {
   1475               EXIFMultipleValues(2,"%hu",ReadPropertyUnsignedShort(endian,p1));
   1476               break;
   1477             }
   1478             case EXIF_FMT_ULONG:
   1479             {
   1480               EXIFMultipleValues(4,"%.20g",(double)
   1481                 ReadPropertyUnsignedLong(endian,p1));
   1482               break;
   1483             }
   1484             case EXIF_FMT_SLONG:
   1485             {
   1486               EXIFMultipleValues(4,"%.20g",(double)
   1487                 ReadPropertySignedLong(endian,p1));
   1488               break;
   1489             }
   1490             case EXIF_FMT_URATIONAL:
   1491             {
   1492               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
   1493                 ReadPropertyUnsignedLong(endian,p1),(double)
   1494                 ReadPropertyUnsignedLong(endian,p1+4));
   1495               break;
   1496             }
   1497             case EXIF_FMT_SRATIONAL:
   1498             {
   1499               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
   1500                 ReadPropertySignedLong(endian,p1),(double)
   1501                 ReadPropertySignedLong(endian,p1+4));
   1502               break;
   1503             }
   1504             case EXIF_FMT_SINGLE:
   1505             {
   1506               EXIFMultipleValues(4,"%f",(double) *(float *) p1);
   1507               break;
   1508             }
   1509             case EXIF_FMT_DOUBLE:
   1510             {
   1511               EXIFMultipleValues(8,"%f",*(double *) p1);
   1512               break;
   1513             }
   1514             default:
   1515             case EXIF_FMT_STRING:
   1516             {
   1517               value=(char *) NULL;
   1518               if (~((size_t) number_bytes) >= 1)
   1519                 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
   1520                   sizeof(*value));
   1521               if (value != (char *) NULL)
   1522                 {
   1523                   register ssize_t
   1524                     i;
   1525 
   1526                   for (i=0; i < (ssize_t) number_bytes; i++)
   1527                   {
   1528                     value[i]='.';
   1529                     if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
   1530                       value[i]=(char) p[i];
   1531                   }
   1532                   value[i]='\0';
   1533                 }
   1534               break;
   1535             }
   1536           }
   1537           if (value != (char *) NULL)
   1538             {
   1539               char
   1540                 *key;
   1541 
   1542               register const char
   1543                 *p;
   1544 
   1545               key=AcquireString(property);
   1546               switch (all)
   1547               {
   1548                 case 1:
   1549                 {
   1550                   const char
   1551                     *description;
   1552 
   1553                   register ssize_t
   1554                     i;
   1555 
   1556                   description="unknown";
   1557                   for (i=0; ; i++)
   1558                   {
   1559                     if (EXIFTag[i].tag == 0)
   1560                       break;
   1561                     if (EXIFTag[i].tag == tag_value)
   1562                       {
   1563                         description=EXIFTag[i].description;
   1564                         break;
   1565                       }
   1566                   }
   1567                   (void) FormatLocaleString(key,MagickPathExtent,"%s",
   1568                     description);
   1569                   if (level == 2)
   1570                     (void) SubstituteString(&key,"exif:","exif:thumbnail:");
   1571                   break;
   1572                 }
   1573                 case 2:
   1574                 {
   1575                   if (tag_value < 0x10000)
   1576                     (void) FormatLocaleString(key,MagickPathExtent,"#%04lx",
   1577                       (unsigned long) tag_value);
   1578                   else
   1579                     if (tag_value < 0x20000)
   1580                       (void) FormatLocaleString(key,MagickPathExtent,"@%04lx",
   1581                         (unsigned long) (tag_value & 0xffff));
   1582                     else
   1583                       (void) FormatLocaleString(key,MagickPathExtent,"unknown");
   1584                   break;
   1585                 }
   1586                 default:
   1587                 {
   1588                   if (level == 2)
   1589                     (void) SubstituteString(&key,"exif:","exif:thumbnail:");
   1590                 }
   1591               }
   1592               p=(const char *) NULL;
   1593               if (image->properties != (void *) NULL)
   1594                 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
   1595                   image->properties,key);
   1596               if (p == (const char *) NULL)
   1597                 (void) SetImageProperty((Image *) image,key,value,exception);
   1598               value=DestroyString(value);
   1599               key=DestroyString(key);
   1600               status=MagickTrue;
   1601             }
   1602         }
   1603         if ((tag_value == TAG_EXIF_OFFSET) ||
   1604             (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
   1605           {
   1606             ssize_t
   1607               offset;
   1608 
   1609             offset=(ssize_t) ReadPropertySignedLong(endian,p);
   1610             if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
   1611               {
   1612                 ssize_t
   1613                   tag_offset1;
   1614 
   1615                 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
   1616                   0);
   1617                 directory_stack[level].directory=directory;
   1618                 entry++;
   1619                 directory_stack[level].entry=entry;
   1620                 directory_stack[level].offset=tag_offset;
   1621                 level++;
   1622                 directory_stack[level].directory=exif+offset;
   1623                 directory_stack[level].offset=tag_offset1;
   1624                 directory_stack[level].entry=0;
   1625                 level++;
   1626                 if ((directory+2+(12*number_entries)) > (exif+length))
   1627                   break;
   1628                 offset=(ssize_t) ReadPropertySignedLong(endian,directory+2+(12*
   1629                   number_entries));
   1630                 if ((offset != 0) && ((size_t) offset < length) &&
   1631                     (level < (MaxDirectoryStack-2)))
   1632                   {
   1633                     directory_stack[level].directory=exif+offset;
   1634                     directory_stack[level].entry=0;
   1635                     directory_stack[level].offset=tag_offset1;
   1636                     level++;
   1637                   }
   1638               }
   1639             break;
   1640           }
   1641     }
   1642   } while (level > 0);
   1643   exif_resources=DestroySplayTree(exif_resources);
   1644   return(status);
   1645 }
   1646 
   1647 static MagickBooleanType GetICCProperty(const Image *image,const char *property,
   1648   ExceptionInfo *exception)
   1649 {
   1650   const StringInfo
   1651     *profile;
   1652 
   1653   magick_unreferenced(property);
   1654 
   1655   profile=GetImageProfile(image,"icc");
   1656   if (profile == (StringInfo *) NULL)
   1657     profile=GetImageProfile(image,"icm");
   1658   if (profile == (StringInfo *) NULL)
   1659     return(MagickFalse);
   1660   if (GetStringInfoLength(profile) < 128)
   1661     return(MagickFalse);  /* minimum ICC profile length */
   1662 #if defined(MAGICKCORE_LCMS_DELEGATE)
   1663   {
   1664     cmsHPROFILE
   1665       icc_profile;
   1666 
   1667     icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
   1668       (cmsUInt32Number) GetStringInfoLength(profile));
   1669     if (icc_profile != (cmsHPROFILE *) NULL)
   1670       {
   1671 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
   1672         const char
   1673           *name;
   1674 
   1675         name=cmsTakeProductName(icc_profile);
   1676         if (name != (const char *) NULL)
   1677           (void) SetImageProperty((Image *) image,"icc:name",name,exception);
   1678 #else
   1679         char
   1680           info[MagickPathExtent];
   1681 
   1682         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,"en","US",
   1683           info,MagickPathExtent);
   1684         (void) SetImageProperty((Image *) image,"icc:description",info,
   1685           exception);
   1686         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,"en","US",
   1687           info,MagickPathExtent);
   1688         (void) SetImageProperty((Image *) image,"icc:manufacturer",info,
   1689           exception);
   1690         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en","US",info,
   1691           MagickPathExtent);
   1692         (void) SetImageProperty((Image *) image,"icc:model",info,exception);
   1693         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,"en","US",
   1694           info,MagickPathExtent);
   1695         (void) SetImageProperty((Image *) image,"icc:copyright",info,exception);
   1696 #endif
   1697         (void) cmsCloseProfile(icc_profile);
   1698       }
   1699   }
   1700 #endif
   1701   return(MagickTrue);
   1702 }
   1703 
   1704 static MagickBooleanType SkipXMPValue(const char *value)
   1705 {
   1706   if (value == (const char*) NULL)
   1707     return(MagickTrue);
   1708   while (*value != '\0')
   1709   {
   1710     if (isspace((int) ((unsigned char) *value)) == 0)
   1711       return(MagickFalse);
   1712     value++;
   1713   }
   1714   return(MagickTrue);
   1715 }
   1716 
   1717 static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
   1718 {
   1719   char
   1720     *xmp_profile;
   1721 
   1722   const char
   1723     *content;
   1724 
   1725   const StringInfo
   1726     *profile;
   1727 
   1728   ExceptionInfo
   1729     *exception;
   1730 
   1731   MagickBooleanType
   1732     status;
   1733 
   1734   register const char
   1735     *p;
   1736 
   1737   XMLTreeInfo
   1738     *child,
   1739     *description,
   1740     *node,
   1741     *rdf,
   1742     *xmp;
   1743 
   1744   profile=GetImageProfile(image,"xmp");
   1745   if (profile == (StringInfo *) NULL)
   1746     return(MagickFalse);
   1747   if ((property == (const char *) NULL) || (*property == '\0'))
   1748     return(MagickFalse);
   1749   xmp_profile=StringInfoToString(profile);
   1750   if (xmp_profile == (char *) NULL)
   1751     return(MagickFalse);
   1752   for (p=xmp_profile; *p != '\0'; p++)
   1753     if ((*p == '<') && (*(p+1) == 'x'))
   1754       break;
   1755   exception=AcquireExceptionInfo();
   1756   xmp=NewXMLTree((char *) p,exception);
   1757   xmp_profile=DestroyString(xmp_profile);
   1758   exception=DestroyExceptionInfo(exception);
   1759   if (xmp == (XMLTreeInfo *) NULL)
   1760     return(MagickFalse);
   1761   status=MagickFalse;
   1762   rdf=GetXMLTreeChild(xmp,"rdf:RDF");
   1763   if (rdf != (XMLTreeInfo *) NULL)
   1764     {
   1765       if (image->properties == (void *) NULL)
   1766         ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
   1767           RelinquishMagickMemory,RelinquishMagickMemory);
   1768       description=GetXMLTreeChild(rdf,"rdf:Description");
   1769       while (description != (XMLTreeInfo *) NULL)
   1770       {
   1771         node=GetXMLTreeChild(description,(const char *) NULL);
   1772         while (node != (XMLTreeInfo *) NULL)
   1773         {
   1774           child=GetXMLTreeChild(node,(const char *) NULL);
   1775           content=GetXMLTreeContent(node);
   1776           if ((child == (XMLTreeInfo *) NULL) &&
   1777               (SkipXMPValue(content) == MagickFalse))
   1778             (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
   1779               ConstantString(GetXMLTreeTag(node)),ConstantString(content));
   1780           while (child != (XMLTreeInfo *) NULL)
   1781           {
   1782             content=GetXMLTreeContent(child);
   1783             if (SkipXMPValue(content) == MagickFalse)
   1784               (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
   1785                 ConstantString(GetXMLTreeTag(child)),ConstantString(content));
   1786             child=GetXMLTreeSibling(child);
   1787           }
   1788           node=GetXMLTreeSibling(node);
   1789         }
   1790         description=GetNextXMLTreeTag(description);
   1791       }
   1792     }
   1793   xmp=DestroyXMLTree(xmp);
   1794   return(status);
   1795 }
   1796 
   1797 static char *TracePSClippath(const unsigned char *blob,size_t length)
   1798 {
   1799   char
   1800     *path,
   1801     *message;
   1802 
   1803   MagickBooleanType
   1804     in_subpath;
   1805 
   1806   PointInfo
   1807     first[3],
   1808     last[3],
   1809     point[3];
   1810 
   1811   register ssize_t
   1812     i,
   1813     x;
   1814 
   1815   ssize_t
   1816     knot_count,
   1817     selector,
   1818     y;
   1819 
   1820   path=AcquireString((char *) NULL);
   1821   if (path == (char *) NULL)
   1822     return((char *) NULL);
   1823   message=AcquireString((char *) NULL);
   1824   (void) FormatLocaleString(message,MagickPathExtent,"/ClipImage\n");
   1825   (void) ConcatenateString(&path,message);
   1826   (void) FormatLocaleString(message,MagickPathExtent,"{\n");
   1827   (void) ConcatenateString(&path,message);
   1828   (void) FormatLocaleString(message,MagickPathExtent,
   1829     "  /c {curveto} bind def\n");
   1830   (void) ConcatenateString(&path,message);
   1831   (void) FormatLocaleString(message,MagickPathExtent,
   1832     "  /l {lineto} bind def\n");
   1833   (void) ConcatenateString(&path,message);
   1834   (void) FormatLocaleString(message,MagickPathExtent,
   1835     "  /m {moveto} bind def\n");
   1836   (void) ConcatenateString(&path,message);
   1837   (void) FormatLocaleString(message,MagickPathExtent,
   1838     "  /v {currentpoint 6 2 roll curveto} bind def\n");
   1839   (void) ConcatenateString(&path,message);
   1840   (void) FormatLocaleString(message,MagickPathExtent,
   1841     "  /y {2 copy curveto} bind def\n");
   1842   (void) ConcatenateString(&path,message);
   1843   (void) FormatLocaleString(message,MagickPathExtent,
   1844     "  /z {closepath} bind def\n");
   1845   (void) ConcatenateString(&path,message);
   1846   (void) FormatLocaleString(message,MagickPathExtent,"  newpath\n");
   1847   (void) ConcatenateString(&path,message);
   1848   /*
   1849     The clipping path format is defined in "Adobe Photoshop File Formats
   1850     Specification" version 6.0 downloadable from adobe.com.
   1851   */
   1852   (void) ResetMagickMemory(point,0,sizeof(point));
   1853   (void) ResetMagickMemory(first,0,sizeof(first));
   1854   (void) ResetMagickMemory(last,0,sizeof(last));
   1855   knot_count=0;
   1856   in_subpath=MagickFalse;
   1857   while (length > 0)
   1858   {
   1859     selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
   1860     switch (selector)
   1861     {
   1862       case 0:
   1863       case 3:
   1864       {
   1865         if (knot_count != 0)
   1866           {
   1867             blob+=24;
   1868             length-=MagickMin(24,(ssize_t) length);
   1869             break;
   1870           }
   1871         /*
   1872           Expected subpath length record.
   1873         */
   1874         knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
   1875         blob+=22;
   1876         length-=MagickMin(22,(ssize_t) length);
   1877         break;
   1878       }
   1879       case 1:
   1880       case 2:
   1881       case 4:
   1882       case 5:
   1883       {
   1884         if (knot_count == 0)
   1885           {
   1886             /*
   1887               Unexpected subpath knot
   1888             */
   1889             blob+=24;
   1890             length-=MagickMin(24,(ssize_t) length);
   1891             break;
   1892           }
   1893         /*
   1894           Add sub-path knot
   1895         */
   1896         for (i=0; i < 3; i++)
   1897         {
   1898           size_t
   1899             xx,
   1900             yy;
   1901 
   1902           yy=(size_t) ReadPropertyMSBLong(&blob,&length);
   1903           xx=(size_t) ReadPropertyMSBLong(&blob,&length);
   1904           x=(ssize_t) xx;
   1905           if (xx > 2147483647)
   1906             x=(ssize_t) xx-4294967295U-1;
   1907           y=(ssize_t) yy;
   1908           if (yy > 2147483647)
   1909             y=(ssize_t) yy-4294967295U-1;
   1910           point[i].x=(double) x/4096/4096;
   1911           point[i].y=1.0-(double) y/4096/4096;
   1912         }
   1913         if (in_subpath == MagickFalse)
   1914           {
   1915             (void) FormatLocaleString(message,MagickPathExtent,"  %g %g m\n",
   1916               point[1].x,point[1].y);
   1917             for (i=0; i < 3; i++)
   1918             {
   1919               first[i]=point[i];
   1920               last[i]=point[i];
   1921             }
   1922           }
   1923         else
   1924           {
   1925             /*
   1926               Handle special cases when Bezier curves are used to describe
   1927               corners and straight lines.
   1928             */
   1929             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
   1930                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
   1931               (void) FormatLocaleString(message,MagickPathExtent,
   1932                 "  %g %g l\n",point[1].x,point[1].y);
   1933             else
   1934               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
   1935                 (void) FormatLocaleString(message,MagickPathExtent,
   1936                   "  %g %g %g %g v\n",point[0].x,point[0].y,
   1937                   point[1].x,point[1].y);
   1938               else
   1939                 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
   1940                   (void) FormatLocaleString(message,MagickPathExtent,
   1941                     "  %g %g %g %g y\n",last[2].x,last[2].y,
   1942                     point[1].x,point[1].y);
   1943                 else
   1944                   (void) FormatLocaleString(message,MagickPathExtent,
   1945                     "  %g %g %g %g %g %g c\n",last[2].x,
   1946                     last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
   1947             for (i=0; i < 3; i++)
   1948               last[i]=point[i];
   1949           }
   1950         (void) ConcatenateString(&path,message);
   1951         in_subpath=MagickTrue;
   1952         knot_count--;
   1953         /*
   1954           Close the subpath if there are no more knots.
   1955         */
   1956         if (knot_count == 0)
   1957           {
   1958             /*
   1959               Same special handling as above except we compare to the
   1960               first point in the path and close the path.
   1961             */
   1962             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
   1963                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
   1964               (void) FormatLocaleString(message,MagickPathExtent,
   1965                 "  %g %g l z\n",first[1].x,first[1].y);
   1966             else
   1967               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
   1968                 (void) FormatLocaleString(message,MagickPathExtent,
   1969                   "  %g %g %g %g v z\n",first[0].x,first[0].y,
   1970                   first[1].x,first[1].y);
   1971               else
   1972                 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
   1973                   (void) FormatLocaleString(message,MagickPathExtent,
   1974                     "  %g %g %g %g y z\n",last[2].x,last[2].y,
   1975                     first[1].x,first[1].y);
   1976                 else
   1977                   (void) FormatLocaleString(message,MagickPathExtent,
   1978                     "  %g %g %g %g %g %g c z\n",last[2].x,
   1979                     last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
   1980             (void) ConcatenateString(&path,message);
   1981             in_subpath=MagickFalse;
   1982           }
   1983         break;
   1984       }
   1985       case 6:
   1986       case 7:
   1987       case 8:
   1988       default:
   1989       {
   1990         blob+=24;
   1991         length-=MagickMin(24,(ssize_t) length);
   1992         break;
   1993       }
   1994     }
   1995   }
   1996   /*
   1997     Returns an empty PS path if the path has no knots.
   1998   */
   1999   (void) FormatLocaleString(message,MagickPathExtent,"  eoclip\n");
   2000   (void) ConcatenateString(&path,message);
   2001   (void) FormatLocaleString(message,MagickPathExtent,"} bind def");
   2002   (void) ConcatenateString(&path,message);
   2003   message=DestroyString(message);
   2004   return(path);
   2005 }
   2006 
   2007 static char *TraceSVGClippath(const unsigned char *blob,size_t length,
   2008   const size_t columns,const size_t rows)
   2009 {
   2010   char
   2011     *path,
   2012     *message;
   2013 
   2014   MagickBooleanType
   2015     in_subpath;
   2016 
   2017   PointInfo
   2018     first[3],
   2019     last[3],
   2020     point[3];
   2021 
   2022   register ssize_t
   2023     i;
   2024 
   2025   ssize_t
   2026     knot_count,
   2027     selector,
   2028     x,
   2029     y;
   2030 
   2031   path=AcquireString((char *) NULL);
   2032   if (path == (char *) NULL)
   2033     return((char *) NULL);
   2034   message=AcquireString((char *) NULL);
   2035   (void) FormatLocaleString(message,MagickPathExtent,(
   2036     "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
   2037     "<svg xmlns=\"http://www.w3.org/2000/svg\""
   2038     " width=\"%.20g\" height=\"%.20g\">\n"
   2039     "<g>\n"
   2040     "<path fill-rule=\"evenodd\" style=\"fill:#00000000;stroke:#00000000;"
   2041     "stroke-width:0;stroke-antialiasing:false\" d=\"\n"),(double) columns,
   2042     (double) rows);
   2043   (void) ConcatenateString(&path,message);
   2044   (void) ResetMagickMemory(point,0,sizeof(point));
   2045   (void) ResetMagickMemory(first,0,sizeof(first));
   2046   (void) ResetMagickMemory(last,0,sizeof(last));
   2047   knot_count=0;
   2048   in_subpath=MagickFalse;
   2049   while (length != 0)
   2050   {
   2051     selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
   2052     switch (selector)
   2053     {
   2054       case 0:
   2055       case 3:
   2056       {
   2057         if (knot_count != 0)
   2058           {
   2059             blob+=24;
   2060             length-=MagickMin(24,(ssize_t) length);
   2061             break;
   2062           }
   2063         /*
   2064           Expected subpath length record.
   2065         */
   2066         knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
   2067         blob+=22;
   2068         length-=MagickMin(22,(ssize_t) length);
   2069         break;
   2070       }
   2071       case 1:
   2072       case 2:
   2073       case 4:
   2074       case 5:
   2075       {
   2076         if (knot_count == 0)
   2077           {
   2078             /*
   2079               Unexpected subpath knot.
   2080             */
   2081             blob+=24;
   2082             length-=MagickMin(24,(ssize_t) length);
   2083             break;
   2084           }
   2085         /*
   2086           Add sub-path knot
   2087         */
   2088         for (i=0; i < 3; i++)
   2089         {
   2090           unsigned int
   2091             xx,
   2092             yy;
   2093 
   2094           yy=(unsigned int) ReadPropertyMSBLong(&blob,&length);
   2095           xx=(unsigned int) ReadPropertyMSBLong(&blob,&length);
   2096           x=(ssize_t) xx;
   2097           if (xx > 2147483647)
   2098             x=(ssize_t) xx-4294967295U-1;
   2099           y=(ssize_t) yy;
   2100           if (yy > 2147483647)
   2101             y=(ssize_t) yy-4294967295U-1;
   2102           point[i].x=(double) x*columns/4096/4096;
   2103           point[i].y=(double) y*rows/4096/4096;
   2104         }
   2105         if (in_subpath == MagickFalse)
   2106           {
   2107             (void) FormatLocaleString(message,MagickPathExtent,"M %g %g\n",
   2108               point[1].x,point[1].y);
   2109             for (i=0; i < 3; i++)
   2110             {
   2111               first[i]=point[i];
   2112               last[i]=point[i];
   2113             }
   2114           }
   2115         else
   2116           {
   2117             /*
   2118               Handle special cases when Bezier curves are used to describe
   2119               corners and straight lines.
   2120             */
   2121             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
   2122                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
   2123               (void) FormatLocaleString(message,MagickPathExtent,
   2124                 "L %g %g\n",point[1].x,point[1].y);
   2125             else
   2126               (void) FormatLocaleString(message,MagickPathExtent,
   2127                 "C %g %g %g %g %g %g\n",last[2].x,
   2128                 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
   2129             for (i=0; i < 3; i++)
   2130               last[i]=point[i];
   2131           }
   2132         (void) ConcatenateString(&path,message);
   2133         in_subpath=MagickTrue;
   2134         knot_count--;
   2135         /*
   2136           Close the subpath if there are no more knots.
   2137         */
   2138         if (knot_count == 0)
   2139           {
   2140            /*
   2141               Same special handling as above except we compare to the
   2142               first point in the path and close the path.
   2143             */
   2144             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
   2145                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
   2146               (void) FormatLocaleString(message,MagickPathExtent,
   2147                 "L %g %g Z\n",first[1].x,first[1].y);
   2148             else
   2149               (void) FormatLocaleString(message,MagickPathExtent,
   2150                 "C %g %g %g %g %g %g Z\n",last[2].x,
   2151                 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
   2152             (void) ConcatenateString(&path,message);
   2153             in_subpath=MagickFalse;
   2154           }
   2155         break;
   2156       }
   2157       case 6:
   2158       case 7:
   2159       case 8:
   2160       default:
   2161       {
   2162         blob+=24;
   2163         length-=MagickMin(24,(ssize_t) length);
   2164         break;
   2165       }
   2166     }
   2167   }
   2168   /*
   2169     Return an empty SVG image if the path does not have knots.
   2170   */
   2171   (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n");
   2172   message=DestroyString(message);
   2173   return(path);
   2174 }
   2175 
   2176 MagickExport const char *GetImageProperty(const Image *image,
   2177   const char *property,ExceptionInfo *exception)
   2178 {
   2179   register const char
   2180     *p;
   2181 
   2182   assert(image != (Image *) NULL);
   2183   assert(image->signature == MagickCoreSignature);
   2184   if (image->debug != MagickFalse)
   2185     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2186   p=(const char *) NULL;
   2187   if (image->properties != (void *) NULL)
   2188     {
   2189       if (property == (const char *) NULL)
   2190         {
   2191           ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
   2192           p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
   2193             image->properties);
   2194           return(p);
   2195         }
   2196         p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
   2197           image->properties,property);
   2198         if (p != (const char *) NULL)
   2199           return(p);
   2200     }
   2201   if ((property == (const char *) NULL) ||
   2202       (strchr(property,':') == (char *) NULL))
   2203     return(p);
   2204   switch (*property)
   2205   {
   2206     case '8':
   2207     {
   2208       if (LocaleNCompare("8bim:",property,5) == 0)
   2209         {
   2210           (void) Get8BIMProperty(image,property,exception);
   2211           break;
   2212         }
   2213       break;
   2214     }
   2215     case 'E':
   2216     case 'e':
   2217     {
   2218       if (LocaleNCompare("exif:",property,5) == 0)
   2219         {
   2220           (void) GetEXIFProperty(image,property,exception);
   2221           break;
   2222         }
   2223       break;
   2224     }
   2225     case 'I':
   2226     case 'i':
   2227     {
   2228       if ((LocaleNCompare("icc:",property,4) == 0) ||
   2229           (LocaleNCompare("icm:",property,4) == 0))
   2230         {
   2231           (void) GetICCProperty(image,property,exception);
   2232           break;
   2233         }
   2234       if (LocaleNCompare("iptc:",property,5) == 0)
   2235         {
   2236           (void) GetIPTCProperty(image,property,exception);
   2237           break;
   2238         }
   2239       break;
   2240     }
   2241     case 'X':
   2242     case 'x':
   2243     {
   2244       if (LocaleNCompare("xmp:",property,4) == 0)
   2245         {
   2246           (void) GetXMPProperty(image,property);
   2247           break;
   2248         }
   2249       break;
   2250     }
   2251     default:
   2252       break;
   2253   }
   2254   if (image->properties != (void *) NULL)
   2255     {
   2256       p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
   2257         image->properties,property);
   2258       return(p);
   2259     }
   2260   return((const char *) NULL);
   2261 }
   2262 
   2263 /*
   2265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2266 %                                                                             %
   2267 %                                                                             %
   2268 %                                                                             %
   2269 +   G e t M a g i c k P r o p e r t y                                         %
   2270 %                                                                             %
   2271 %                                                                             %
   2272 %                                                                             %
   2273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2274 %
   2275 %  GetMagickProperty() gets attributes or calculated values that is associated
   2276 %  with a fixed known property name, or single letter property. It may be
   2277 %  called if no image is defined (IMv7), in which case only global image_info
   2278 %  values are available:
   2279 %
   2280 %    \n   newline
   2281 %    \r   carriage return
   2282 %    <    less-than character.
   2283 %    >    greater-than character.
   2284 %    &    ampersand character.
   2285 %    %%   a percent sign
   2286 %    %b   file size of image read in
   2287 %    %c   comment meta-data property
   2288 %    %d   directory component of path
   2289 %    %e   filename extension or suffix
   2290 %    %f   filename (including suffix)
   2291 %    %g   layer canvas page geometry   (equivalent to "%Wx%H%X%Y")
   2292 %    %h   current image height in pixels
   2293 %    %i   image filename (note: becomes output filename for "info:")
   2294 %    %k   CALCULATED: number of unique colors
   2295 %    %l   label meta-data property
   2296 %    %m   image file format (file magic)
   2297 %    %n   number of images in current image sequence
   2298 %    %o   output filename  (used for delegates)
   2299 %    %p   index of image in current image list
   2300 %    %q   quantum depth (compile-time constant)
   2301 %    %r   image class and colorspace
   2302 %    %s   scene number (from input unless re-assigned)
   2303 %    %t   filename without directory or extension (suffix)
   2304 %    %u   unique temporary filename (used for delegates)
   2305 %    %w   current width in pixels
   2306 %    %x   x resolution (density)
   2307 %    %y   y resolution (density)
   2308 %    %z   image depth (as read in unless modified, image save depth)
   2309 %    %A   image transparency channel enabled (true/false)
   2310 %    %C   image compression type
   2311 %    %D   image GIF dispose method
   2312 %    %G   original image size (%wx%h; before any resizes)
   2313 %    %H   page (canvas) height
   2314 %    %M   Magick filename (original file exactly as given,  including read mods)
   2315 %    %O   page (canvas) offset ( = %X%Y )
   2316 %    %P   page (canvas) size ( = %Wx%H )
   2317 %    %Q   image compression quality ( 0 = default )
   2318 %    %S   ?? scenes ??
   2319 %    %T   image time delay (in centi-seconds)
   2320 %    %U   image resolution units
   2321 %    %W   page (canvas) width
   2322 %    %X   page (canvas) x offset (including sign)
   2323 %    %Y   page (canvas) y offset (including sign)
   2324 %    %Z   unique filename (used for delegates)
   2325 %    %@   CALCULATED: trim bounding box (without actually trimming)
   2326 %    %#   CALCULATED: 'signature' hash of image values
   2327 %
   2328 %  This routine only handles specifically known properties.  It does not
   2329 %  handle special prefixed properties, profiles, or expressions. Nor does
   2330 %  it return any free-form property strings.
   2331 %
   2332 %  The returned string is stored in a structure somewhere, and should not be
   2333 %  directly freed.  If the string was generated (common) the string will be
   2334 %  stored as as either as artifact or option 'get-property'.  These may be
   2335 %  deleted (cleaned up) when no longer required, but neither artifact or
   2336 %  option is guranteed to exist.
   2337 %
   2338 %  The format of the GetMagickProperty method is:
   2339 %
   2340 %      const char *GetMagickProperty(ImageInfo *image_info,Image *image,
   2341 %        const char *property,ExceptionInfo *exception)
   2342 %
   2343 %  A description of each parameter follows:
   2344 %
   2345 %    o image_info: the image info (optional)
   2346 %
   2347 %    o image: the image (optional)
   2348 %
   2349 %    o key: the key.
   2350 %
   2351 %    o exception: return any errors or warnings in this structure.
   2352 %
   2353 */
   2354 static const char *GetMagickPropertyLetter(ImageInfo *image_info,
   2355   Image *image,const char letter,ExceptionInfo *exception)
   2356 {
   2357 #define WarnNoImageReturn(format,arg) \
   2358   if (image == (Image *) NULL ) { \
   2359     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
   2360       "NoImageForProperty",format,arg); \
   2361     return((const char *) NULL); \
   2362   }
   2363 #define WarnNoImageInfoReturn(format,arg) \
   2364   if (image_info == (ImageInfo *) NULL ) { \
   2365     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
   2366       "NoImageInfoForProperty",format,arg); \
   2367     return((const char *) NULL); \
   2368   }
   2369 
   2370   char
   2371     value[MagickPathExtent];  /* formatted string to store as an artifact */
   2372 
   2373   const char
   2374     *string;     /* return a string already stored somewher */
   2375 
   2376   if ((image != (Image *) NULL) && (image->debug != MagickFalse))
   2377     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2378   else
   2379     if ((image_info != (ImageInfo *) NULL) &&
   2380         (image_info->debug != MagickFalse))
   2381     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
   2382   *value='\0';           /* formatted string */
   2383   string=(char *) NULL;  /* constant string reference */
   2384   /*
   2385     Get properities that are directly defined by images.
   2386   */
   2387   switch (letter)
   2388   {
   2389     case 'b':  /* image size read in - in bytes */
   2390     {
   2391       WarnNoImageReturn("\"%%%c\"",letter);
   2392       (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
   2393         value);
   2394       if (image->extent == 0)
   2395         (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
   2396           MagickPathExtent,value);
   2397       break;
   2398     }
   2399     case 'c':  /* image comment property - empty string by default */
   2400     {
   2401       WarnNoImageReturn("\"%%%c\"",letter);
   2402       string=GetImageProperty(image,"comment",exception);
   2403       if ( string == (const char *) NULL )
   2404         string="";
   2405       break;
   2406     }
   2407     case 'd':  /* Directory component of filename */
   2408     {
   2409       WarnNoImageReturn("\"%%%c\"",letter);
   2410       GetPathComponent(image->magick_filename,HeadPath,value);
   2411       if (*value == '\0')
   2412         string="";
   2413       break;
   2414     }
   2415     case 'e': /* Filename extension (suffix) of image file */
   2416     {
   2417       WarnNoImageReturn("\"%%%c\"",letter);
   2418       GetPathComponent(image->magick_filename,ExtensionPath,value);
   2419       if (*value == '\0')
   2420         string="";
   2421       break;
   2422     }
   2423     case 'f': /* Filename without directory component */
   2424     {
   2425       WarnNoImageReturn("\"%%%c\"",letter);
   2426       GetPathComponent(image->magick_filename,TailPath,value);
   2427       if (*value == '\0')
   2428         string="";
   2429       break;
   2430     }
   2431     case 'g': /* Image geometry, canvas and offset  %Wx%H+%X+%Y */
   2432     {
   2433       WarnNoImageReturn("\"%%%c\"",letter);
   2434       (void) FormatLocaleString(value,MagickPathExtent,
   2435         "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
   2436         image->page.height,(double) image->page.x,(double) image->page.y);
   2437       break;
   2438     }
   2439     case 'h': /* Image height (current) */
   2440     {
   2441       WarnNoImageReturn("\"%%%c\"",letter);
   2442       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2443         (image->rows != 0 ? image->rows : image->magick_rows));
   2444       break;
   2445     }
   2446     case 'i': /* Filename last used for an image (read or write) */
   2447     {
   2448       WarnNoImageReturn("\"%%%c\"",letter);
   2449       string=image->filename;
   2450       break;
   2451     }
   2452     case 'k': /* Number of unique colors  */
   2453     {
   2454       /*
   2455         FUTURE: ensure this does not generate the formatted comment!
   2456       */
   2457       WarnNoImageReturn("\"%%%c\"",letter);
   2458       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2459         GetNumberColors(image,(FILE *) NULL,exception));
   2460       break;
   2461     }
   2462     case 'l': /* Image label property - empty string by default */
   2463     {
   2464       WarnNoImageReturn("\"%%%c\"",letter);
   2465       string=GetImageProperty(image,"label",exception);
   2466       if (string == (const char *) NULL)
   2467         string="";
   2468       break;
   2469     }
   2470     case 'm': /* Image format (file magick) */
   2471     {
   2472       WarnNoImageReturn("\"%%%c\"",letter);
   2473       string=image->magick;
   2474       break;
   2475     }
   2476     case 'n': /* Number of images in the list.  */
   2477     {
   2478       if ( image != (Image *) NULL )
   2479         (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2480           GetImageListLength(image));
   2481       else
   2482         string="0";    /* no images or scenes */
   2483       break;
   2484     }
   2485     case 'o': /* Output Filename - for delegate use only */
   2486       WarnNoImageInfoReturn("\"%%%c\"",letter);
   2487       string=image_info->filename;
   2488       break;
   2489     case 'p': /* Image index in current image list */
   2490     {
   2491       WarnNoImageReturn("\"%%%c\"",letter);
   2492       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2493         GetImageIndexInList(image));
   2494       break;
   2495     }
   2496     case 'q': /* Quantum depth of image in memory */
   2497     {
   2498       WarnNoImageReturn("\"%%%c\"",letter);
   2499       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2500         MAGICKCORE_QUANTUM_DEPTH);
   2501       break;
   2502     }
   2503     case 'r': /* Image storage class, colorspace, and alpha enabled.  */
   2504     {
   2505       ColorspaceType
   2506         colorspace;
   2507 
   2508       WarnNoImageReturn("\"%%%c\"",letter);
   2509       colorspace=image->colorspace;
   2510       if (SetImageGray(image,exception) != MagickFalse)
   2511         colorspace=GRAYColorspace;   /* FUTURE: this is IMv6 not IMv7 */
   2512       (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s",
   2513         CommandOptionToMnemonic(MagickClassOptions,(ssize_t)
   2514         image->storage_class),CommandOptionToMnemonic(MagickColorspaceOptions,
   2515         (ssize_t) colorspace),image->alpha_trait != UndefinedPixelTrait ?
   2516         "Alpha" : "");
   2517       break;
   2518     }
   2519     case 's': /* Image scene number */
   2520     {
   2521 #if 0  /* this seems non-sensical -- simplifing */
   2522       if (image_info->number_scenes != 0)
   2523         (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2524           image_info->scene);
   2525       else if (image != (Image *) NULL)
   2526         (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2527           image->scene);
   2528       else
   2529           string="0";
   2530 #else
   2531       WarnNoImageReturn("\"%%%c\"",letter);
   2532       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2533          image->scene);
   2534 #endif
   2535       break;
   2536     }
   2537     case 't': /* Base filename without directory or extention */
   2538     {
   2539       WarnNoImageReturn("\"%%%c\"",letter);
   2540       GetPathComponent(image->magick_filename,BasePath,value);
   2541       if (*value == '\0')
   2542         string="";
   2543       break;
   2544     }
   2545     case 'u': /* Unique filename */
   2546     {
   2547       WarnNoImageInfoReturn("\"%%%c\"",letter);
   2548       string=image_info->unique;
   2549       break;
   2550     }
   2551     case 'w': /* Image width (current) */
   2552     {
   2553       WarnNoImageReturn("\"%%%c\"",letter);
   2554       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2555         (image->columns != 0 ? image->columns : image->magick_columns));
   2556       break;
   2557     }
   2558     case 'x': /* Image horizontal resolution (with units) */
   2559     {
   2560       WarnNoImageReturn("\"%%%c\"",letter);
   2561       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
   2562         fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0);
   2563       break;
   2564     }
   2565     case 'y': /* Image vertical resolution (with units) */
   2566     {
   2567       WarnNoImageReturn("\"%%%c\"",letter);
   2568       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
   2569         fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0);
   2570       break;
   2571     }
   2572     case 'z': /* Image depth as read in */
   2573     {
   2574       WarnNoImageReturn("\"%%%c\"",letter);
   2575       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2576         image->depth);
   2577       break;
   2578     }
   2579     case 'A': /* Image alpha channel  */
   2580     {
   2581       WarnNoImageReturn("\"%%%c\"",letter);
   2582       string=CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t)
   2583         image->alpha_trait);
   2584       break;
   2585     }
   2586     case 'C': /* Image compression method.  */
   2587     {
   2588       WarnNoImageReturn("\"%%%c\"",letter);
   2589       string=CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
   2590         image->compression);
   2591       break;
   2592     }
   2593     case 'D': /* Image dispose method.  */
   2594     {
   2595       WarnNoImageReturn("\"%%%c\"",letter);
   2596       string=CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t)
   2597         image->dispose);
   2598       break;
   2599     }
   2600     case 'G': /* Image size as geometry = "%wx%h" */
   2601     {
   2602       WarnNoImageReturn("\"%%%c\"",letter);
   2603       (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",(double)
   2604         image->magick_columns,(double) image->magick_rows);
   2605       break;
   2606     }
   2607     case 'H': /* layer canvas height */
   2608     {
   2609       WarnNoImageReturn("\"%%%c\"",letter);
   2610       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2611         image->page.height);
   2612       break;
   2613     }
   2614     case 'M': /* Magick filename - filename given incl. coder & read mods */
   2615     {
   2616       WarnNoImageReturn("\"%%%c\"",letter);
   2617       string=image->magick_filename;
   2618       break;
   2619     }
   2620     case 'O': /* layer canvas offset with sign = "+%X+%Y" */
   2621     {
   2622       WarnNoImageReturn("\"%%%c\"",letter);
   2623       (void) FormatLocaleString(value,MagickPathExtent,"%+ld%+ld",(long)
   2624         image->page.x,(long) image->page.y);
   2625       break;
   2626     }
   2627     case 'P': /* layer canvas page size = "%Wx%H" */
   2628     {
   2629       WarnNoImageReturn("\"%%%c\"",letter);
   2630       (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",(double)
   2631         image->page.width,(double) image->page.height);
   2632       break;
   2633     }
   2634     case 'Q': /* image compression quality */
   2635     {
   2636       WarnNoImageReturn("\"%%%c\"",letter);
   2637       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2638         (image->quality == 0 ? 92 : image->quality));
   2639       break;
   2640     }
   2641     case 'S': /* Number of scenes in image list.  */
   2642     {
   2643       WarnNoImageInfoReturn("\"%%%c\"",letter);
   2644 #if 0 /* What is this number? -- it makes no sense - simplifing */
   2645       if (image_info->number_scenes == 0)
   2646          string="2147483647";
   2647       else if ( image != (Image *) NULL )
   2648         (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2649                 image_info->scene+image_info->number_scenes);
   2650       else
   2651         string="0";
   2652 #else
   2653       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2654         (image_info->number_scenes == 0 ? 2147483647 :
   2655          image_info->number_scenes));
   2656 #endif
   2657       break;
   2658     }
   2659     case 'T': /* image time delay for animations */
   2660     {
   2661       WarnNoImageReturn("\"%%%c\"",letter);
   2662       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2663         image->delay);
   2664       break;
   2665     }
   2666     case 'U': /* Image resolution units. */
   2667     {
   2668       WarnNoImageReturn("\"%%%c\"",letter);
   2669       string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
   2670         image->units);
   2671       break;
   2672     }
   2673     case 'W': /* layer canvas width */
   2674     {
   2675       WarnNoImageReturn("\"%%%c\"",letter);
   2676       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2677         image->page.width);
   2678       break;
   2679     }
   2680     case 'X': /* layer canvas X offset */
   2681     {
   2682       WarnNoImageReturn("\"%%%c\"",letter);
   2683       (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
   2684         image->page.x);
   2685       break;
   2686     }
   2687     case 'Y': /* layer canvas Y offset */
   2688     {
   2689       WarnNoImageReturn("\"%%%c\"",letter);
   2690       (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
   2691         image->page.y);
   2692       break;
   2693     }
   2694     case '%': /* percent escaped */
   2695     {
   2696       string="%";
   2697       break;
   2698     }
   2699     case '@': /* Trim bounding box, without actually Trimming! */
   2700     {
   2701       RectangleInfo
   2702         page;
   2703 
   2704       WarnNoImageReturn("\"%%%c\"",letter);
   2705       page=GetImageBoundingBox(image,exception);
   2706       (void) FormatLocaleString(value,MagickPathExtent,
   2707         "%.20gx%.20g%+.20g%+.20g",(double) page.width,(double) page.height,
   2708         (double) page.x,(double)page.y);
   2709       break;
   2710     }
   2711     case '#':
   2712     {
   2713       /*
   2714         Image signature.
   2715       */
   2716       WarnNoImageReturn("\"%%%c\"",letter);
   2717       (void) SignatureImage(image,exception);
   2718       string=GetImageProperty(image,"signature",exception);
   2719       break;
   2720     }
   2721   }
   2722   if (string != (char *) NULL)
   2723     return(string);
   2724   if (*value != '\0')
   2725     {
   2726       /*
   2727         Create a cloned copy of result.
   2728       */
   2729       if (image != (Image *) NULL)
   2730         {
   2731           (void) SetImageArtifact(image,"get-property",value);
   2732           return(GetImageArtifact(image,"get-property"));
   2733         }
   2734       else
   2735         {
   2736           (void) SetImageOption(image_info,"get-property",value);
   2737           return(GetImageOption(image_info,"get-property"));
   2738         }
   2739     }
   2740   return((char *) NULL);
   2741 }
   2742 
   2743 MagickExport const char *GetMagickProperty(ImageInfo *image_info,
   2744   Image *image,const char *property,ExceptionInfo *exception)
   2745 {
   2746   char
   2747     value[MagickPathExtent];
   2748 
   2749   const char
   2750     *string;
   2751 
   2752   assert(property[0] != '\0');
   2753   assert(image != (Image *) NULL || image_info != (ImageInfo *) NULL );
   2754   if (property[1] == '\0')  /* single letter property request */
   2755     return(GetMagickPropertyLetter(image_info,image,*property,exception));
   2756   if ((image != (Image *) NULL) && (image->debug != MagickFalse))
   2757     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2758   else
   2759     if ((image_info != (ImageInfo *) NULL) &&
   2760         (image_info->debug != MagickFalse))
   2761     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
   2762   *value='\0';           /* formated string */
   2763   string=(char *) NULL;  /* constant string reference */
   2764   switch (*property)
   2765   {
   2766     case 'b':
   2767     {
   2768       if (LocaleCompare("basename",property) == 0)
   2769         {
   2770           WarnNoImageReturn("\"%%[%s]\"",property);
   2771           GetPathComponent(image->magick_filename,BasePath,value);
   2772           if (*value == '\0')
   2773             string="";
   2774           break;
   2775         }
   2776       if (LocaleCompare("bit-depth",property) == 0)
   2777         {
   2778           (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2779             GetImageDepth(image,exception));
   2780           break;
   2781         }
   2782       break;
   2783     }
   2784     case 'c':
   2785     {
   2786       if (LocaleCompare("channels",property) == 0)
   2787         {
   2788           WarnNoImageReturn("\"%%[%s]\"",property);
   2789           /* FUTURE: return actual image channels */
   2790           (void) FormatLocaleString(value,MagickPathExtent,"%s",
   2791             CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
   2792             image->colorspace));
   2793           LocaleLower(value);
   2794           if( image->alpha_trait != UndefinedPixelTrait )
   2795             (void) ConcatenateMagickString(value,"a",MagickPathExtent);
   2796           break;
   2797         }
   2798       if (LocaleCompare("colorspace",property) == 0)
   2799         {
   2800           WarnNoImageReturn("\"%%[%s]\"",property);
   2801           /* FUTURE: return actual colorspace - no 'gray' stuff */
   2802           string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
   2803             image->colorspace);
   2804           break;
   2805         }
   2806       if (LocaleCompare("compose",property) == 0)
   2807         {
   2808           WarnNoImageReturn("\"%%[%s]\"",property);
   2809           string=CommandOptionToMnemonic(MagickComposeOptions,(ssize_t)
   2810             image->compose);
   2811           break;
   2812         }
   2813       if (LocaleCompare("copyright",property) == 0)
   2814         {
   2815           (void) CopyMagickString(value,GetMagickCopyright(),MagickPathExtent);
   2816           break;
   2817         }
   2818       break;
   2819     }
   2820     case 'd':
   2821     {
   2822       if (LocaleCompare("depth",property) == 0)
   2823         {
   2824           WarnNoImageReturn("\"%%[%s]\"",property);
   2825           (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   2826             image->depth);
   2827           break;
   2828         }
   2829       if (LocaleCompare("directory",property) == 0)
   2830         {
   2831           WarnNoImageReturn("\"%%[%s]\"",property);
   2832           GetPathComponent(image->magick_filename,HeadPath,value);
   2833           if (*value == '\0')
   2834             string="";
   2835           break;
   2836         }
   2837       break;
   2838     }
   2839     case 'e':
   2840     {
   2841       if (LocaleCompare("entropy",property) == 0)
   2842         {
   2843           double
   2844             entropy;
   2845 
   2846           WarnNoImageReturn("\"%%[%s]\"",property);
   2847           (void) GetImageEntropy(image,&entropy,exception);
   2848           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   2849             GetMagickPrecision(),entropy);
   2850           break;
   2851         }
   2852       if (LocaleCompare("extension",property) == 0)
   2853         {
   2854           WarnNoImageReturn("\"%%[%s]\"",property);
   2855           GetPathComponent(image->magick_filename,ExtensionPath,value);
   2856           if (*value == '\0')
   2857             string="";
   2858           break;
   2859         }
   2860       break;
   2861     }
   2862     case 'g':
   2863     {
   2864       if (LocaleCompare("gamma",property) == 0)
   2865         {
   2866           WarnNoImageReturn("\"%%[%s]\"",property);
   2867           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   2868             GetMagickPrecision(),image->gamma);
   2869           break;
   2870         }
   2871       break;
   2872     }
   2873     case 'h':
   2874     {
   2875       if (LocaleCompare("height",property) == 0)
   2876         {
   2877           WarnNoImageReturn("\"%%[%s]\"",property);
   2878           (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
   2879             image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
   2880           break;
   2881         }
   2882       break;
   2883     }
   2884     case 'i':
   2885     {
   2886       if (LocaleCompare("input",property) == 0)
   2887         {
   2888           WarnNoImageReturn("\"%%[%s]\"",property);
   2889           string=image->filename;
   2890           break;
   2891         }
   2892       break;
   2893     }
   2894     case 'k':
   2895     {
   2896       if (LocaleCompare("kurtosis",property) == 0)
   2897         {
   2898           double
   2899             kurtosis,
   2900             skewness;
   2901 
   2902           WarnNoImageReturn("\"%%[%s]\"",property);
   2903           (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
   2904           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   2905             GetMagickPrecision(),kurtosis);
   2906           break;
   2907         }
   2908       break;
   2909     }
   2910     case 'm':
   2911     {
   2912       if (LocaleCompare("magick",property) == 0)
   2913         {
   2914           WarnNoImageReturn("\"%%[%s]\"",property);
   2915           string=image->magick;
   2916           break;
   2917         }
   2918       if ((LocaleCompare("maxima",property) == 0) ||
   2919           (LocaleCompare("max",property) == 0))
   2920         {
   2921           double
   2922             maximum,
   2923             minimum;
   2924 
   2925           WarnNoImageReturn("\"%%[%s]\"",property);
   2926           (void) GetImageRange(image,&minimum,&maximum,exception);
   2927           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   2928             GetMagickPrecision(),maximum);
   2929           break;
   2930         }
   2931       if (LocaleCompare("mean",property) == 0)
   2932         {
   2933           double
   2934             mean,
   2935             standard_deviation;
   2936 
   2937           WarnNoImageReturn("\"%%[%s]\"",property);
   2938           (void) GetImageMean(image,&mean,&standard_deviation,exception);
   2939           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   2940             GetMagickPrecision(),mean);
   2941           break;
   2942         }
   2943       if ((LocaleCompare("minima",property) == 0) ||
   2944           (LocaleCompare("min",property) == 0))
   2945         {
   2946           double
   2947             maximum,
   2948             minimum;
   2949 
   2950           WarnNoImageReturn("\"%%[%s]\"",property);
   2951           (void) GetImageRange(image,&minimum,&maximum,exception);
   2952           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   2953             GetMagickPrecision(),minimum);
   2954           break;
   2955         }
   2956       break;
   2957     }
   2958     case 'o':
   2959     {
   2960       if (LocaleCompare("opaque",property) == 0)
   2961         {
   2962           WarnNoImageReturn("\"%%[%s]\"",property);
   2963           string=CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t)
   2964             IsImageOpaque(image,exception));
   2965           break;
   2966         }
   2967       if (LocaleCompare("orientation",property) == 0)
   2968         {
   2969           WarnNoImageReturn("\"%%[%s]\"",property);
   2970           string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
   2971             image->orientation);
   2972           break;
   2973         }
   2974       if (LocaleCompare("output",property) == 0)
   2975         {
   2976           WarnNoImageInfoReturn("\"%%[%s]\"",property);
   2977           (void) CopyMagickString(value,image_info->filename,MagickPathExtent);
   2978           break;
   2979         }
   2980      break;
   2981     }
   2982     case 'p':
   2983     {
   2984 #if defined(MAGICKCORE_LCMS_DELEGATE)
   2985       if (LocaleCompare("profile:icc",property) == 0 ||
   2986           LocaleCompare("profile:icm",property) == 0)
   2987         {
   2988 #if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
   2989 #define cmsUInt32Number  DWORD
   2990 #endif
   2991 
   2992           const StringInfo
   2993             *profile;
   2994 
   2995           cmsHPROFILE
   2996             icc_profile;
   2997 
   2998           profile=GetImageProfile(image,property+8);
   2999           if (profile == (StringInfo *) NULL)
   3000             break;
   3001           icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
   3002             (cmsUInt32Number) GetStringInfoLength(profile));
   3003           if (icc_profile != (cmsHPROFILE *) NULL)
   3004             {
   3005 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
   3006               string=cmsTakeProductName(icc_profile);
   3007 #else
   3008               (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
   3009                 "en","US",value,MagickPathExtent);
   3010 #endif
   3011               (void) cmsCloseProfile(icc_profile);
   3012             }
   3013       }
   3014 #endif
   3015       if (LocaleCompare("profiles",property) == 0)
   3016         {
   3017           const char
   3018             *name;
   3019 
   3020           ResetImageProfileIterator(image);
   3021           name=GetNextImageProfile(image);
   3022           if (name != (char *) NULL)
   3023             {
   3024               (void) CopyMagickString(value,name,MagickPathExtent);
   3025               name=GetNextImageProfile(image);
   3026               while (name != (char *) NULL)
   3027               {
   3028                 ConcatenateMagickString(value,",",MagickPathExtent);
   3029                 ConcatenateMagickString(value,name,MagickPathExtent);
   3030                 name=GetNextImageProfile(image);
   3031               }
   3032             }
   3033           break;
   3034         }
   3035       break;
   3036     }
   3037     case 'r':
   3038     {
   3039       if (LocaleCompare("resolution.x",property) == 0)
   3040         {
   3041           WarnNoImageReturn("\"%%[%s]\"",property);
   3042           (void) FormatLocaleString(value,MagickPathExtent,"%g",
   3043             image->resolution.x);
   3044           break;
   3045         }
   3046       if (LocaleCompare("resolution.y",property) == 0)
   3047         {
   3048           WarnNoImageReturn("\"%%[%s]\"",property);
   3049           (void) FormatLocaleString(value,MagickPathExtent,"%g",
   3050             image->resolution.y);
   3051           break;
   3052         }
   3053       break;
   3054     }
   3055     case 's':
   3056     {
   3057       if (LocaleCompare("scene",property) == 0)
   3058         {
   3059           WarnNoImageInfoReturn("\"%%[%s]\"",property);
   3060           if (image_info->number_scenes != 0)
   3061             (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3062               image_info->scene);
   3063           else {
   3064             WarnNoImageReturn("\"%%[%s]\"",property);
   3065             (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3066               image->scene);
   3067           }
   3068           break;
   3069         }
   3070       if (LocaleCompare("scenes",property) == 0)
   3071         {
   3072           /* FUTURE: equivelent to %n? */
   3073           WarnNoImageReturn("\"%%[%s]\"",property);
   3074           (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3075             GetImageListLength(image));
   3076           break;
   3077         }
   3078       if (LocaleCompare("size",property) == 0)
   3079         {
   3080           WarnNoImageReturn("\"%%[%s]\"",property);
   3081           (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
   3082             MagickPathExtent,value);
   3083           break;
   3084         }
   3085       if (LocaleCompare("skewness",property) == 0)
   3086         {
   3087           double
   3088             kurtosis,
   3089             skewness;
   3090 
   3091           WarnNoImageReturn("\"%%[%s]\"",property);
   3092           (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
   3093           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   3094             GetMagickPrecision(),skewness);
   3095           break;
   3096         }
   3097       if (LocaleCompare("standard-deviation",property) == 0)
   3098         {
   3099           double
   3100             mean,
   3101             standard_deviation;
   3102 
   3103           WarnNoImageReturn("\"%%[%s]\"",property);
   3104           (void) GetImageMean(image,&mean,&standard_deviation,exception);
   3105           (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
   3106             GetMagickPrecision(),standard_deviation);
   3107           break;
   3108         }
   3109        break;
   3110     }
   3111     case 't':
   3112     {
   3113       if (LocaleCompare("type",property) == 0)
   3114         {
   3115           WarnNoImageReturn("\"%%[%s]\"",property);
   3116           string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
   3117             IdentifyImageType(image,exception));
   3118           break;
   3119         }
   3120        break;
   3121     }
   3122     case 'u':
   3123     {
   3124       if (LocaleCompare("unique",property) == 0)
   3125         {
   3126           WarnNoImageInfoReturn("\"%%[%s]\"",property);
   3127           string=image_info->unique;
   3128           break;
   3129         }
   3130       if (LocaleCompare("units",property) == 0)
   3131         {
   3132           WarnNoImageReturn("\"%%[%s]\"",property);
   3133           string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
   3134             image->units);
   3135           break;
   3136         }
   3137       if (LocaleCompare("copyright",property) == 0)
   3138       break;
   3139     }
   3140     case 'v':
   3141     {
   3142       if (LocaleCompare("version",property) == 0)
   3143         {
   3144           string=GetMagickVersion((size_t *) NULL);
   3145           break;
   3146         }
   3147       break;
   3148     }
   3149     case 'w':
   3150     {
   3151       if (LocaleCompare("width",property) == 0)
   3152         {
   3153           WarnNoImageReturn("\"%%[%s]\"",property);
   3154           (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3155             (image->magick_columns != 0 ? image->magick_columns : 256));
   3156           break;
   3157         }
   3158       break;
   3159     }
   3160   }
   3161   if (string != (char *) NULL)
   3162     return(string);
   3163   if (*value != '\0')
   3164     {
   3165       /*
   3166         Create a cloned copy of result, that will get cleaned up, eventually.
   3167       */
   3168       if (image != (Image *) NULL)
   3169         {
   3170           (void) SetImageArtifact(image,"get-property",value);
   3171           return(GetImageArtifact(image,"get-property"));
   3172         }
   3173       else
   3174         {
   3175           (void) SetImageOption(image_info,"get-property",value);
   3176           return(GetImageOption(image_info,"get-property"));
   3177         }
   3178     }
   3179   return((char *) NULL);
   3180 }
   3181 #undef WarnNoImageReturn
   3182 
   3183 /*
   3185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3186 %                                                                             %
   3187 %                                                                             %
   3188 %                                                                             %
   3189 %   G e t N e x t I m a g e P r o p e r t y                                   %
   3190 %                                                                             %
   3191 %                                                                             %
   3192 %                                                                             %
   3193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3194 %
   3195 %  GetNextImageProperty() gets the next free-form string property name.
   3196 %
   3197 %  The format of the GetNextImageProperty method is:
   3198 %
   3199 %      char *GetNextImageProperty(const Image *image)
   3200 %
   3201 %  A description of each parameter follows:
   3202 %
   3203 %    o image: the image.
   3204 %
   3205 */
   3206 MagickExport const char *GetNextImageProperty(const Image *image)
   3207 {
   3208   assert(image != (Image *) NULL);
   3209   assert(image->signature == MagickCoreSignature);
   3210   if (image->debug != MagickFalse)
   3211     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
   3212       image->filename);
   3213   if (image->properties == (void *) NULL)
   3214     return((const char *) NULL);
   3215   return((const char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
   3216 }
   3217 
   3218 /*
   3220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3221 %                                                                             %
   3222 %                                                                             %
   3223 %                                                                             %
   3224 %   I n t e r p r e t I m a g e P r o p e r t i e s                           %
   3225 %                                                                             %
   3226 %                                                                             %
   3227 %                                                                             %
   3228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3229 %
   3230 %  InterpretImageProperties() replaces any embedded formatting characters with
   3231 %  the appropriate image property and returns the interpreted text.
   3232 %
   3233 %  This searches for and replaces
   3234 %     \n \r \%          replaced by newline, return, and percent resp.
   3235 %     &lt; &gt; &amp;   replaced by '<', '>', '&' resp.
   3236 %     %%                replaced by percent
   3237 %
   3238 %     %x %[x]       where 'x' is a single letter properity, case sensitive).
   3239 %     %[type:name]  where 'type' a is special and known prefix.
   3240 %     %[name]       where 'name' is a specifically known attribute, calculated
   3241 %                   value, or a per-image property string name, or a per-image
   3242 %                   'artifact' (as generated from a global option).
   3243 %                   It may contain ':' as long as the prefix is not special.
   3244 %
   3245 %  Single letter % substitutions will only happen if the character before the
   3246 %  percent is NOT a number. But braced substitutions will always be performed.
   3247 %  This prevents the typical usage of percent in a interpreted geometry
   3248 %  argument from being substituted when the percent is a geometry flag.
   3249 %
   3250 %  If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
   3251 %  used as a search pattern to print multiple lines of "name=value\n" pairs of
   3252 %  the associacted set of properties.
   3253 %
   3254 %  The returned string must be freed using DestoryString() by the caller.
   3255 %
   3256 %  The format of the InterpretImageProperties method is:
   3257 %
   3258 %      char *InterpretImageProperties(ImageInfo *image_info,
   3259 %        Image *image,const char *embed_text,ExceptionInfo *exception)
   3260 %
   3261 %  A description of each parameter follows:
   3262 %
   3263 %    o image_info: the image info. (required)
   3264 %
   3265 %    o image: the image. (optional)
   3266 %
   3267 %    o embed_text: the address of a character string containing the embedded
   3268 %      formatting characters.
   3269 %
   3270 %    o exception: return any errors or warnings in this structure.
   3271 %
   3272 */
   3273 MagickExport char *InterpretImageProperties(ImageInfo *image_info,Image *image,
   3274   const char *embed_text,ExceptionInfo *exception)
   3275 {
   3276 #define ExtendInterpretText(string_length) \
   3277 DisableMSCWarning(4127) \
   3278 { \
   3279   size_t length=(string_length); \
   3280   if ((size_t) (q-interpret_text+length+1) >= extent) \
   3281     { \
   3282       extent+=length; \
   3283       interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
   3284         MaxTextExtent,sizeof(*interpret_text)); \
   3285       if (interpret_text == (char *) NULL) \
   3286         return((char *) NULL); \
   3287       q=interpret_text+strlen(interpret_text); \
   3288    } \
   3289 } \
   3290 RestoreMSCWarning
   3291 
   3292 #define AppendKeyValue2Text(key,value)\
   3293 DisableMSCWarning(4127) \
   3294 { \
   3295   size_t length=strlen(key)+strlen(value)+2; \
   3296   if ((size_t) (q-interpret_text+length+1) >= extent) \
   3297     { \
   3298       extent+=length; \
   3299       interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
   3300         MaxTextExtent,sizeof(*interpret_text)); \
   3301       if (interpret_text == (char *) NULL) \
   3302         return((char *) NULL); \
   3303       q=interpret_text+strlen(interpret_text); \
   3304      } \
   3305    q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(value)); \
   3306 } \
   3307 RestoreMSCWarning
   3308 
   3309 #define AppendString2Text(string) \
   3310 DisableMSCWarning(4127) \
   3311 { \
   3312   size_t length=strlen((string)); \
   3313   if ((size_t) (q-interpret_text+length+1) >= extent) \
   3314     { \
   3315       extent+=length; \
   3316       interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
   3317         MaxTextExtent,sizeof(*interpret_text)); \
   3318       if (interpret_text == (char *) NULL) \
   3319         return((char *) NULL); \
   3320       q=interpret_text+strlen(interpret_text); \
   3321     } \
   3322   (void) CopyMagickString(q,(string),extent); \
   3323   q+=length; \
   3324 } \
   3325 RestoreMSCWarning
   3326 
   3327   char
   3328     *interpret_text;
   3329 
   3330   MagickBooleanType
   3331     number;
   3332 
   3333   register char
   3334     *q;  /* current position in interpret_text */
   3335 
   3336   register const char
   3337     *p;  /* position in embed_text string being expanded */
   3338 
   3339   size_t
   3340     extent;  /* allocated length of interpret_text */
   3341 
   3342   assert(image == NULL || image->signature == MagickCoreSignature);
   3343   assert(image_info == NULL || image_info->signature == MagickCoreSignature);
   3344   if ((image != (Image *) NULL) && (image->debug != MagickFalse))
   3345     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3346   else
   3347    if ((image_info != (ImageInfo *) NULL) && (image_info->debug != MagickFalse))
   3348      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
   3349   if (embed_text == (const char *) NULL)
   3350     return(ConstantString(""));
   3351   p=embed_text;
   3352   while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0'))
   3353     p++;
   3354   if (*p == '\0')
   3355     return(ConstantString(""));
   3356   if ((*p == '@') && (IsPathAccessible(p+1) != MagickFalse))
   3357     {
   3358       /*
   3359         Handle a '@' replace string from file.
   3360       */
   3361       if (IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,p) == MagickFalse)
   3362         {
   3363           errno=EPERM;
   3364           (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
   3365             "NotAuthorized","`%s'",p);
   3366           return(ConstantString(""));
   3367         }
   3368       interpret_text=FileToString(p+1,~0UL,exception);
   3369       if (interpret_text != (char *) NULL)
   3370         return(interpret_text);
   3371     }
   3372   /*
   3373     Translate any embedded format characters.
   3374   */
   3375   interpret_text=AcquireString(embed_text); /* new string with extra space */
   3376   extent=MagickPathExtent;                     /* allocated space in string */
   3377   number=MagickFalse;                       /* is last char a number? */
   3378   for (q=interpret_text; *p!='\0'; number=isdigit(*p) ? MagickTrue : MagickFalse,p++)
   3379   {
   3380     /*
   3381       Look for the various escapes, (and handle other specials)
   3382     */
   3383     *q='\0';
   3384     ExtendInterpretText(MagickPathExtent);
   3385     switch (*p)
   3386     {
   3387       case '\\':
   3388       {
   3389         switch (*(p+1))
   3390         {
   3391           case '\0':
   3392             continue;
   3393           case 'r':  /* convert to RETURN */
   3394           {
   3395             *q++='\r';
   3396             p++;
   3397             continue;
   3398           }
   3399           case 'n':  /* convert to NEWLINE */
   3400           {
   3401             *q++='\n';
   3402             p++;
   3403             continue;
   3404           }
   3405           case '\n':  /* EOL removal UNIX,MacOSX */
   3406           {
   3407             p++;
   3408             continue;
   3409           }
   3410           case '\r':  /* EOL removal DOS,Windows */
   3411           {
   3412             p++;
   3413             if (*p == '\n') /* return-newline EOL */
   3414               p++;
   3415             continue;
   3416           }
   3417           default:
   3418           {
   3419             p++;
   3420             *q++=(*p);
   3421           }
   3422         }
   3423         continue;
   3424       }
   3425       case '&':
   3426       {
   3427         if (LocaleNCompare("&lt;",p,4) == 0)
   3428           {
   3429             *q++='<';
   3430             p+=3;
   3431           }
   3432         else
   3433           if (LocaleNCompare("&gt;",p,4) == 0)
   3434             {
   3435               *q++='>';
   3436               p+=3;
   3437             }
   3438           else
   3439             if (LocaleNCompare("&amp;",p,5) == 0)
   3440               {
   3441                 *q++='&';
   3442                 p+=4;
   3443               }
   3444             else
   3445               *q++=(*p);
   3446         continue;
   3447       }
   3448       case '%':
   3449         break;  /* continue to next set of handlers */
   3450       default:
   3451       {
   3452         *q++=(*p);  /* any thing else is 'as normal' */
   3453         continue;
   3454       }
   3455     }
   3456     p++; /* advance beyond the percent */
   3457     /*
   3458       Doubled Percent - or percent at end of string.
   3459     */
   3460     if ((*p == '\0') || (*p == '\'') || (*p == '"'))
   3461       p--;
   3462     if (*p == '%')
   3463       {
   3464         *q++='%';
   3465         continue;
   3466       }
   3467     /*
   3468       Single letter escapes %c.
   3469     */
   3470     if (*p != '[')
   3471       {
   3472         const char
   3473           *string;
   3474 
   3475         if (number != MagickFalse)
   3476           {
   3477             /*
   3478               But only if not preceeded by a number!
   3479             */
   3480             *q++='%'; /* do NOT substitute the percent */
   3481             p--;      /* back up one */
   3482             continue;
   3483           }
   3484         string=GetMagickPropertyLetter(image_info,image,*p, exception);
   3485         if (string != (char *) NULL)
   3486           {
   3487             AppendString2Text(string);
   3488             if (image != (Image *) NULL)
   3489               (void) DeleteImageArtifact(image,"get-property");
   3490             if (image_info != (ImageInfo *) NULL)
   3491               (void) DeleteImageOption(image_info,"get-property");
   3492             continue;
   3493           }
   3494         (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
   3495           "UnknownImageProperty","\"%%%c\"",*p);
   3496         continue;
   3497       }
   3498     {
   3499       char
   3500         pattern[2*MagickPathExtent];
   3501 
   3502       const char
   3503         *key,
   3504         *string;
   3505 
   3506       register ssize_t
   3507         len;
   3508 
   3509       ssize_t
   3510         depth;
   3511 
   3512       /*
   3513         Braced Percent Escape %[...].
   3514       */
   3515       p++;  /* advance p to just inside the opening brace */
   3516       depth=1;
   3517       if (*p == ']')
   3518         {
   3519           (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
   3520             "UnknownImageProperty","\"%%[]\"");
   3521           break;
   3522         }
   3523       for (len=0; len<(MagickPathExtent-1L) && (*p != '\0');)
   3524       {
   3525         if ((*p == '\\') && (*(p+1) != '\0'))
   3526           {
   3527             /*
   3528               Skip escaped braces within braced pattern.
   3529             */
   3530             pattern[len++]=(*p++);
   3531             pattern[len++]=(*p++);
   3532             continue;
   3533           }
   3534         if (*p == '[')
   3535           depth++;
   3536         if (*p == ']')
   3537           depth--;
   3538         if (depth <= 0)
   3539           break;
   3540         pattern[len++]=(*p++);
   3541       }
   3542       pattern[len]='\0';
   3543       if (depth != 0)
   3544         {
   3545           /*
   3546             Check for unmatched final ']' for "%[...]".
   3547           */
   3548           if (len >= 64)
   3549             {
   3550               pattern[61] = '.';  /* truncate string for error message */
   3551               pattern[62] = '.';
   3552               pattern[63] = '.';
   3553               pattern[64] = '\0';
   3554             }
   3555           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   3556             "UnbalancedBraces","\"%%[%s\"",pattern);
   3557           interpret_text=DestroyString(interpret_text);
   3558           return((char *) NULL);
   3559         }
   3560       /*
   3561         Special Lookup Prefixes %[prefix:...].
   3562       */
   3563       if (LocaleNCompare("fx:",pattern,3) == 0)
   3564         {
   3565           double
   3566             value;
   3567 
   3568           FxInfo
   3569             *fx_info;
   3570 
   3571           MagickBooleanType
   3572             status;
   3573 
   3574           /*
   3575             FX - value calculator.
   3576           */
   3577           if (image == (Image *) NULL )
   3578             {
   3579               (void) ThrowMagickException(exception,GetMagickModule(),
   3580                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
   3581               continue; /* else no image to retrieve artifact */
   3582             }
   3583           fx_info=AcquireFxInfo(image,pattern+3,exception);
   3584           status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
   3585             &value,exception);
   3586           fx_info=DestroyFxInfo(fx_info);
   3587           if (status != MagickFalse)
   3588             {
   3589               char
   3590                 result[MagickPathExtent];
   3591 
   3592               (void) FormatLocaleString(result,MagickPathExtent,"%.*g",
   3593                 GetMagickPrecision(),(double) value);
   3594               AppendString2Text(result);
   3595             }
   3596           continue;
   3597         }
   3598       if (LocaleNCompare("pixel:",pattern,6) == 0)
   3599         {
   3600           FxInfo
   3601             *fx_info;
   3602 
   3603           double
   3604             value;
   3605 
   3606           MagickStatusType
   3607             status;
   3608 
   3609           PixelInfo
   3610             pixel;
   3611 
   3612           /*
   3613             Pixel - color value calculator.
   3614           */
   3615           if (image == (Image *) NULL)
   3616             {
   3617               (void) ThrowMagickException(exception,GetMagickModule(),
   3618                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
   3619               continue; /* else no image to retrieve artifact */
   3620             }
   3621           GetPixelInfo(image,&pixel);
   3622           fx_info=AcquireFxInfo(image,pattern+6,exception);
   3623           status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
   3624             &value,exception);
   3625           pixel.red=(double) QuantumRange*value;
   3626           status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
   3627             &value,exception);
   3628           pixel.green=(double) QuantumRange*value;
   3629           status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
   3630             &value,exception);
   3631           pixel.blue=(double) QuantumRange*value;
   3632           if (image->colorspace == CMYKColorspace)
   3633             {
   3634               status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
   3635                 &value,exception);
   3636               pixel.black=(double) QuantumRange*value;
   3637             }
   3638           status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
   3639             &value,exception);
   3640           pixel.alpha=(double) QuantumRange*value;
   3641           fx_info=DestroyFxInfo(fx_info);
   3642           if (status != MagickFalse)
   3643             {
   3644               char
   3645                 name[MagickPathExtent];
   3646 
   3647               (void) QueryColorname(image,&pixel,SVGCompliance,name,
   3648                 exception);
   3649               AppendString2Text(name);
   3650             }
   3651           continue;
   3652         }
   3653       if (LocaleNCompare("option:",pattern,7) == 0)
   3654         {
   3655           /*
   3656             Option - direct global option lookup (with globbing).
   3657           */
   3658           if (image_info == (ImageInfo *) NULL )
   3659             {
   3660               (void) ThrowMagickException(exception,GetMagickModule(),
   3661                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
   3662               continue; /* else no image to retrieve artifact */
   3663             }
   3664           if (IsGlob(pattern+7) != MagickFalse)
   3665             {
   3666               ResetImageOptionIterator(image_info);
   3667               while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
   3668                 if (GlobExpression(key,pattern+7,MagickTrue) != MagickFalse)
   3669                   {
   3670                     string=GetImageOption(image_info,key);
   3671                     if (string != (const char *) NULL)
   3672                       AppendKeyValue2Text(key,string);
   3673                     /* else - assertion failure? key found but no string value! */
   3674                   }
   3675               continue;
   3676             }
   3677           string=GetImageOption(image_info,pattern+7);
   3678           if (string == (char *) NULL)
   3679             goto PropertyLookupFailure; /* no artifact of this specifc name */
   3680           AppendString2Text(string);
   3681           continue;
   3682         }
   3683       if (LocaleNCompare("artifact:",pattern,9) == 0)
   3684         {
   3685           /*
   3686             Artifact - direct image artifact lookup (with glob).
   3687           */
   3688           if (image == (Image *) NULL)
   3689             {
   3690               (void) ThrowMagickException(exception,GetMagickModule(),
   3691                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
   3692               continue; /* else no image to retrieve artifact */
   3693             }
   3694           if (IsGlob(pattern+9) != MagickFalse)
   3695             {
   3696               ResetImageArtifactIterator(image);
   3697               while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
   3698               if (GlobExpression(key,pattern+9,MagickTrue) != MagickFalse)
   3699                 {
   3700                   string=GetImageArtifact(image,key);
   3701                   if (string != (const char *) NULL)
   3702                     AppendKeyValue2Text(key,string);
   3703                   /* else - assertion failure? key found but no string value! */
   3704                 }
   3705               continue;
   3706             }
   3707           string=GetImageArtifact(image,pattern+9);
   3708           if (string == (char *) NULL)
   3709             goto PropertyLookupFailure; /* no artifact of this specifc name */
   3710           AppendString2Text(string);
   3711           continue;
   3712         }
   3713       if (LocaleNCompare("property:",pattern,9) == 0)
   3714         {
   3715           /*
   3716             Property - direct image property lookup (with glob).
   3717           */
   3718           if (image == (Image *) NULL)
   3719             {
   3720               (void) ThrowMagickException(exception,GetMagickModule(),
   3721                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
   3722               continue; /* else no image to retrieve artifact */
   3723             }
   3724           if (IsGlob(pattern+9) != MagickFalse)
   3725             {
   3726               ResetImagePropertyIterator(image);
   3727               while ((key=GetNextImageProperty(image)) != (const char *) NULL)
   3728                 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
   3729                   {
   3730                     string=GetImageProperty(image,key,exception);
   3731                     if (string != (const char *) NULL)
   3732                       AppendKeyValue2Text(key,string);
   3733                     /* else - assertion failure? */
   3734                   }
   3735               continue;
   3736             }
   3737           string=GetImageProperty(image,pattern+9,exception);
   3738           if (string == (char *) NULL)
   3739             goto PropertyLookupFailure; /* no artifact of this specifc name */
   3740           AppendString2Text(string);
   3741           continue;
   3742         }
   3743       if (image != (Image *) NULL)
   3744         {
   3745           /*
   3746             Properties without special prefix.  This handles attributes,
   3747             properties, and profiles such as %[exif:...].  Note the profile
   3748             properties may also include a glob expansion pattern.
   3749           */
   3750           string=GetImageProperty(image,pattern,exception);
   3751           if (string != (const char *) NULL)
   3752             {
   3753               AppendString2Text(string);
   3754               if (image != (Image *) NULL)
   3755                 (void)DeleteImageArtifact(image,"get-property");
   3756               if (image_info != (ImageInfo *) NULL)
   3757                 (void)DeleteImageOption(image_info,"get-property");
   3758               continue;
   3759             }
   3760         }
   3761       if (IsGlob(pattern) != MagickFalse)
   3762         {
   3763           /*
   3764             Handle property 'glob' patterns such as:
   3765             %[*] %[user:array_??] %[filename:e*]>
   3766           */
   3767           if (image == (Image *) NULL)
   3768             continue; /* else no image to retrieve proprty - no list */
   3769           ResetImagePropertyIterator(image);
   3770           while ((key=GetNextImageProperty(image)) != (const char *) NULL)
   3771             if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
   3772               {
   3773                 string=GetImageProperty(image,key,exception);
   3774                 if (string != (const char *) NULL)
   3775                   AppendKeyValue2Text(key,string);
   3776                 /* else - assertion failure? */
   3777               }
   3778           continue;
   3779         }
   3780       /*
   3781         Look for a known property or image attribute such as
   3782         %[basename] %[denisty] %[delay].  Also handles a braced single
   3783         letter: %[b] %[G] %[g].
   3784       */
   3785       string=GetMagickProperty(image_info,image,pattern,exception);
   3786       if (string != (const char *) NULL)
   3787         {
   3788           AppendString2Text(string);
   3789           continue;
   3790         }
   3791       /*
   3792         Look for a per-image artifact. This includes option lookup
   3793         (FUTURE: interpreted according to image).
   3794       */
   3795       if (image != (Image *) NULL)
   3796         {
   3797           string=GetImageArtifact(image,pattern);
   3798           if (string != (char *) NULL)
   3799             {
   3800               AppendString2Text(string);
   3801               continue;
   3802             }
   3803         }
   3804       else
   3805         if (image_info != (ImageInfo *) NULL)
   3806           {
   3807             /*
   3808               No image, so direct 'option' lookup (no delayed percent escapes).
   3809             */
   3810             string=GetImageOption(image_info,pattern);
   3811             if (string != (char *) NULL)
   3812               {
   3813                 AppendString2Text(string);
   3814                 continue;
   3815               }
   3816           }
   3817 PropertyLookupFailure:
   3818       /*
   3819         Failed to find any match anywhere!
   3820       */
   3821       if (len >= 64)
   3822         {
   3823           pattern[61] = '.';  /* truncate string for error message */
   3824           pattern[62] = '.';
   3825           pattern[63] = '.';
   3826           pattern[64] = '\0';
   3827         }
   3828       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
   3829         "UnknownImageProperty","\"%%[%s]\"",pattern);
   3830     }
   3831   }
   3832   *q='\0';
   3833   return(interpret_text);
   3834 }
   3835 
   3836 /*
   3838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3839 %                                                                             %
   3840 %                                                                             %
   3841 %                                                                             %
   3842 %   R e m o v e I m a g e P r o p e r t y                                     %
   3843 %                                                                             %
   3844 %                                                                             %
   3845 %                                                                             %
   3846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3847 %
   3848 %  RemoveImageProperty() removes a property from the image and returns its
   3849 %  value.
   3850 %
   3851 %  In this case the ConstantString() value returned should be freed by the
   3852 %  caller when finished.
   3853 %
   3854 %  The format of the RemoveImageProperty method is:
   3855 %
   3856 %      char *RemoveImageProperty(Image *image,const char *property)
   3857 %
   3858 %  A description of each parameter follows:
   3859 %
   3860 %    o image: the image.
   3861 %
   3862 %    o property: the image property.
   3863 %
   3864 */
   3865 MagickExport char *RemoveImageProperty(Image *image,const char *property)
   3866 {
   3867   char
   3868     *value;
   3869 
   3870   assert(image != (Image *) NULL);
   3871   assert(image->signature == MagickCoreSignature);
   3872   if (image->debug != MagickFalse)
   3873     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3874   if (image->properties == (void *) NULL)
   3875     return((char *) NULL);
   3876   value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
   3877     property);
   3878   return(value);
   3879 }
   3880 
   3881 /*
   3883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3884 %                                                                             %
   3885 %                                                                             %
   3886 %                                                                             %
   3887 %   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                       %
   3888 %                                                                             %
   3889 %                                                                             %
   3890 %                                                                             %
   3891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3892 %
   3893 %  ResetImagePropertyIterator() resets the image properties iterator.  Use it
   3894 %  in conjunction with GetNextImageProperty() to iterate over all the values
   3895 %  associated with an image property.
   3896 %
   3897 %  The format of the ResetImagePropertyIterator method is:
   3898 %
   3899 %      ResetImagePropertyIterator(Image *image)
   3900 %
   3901 %  A description of each parameter follows:
   3902 %
   3903 %    o image: the image.
   3904 %
   3905 */
   3906 MagickExport void ResetImagePropertyIterator(const Image *image)
   3907 {
   3908   assert(image != (Image *) NULL);
   3909   assert(image->signature == MagickCoreSignature);
   3910   if (image->debug != MagickFalse)
   3911     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3912   if (image->properties == (void *) NULL)
   3913     return;
   3914   ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
   3915 }
   3916 
   3917 /*
   3919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3920 %                                                                             %
   3921 %                                                                             %
   3922 %                                                                             %
   3923 %   S e t I m a g e P r o p e r t y                                           %
   3924 %                                                                             %
   3925 %                                                                             %
   3926 %                                                                             %
   3927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3928 %
   3929 %  SetImageProperty() saves the given string value either to specific known
   3930 %  attribute or to a freeform property string.
   3931 %
   3932 %  Attempting to set a property that is normally calculated will produce
   3933 %  an exception.
   3934 %
   3935 %  The format of the SetImageProperty method is:
   3936 %
   3937 %      MagickBooleanType SetImageProperty(Image *image,const char *property,
   3938 %        const char *value,ExceptionInfo *exception)
   3939 %
   3940 %  A description of each parameter follows:
   3941 %
   3942 %    o image: the image.
   3943 %
   3944 %    o property: the image property.
   3945 %
   3946 %    o values: the image property values.
   3947 %
   3948 %    o exception: return any errors or warnings in this structure.
   3949 %
   3950 */
   3951 MagickExport MagickBooleanType SetImageProperty(Image *image,
   3952   const char *property,const char *value,ExceptionInfo *exception)
   3953 {
   3954   MagickBooleanType
   3955     status;
   3956 
   3957   MagickStatusType
   3958     flags;
   3959 
   3960   assert(image != (Image *) NULL);
   3961   assert(image->signature == MagickCoreSignature);
   3962   if (image->debug != MagickFalse)
   3963     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3964   if (image->properties == (void *) NULL)
   3965     image->properties=NewSplayTree(CompareSplayTreeString,
   3966       RelinquishMagickMemory,RelinquishMagickMemory);  /* create splay-tree */
   3967   if (value == (const char *) NULL)
   3968     return(DeleteImageProperty(image,property));  /* delete if NULL */
   3969   status=MagickTrue;
   3970   if (strlen(property) <= 1)
   3971     {
   3972       /*
   3973         Do not 'set' single letter properties - read only shorthand.
   3974        */
   3975       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   3976         "SetReadOnlyProperty","`%s'",property);
   3977       return(MagickFalse);
   3978     }
   3979 
   3980   /* FUTURE: binary chars or quotes in key should produce a error */
   3981   /* Set attributes with known names or special prefixes
   3982      return result is found, or break to set a free form properity
   3983   */
   3984   switch (*property)
   3985   {
   3986 #if 0  /* Percent escape's sets values with this prefix: for later use
   3987           Throwing an exception causes this setting to fail */
   3988     case '8':
   3989     {
   3990       if (LocaleNCompare("8bim:",property,5) == 0)
   3991         {
   3992           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   3993             "SetReadOnlyProperty","`%s'",property);
   3994           return(MagickFalse);
   3995         }
   3996       break;
   3997     }
   3998 #endif
   3999     case 'B':
   4000     case 'b':
   4001     {
   4002       if (LocaleCompare("background",property) == 0)
   4003         {
   4004           (void) QueryColorCompliance(value,AllCompliance,
   4005                &image->background_color,exception);
   4006           /* check for FUTURE: value exception?? */
   4007           /* also add user input to splay tree */
   4008         }
   4009       break; /* not an attribute, add as a property */
   4010     }
   4011     case 'C':
   4012     case 'c':
   4013     {
   4014       if (LocaleCompare("channels",property) == 0)
   4015         {
   4016           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4017             "SetReadOnlyProperty","`%s'",property);
   4018           return(MagickFalse);
   4019         }
   4020       if (LocaleCompare("colorspace",property) == 0)
   4021         {
   4022           ssize_t
   4023             colorspace;
   4024 
   4025           colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
   4026             value);
   4027           if (colorspace < 0)
   4028             return(MagickFalse); /* FUTURE: value exception?? */
   4029           return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
   4030         }
   4031       if (LocaleCompare("compose",property) == 0)
   4032         {
   4033           ssize_t
   4034             compose;
   4035 
   4036           compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
   4037           if (compose < 0)
   4038             return(MagickFalse); /* FUTURE: value exception?? */
   4039           image->compose=(CompositeOperator) compose;
   4040           return(MagickTrue);
   4041         }
   4042       if (LocaleCompare("compress",property) == 0)
   4043         {
   4044           ssize_t
   4045             compression;
   4046 
   4047           compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
   4048             value);
   4049           if (compression < 0)
   4050             return(MagickFalse); /* FUTURE: value exception?? */
   4051           image->compression=(CompressionType) compression;
   4052           return(MagickTrue);
   4053         }
   4054       break; /* not an attribute, add as a property */
   4055     }
   4056     case 'D':
   4057     case 'd':
   4058     {
   4059       if (LocaleCompare("delay",property) == 0)
   4060         {
   4061           GeometryInfo
   4062             geometry_info;
   4063 
   4064           flags=ParseGeometry(value,&geometry_info);
   4065           if ((flags & GreaterValue) != 0)
   4066             {
   4067               if (image->delay > (size_t) floor(geometry_info.rho+0.5))
   4068                 image->delay=(size_t) floor(geometry_info.rho+0.5);
   4069             }
   4070           else
   4071             if ((flags & LessValue) != 0)
   4072               {
   4073                 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
   4074                   image->delay=(ssize_t)
   4075                     floor(geometry_info.sigma+0.5);
   4076               }
   4077             else
   4078               image->delay=(size_t) floor(geometry_info.rho+0.5);
   4079           if ((flags & SigmaValue) != 0)
   4080             image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
   4081           return(MagickTrue);
   4082         }
   4083       if (LocaleCompare("delay_units",property) == 0)
   4084         {
   4085           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4086             "SetReadOnlyProperty","`%s'",property);
   4087           return(MagickFalse);
   4088         }
   4089       if (LocaleCompare("density",property) == 0)
   4090         {
   4091           GeometryInfo
   4092             geometry_info;
   4093 
   4094           flags=ParseGeometry(value,&geometry_info);
   4095           image->resolution.x=geometry_info.rho;
   4096           image->resolution.y=geometry_info.sigma;
   4097           if ((flags & SigmaValue) == 0)
   4098             image->resolution.y=image->resolution.x;
   4099           return(MagickTrue);
   4100         }
   4101       if (LocaleCompare("depth",property) == 0)
   4102         {
   4103           image->depth=StringToUnsignedLong(value);
   4104           return(MagickTrue);
   4105         }
   4106       if (LocaleCompare("dispose",property) == 0)
   4107         {
   4108           ssize_t
   4109             dispose;
   4110 
   4111           dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
   4112           if (dispose < 0)
   4113             return(MagickFalse); /* FUTURE: value exception?? */
   4114           image->dispose=(DisposeType) dispose;
   4115           return(MagickTrue);
   4116         }
   4117       break; /* not an attribute, add as a property */
   4118     }
   4119 #if 0  /* Percent escape's sets values with this prefix: for later use
   4120           Throwing an exception causes this setting to fail */
   4121     case 'E':
   4122     case 'e':
   4123     {
   4124       if (LocaleNCompare("exif:",property,5) == 0)
   4125         {
   4126           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4127             "SetReadOnlyProperty","`%s'",property);
   4128           return(MagickFalse);
   4129         }
   4130       break; /* not an attribute, add as a property */
   4131     }
   4132     case 'F':
   4133     case 'f':
   4134     {
   4135       if (LocaleNCompare("fx:",property,3) == 0)
   4136         {
   4137           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4138             "SetReadOnlyProperty","`%s'",property);
   4139           return(MagickFalse);
   4140         }
   4141       break; /* not an attribute, add as a property */
   4142     }
   4143 #endif
   4144     case 'G':
   4145     case 'g':
   4146     {
   4147       if (LocaleCompare("gamma",property) == 0)
   4148         {
   4149           image->gamma=StringToDouble(value,(char **) NULL);
   4150           return(MagickTrue);
   4151         }
   4152       if (LocaleCompare("gravity",property) == 0)
   4153         {
   4154           ssize_t
   4155             gravity;
   4156 
   4157           gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
   4158           if (gravity < 0)
   4159             return(MagickFalse); /* FUTURE: value exception?? */
   4160           image->gravity=(GravityType) gravity;
   4161           return(MagickTrue);
   4162         }
   4163       break; /* not an attribute, add as a property */
   4164     }
   4165     case 'H':
   4166     case 'h':
   4167     {
   4168       if (LocaleCompare("height",property) == 0)
   4169         {
   4170           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4171             "SetReadOnlyProperty","`%s'",property);
   4172           return(MagickFalse);
   4173         }
   4174       break; /* not an attribute, add as a property */
   4175     }
   4176     case 'I':
   4177     case 'i':
   4178     {
   4179       if (LocaleCompare("intensity",property) == 0)
   4180         {
   4181           ssize_t
   4182             intensity;
   4183 
   4184           intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,value);
   4185           if (intensity < 0)
   4186             return(MagickFalse);
   4187           image->intensity=(PixelIntensityMethod) intensity;
   4188           return(MagickTrue);
   4189         }
   4190       if (LocaleCompare("intent",property) == 0)
   4191         {
   4192           ssize_t
   4193             rendering_intent;
   4194 
   4195           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
   4196             value);
   4197           if (rendering_intent < 0)
   4198             return(MagickFalse); /* FUTURE: value exception?? */
   4199           image->rendering_intent=(RenderingIntent) rendering_intent;
   4200           return(MagickTrue);
   4201         }
   4202       if (LocaleCompare("interpolate",property) == 0)
   4203         {
   4204           ssize_t
   4205             interpolate;
   4206 
   4207           interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
   4208             value);
   4209           if (interpolate < 0)
   4210             return(MagickFalse); /* FUTURE: value exception?? */
   4211           image->interpolate=(PixelInterpolateMethod) interpolate;
   4212           return(MagickTrue);
   4213         }
   4214 #if 0  /* Percent escape's sets values with this prefix: for later use
   4215           Throwing an exception causes this setting to fail */
   4216       if (LocaleNCompare("iptc:",property,5) == 0)
   4217         {
   4218           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4219             "SetReadOnlyProperty","`%s'",property);
   4220           return(MagickFalse);
   4221         }
   4222 #endif
   4223       break; /* not an attribute, add as a property */
   4224     }
   4225     case 'K':
   4226     case 'k':
   4227       if (LocaleCompare("kurtosis",property) == 0)
   4228         {
   4229           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4230             "SetReadOnlyProperty","`%s'",property);
   4231           return(MagickFalse);
   4232         }
   4233       break; /* not an attribute, add as a property */
   4234     case 'L':
   4235     case 'l':
   4236     {
   4237       if (LocaleCompare("loop",property) == 0)
   4238         {
   4239           image->iterations=StringToUnsignedLong(value);
   4240           return(MagickTrue);
   4241         }
   4242       break; /* not an attribute, add as a property */
   4243     }
   4244     case 'M':
   4245     case 'm':
   4246       if ((LocaleCompare("magick",property) == 0) ||
   4247           (LocaleCompare("max",property) == 0) ||
   4248           (LocaleCompare("mean",property) == 0) ||
   4249           (LocaleCompare("min",property) == 0) ||
   4250           (LocaleCompare("min",property) == 0))
   4251         {
   4252           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4253              "SetReadOnlyProperty","`%s'",property);
   4254           return(MagickFalse);
   4255         }
   4256       break; /* not an attribute, add as a property */
   4257     case 'O':
   4258     case 'o':
   4259       if (LocaleCompare("opaque",property) == 0)
   4260         {
   4261           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4262             "SetReadOnlyProperty","`%s'",property);
   4263           return(MagickFalse);
   4264         }
   4265       break; /* not an attribute, add as a property */
   4266     case 'P':
   4267     case 'p':
   4268     {
   4269       if (LocaleCompare("page",property) == 0)
   4270         {
   4271           char
   4272             *geometry;
   4273 
   4274           geometry=GetPageGeometry(value);
   4275           flags=ParseAbsoluteGeometry(geometry,&image->page);
   4276           geometry=DestroyString(geometry);
   4277           return(MagickTrue);
   4278         }
   4279 #if 0  /* Percent escape's sets values with this prefix: for later use
   4280           Throwing an exception causes this setting to fail */
   4281       if (LocaleNCompare("pixel:",property,6) == 0)
   4282         {
   4283           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4284             "SetReadOnlyProperty","`%s'",property);
   4285           return(MagickFalse);
   4286         }
   4287 #endif
   4288       if (LocaleCompare("profile",property) == 0)
   4289         {
   4290           ImageInfo
   4291             *image_info;
   4292 
   4293           StringInfo
   4294             *profile;
   4295 
   4296           image_info=AcquireImageInfo();
   4297           (void) CopyMagickString(image_info->filename,value,MagickPathExtent);
   4298           (void) SetImageInfo(image_info,1,exception);
   4299           profile=FileToStringInfo(image_info->filename,~0UL,exception);
   4300           if (profile != (StringInfo *) NULL)
   4301             status=SetImageProfile(image,image_info->magick,profile,exception);
   4302           image_info=DestroyImageInfo(image_info);
   4303           return(MagickTrue);
   4304         }
   4305       break; /* not an attribute, add as a property */
   4306     }
   4307     case 'R':
   4308     case 'r':
   4309     {
   4310       if (LocaleCompare("rendering-intent",property) == 0)
   4311         {
   4312           ssize_t
   4313             rendering_intent;
   4314 
   4315           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
   4316             value);
   4317           if (rendering_intent < 0)
   4318             return(MagickFalse); /* FUTURE: value exception?? */
   4319           image->rendering_intent=(RenderingIntent) rendering_intent;
   4320           return(MagickTrue);
   4321         }
   4322       break; /* not an attribute, add as a property */
   4323     }
   4324     case 'S':
   4325     case 's':
   4326       if ((LocaleCompare("size",property) == 0) ||
   4327           (LocaleCompare("skewness",property) == 0) ||
   4328           (LocaleCompare("scenes",property) == 0) ||
   4329           (LocaleCompare("standard-deviation",property) == 0))
   4330         {
   4331           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4332             "SetReadOnlyProperty","`%s'",property);
   4333           return(MagickFalse);
   4334         }
   4335       break; /* not an attribute, add as a property */
   4336     case 'T':
   4337     case 't':
   4338     {
   4339       if (LocaleCompare("tile-offset",property) == 0)
   4340         {
   4341           char
   4342             *geometry;
   4343 
   4344           geometry=GetPageGeometry(value);
   4345           flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
   4346           geometry=DestroyString(geometry);
   4347           return(MagickTrue);
   4348         }
   4349       break; /* not an attribute, add as a property */
   4350     }
   4351     case 'U':
   4352     case 'u':
   4353     {
   4354       if (LocaleCompare("units",property) == 0)
   4355         {
   4356           ssize_t
   4357             units;
   4358 
   4359           units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
   4360           if (units < 0)
   4361             return(MagickFalse); /* FUTURE: value exception?? */
   4362           image->units=(ResolutionType) units;
   4363           return(MagickTrue);
   4364         }
   4365       break; /* not an attribute, add as a property */
   4366     }
   4367     case 'V':
   4368     case 'v':
   4369     {
   4370       if (LocaleCompare("version",property) == 0)
   4371         {
   4372           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4373             "SetReadOnlyProperty","`%s'",property);
   4374           return(MagickFalse);
   4375         }
   4376       break; /* not an attribute, add as a property */
   4377     }
   4378     case 'W':
   4379     case 'w':
   4380     {
   4381       if (LocaleCompare("width",property) == 0)
   4382         {
   4383           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4384             "SetReadOnlyProperty","`%s'",property);
   4385           return(MagickFalse);
   4386         }
   4387       break; /* not an attribute, add as a property */
   4388     }
   4389 #if 0  /* Percent escape's sets values with this prefix: for later use
   4390           Throwing an exception causes this setting to fail */
   4391     case 'X':
   4392     case 'x':
   4393     {
   4394       if (LocaleNCompare("xmp:",property,4) == 0)
   4395         {
   4396           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   4397             "SetReadOnlyProperty","`%s'",property);
   4398           return(MagickFalse);
   4399         }
   4400       break; /* not an attribute, add as a property */
   4401     }
   4402 #endif
   4403   }
   4404   /* Default: not an attribute, add as a property */
   4405   status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
   4406     ConstantString(property),ConstantString(value));
   4407   /* FUTURE: error if status is bad? */
   4408   return(status);
   4409 }
   4410