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