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