]> granicus.if.org Git - imagemagick/blob - MagickCore/property.c
Update web pages
[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-2015 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_LCMS_LCMS2_H)
89 #include <lcms/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( IfMagickTrue(clone_image->debug) )
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->background_color=clone_image->background_color;
152   image->border_color=clone_image->border_color;
153   image->matte_color=clone_image->matte_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,const size_t,
419     const size_t),
420   *TraceSVGClippath(const unsigned char *,size_t,const size_t,
421     const size_t);
422
423 static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
424   ExceptionInfo *exception)
425 {
426   char
427     *attribute,
428     *message;
429
430   const StringInfo
431     *profile;
432
433   long
434     count,
435     dataset,
436     record;
437
438   register ssize_t
439     i;
440
441   size_t
442     length;
443
444   profile=GetImageProfile(image,"iptc");
445   if (profile == (StringInfo *) NULL)
446     profile=GetImageProfile(image,"8bim");
447   if (profile == (StringInfo *) NULL)
448     return(MagickFalse);
449   count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
450   if (count != 2)
451     return(MagickFalse);
452   attribute=(char *) NULL;
453   for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
454   {
455     length=1;
456     if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
457       continue;
458     length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
459     length|=GetStringInfoDatum(profile)[i+4];
460     if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
461         ((long) GetStringInfoDatum(profile)[i+2] == record))
462       {
463         message=(char *) NULL;
464         if (~length >= 1)
465           message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
466         if (message != (char *) NULL)
467           {
468             (void) CopyMagickString(message,(char *) GetStringInfoDatum(
469               profile)+i+5,length+1);
470             (void) ConcatenateString(&attribute,message);
471             (void) ConcatenateString(&attribute,";");
472             message=DestroyString(message);
473           }
474       }
475     i+=5;
476   }
477   if ((attribute == (char *) NULL) || (*attribute == ';'))
478     {
479       if (attribute != (char *) NULL)
480         attribute=DestroyString(attribute);
481       return(MagickFalse);
482     }
483   attribute[strlen(attribute)-1]='\0';
484   (void) SetImageProperty((Image *) image,key,(const char *) attribute,
485     exception);
486   attribute=DestroyString(attribute);
487   return(MagickTrue);
488 }
489
490 static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
491 {
492   int
493     c;
494
495   if (*length < 1)
496     return(EOF);
497   c=(int) (*(*p)++);
498   (*length)--;
499   return(c);
500 }
501
502 static inline size_t ReadPropertyMSBLong(const unsigned char **p,
503   size_t *length)
504 {
505   int
506     c;
507
508   register ssize_t
509     i;
510
511   unsigned char
512     buffer[4];
513
514   size_t
515     value;
516
517   if (*length < 4)
518     return(~0UL);
519   for (i=0; i < 4; i++)
520   {
521     c=(int) (*(*p)++);
522     (*length)--;
523     buffer[i]=(unsigned char) c;
524   }
525   value=(size_t) (buffer[0] << 24);
526   value|=buffer[1] << 16;
527   value|=buffer[2] << 8;
528   value|=buffer[3];
529   return(value & 0xffffffff);
530 }
531
532 static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
533   size_t *length)
534 {
535   int
536     c;
537
538   register ssize_t
539     i;
540
541   unsigned char
542     buffer[2];
543
544   unsigned short
545     value;
546
547   if (*length < 2)
548     return((unsigned short) ~0);
549   for (i=0; i < 2; i++)
550   {
551     c=(int) (*(*p)++);
552     (*length)--;
553     buffer[i]=(unsigned char) c;
554   }
555   value=(unsigned short) (buffer[0] << 8);
556   value|=buffer[1];
557   return((unsigned short) (value & 0xffff));
558 }
559
560 static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
561   ExceptionInfo *exception)
562 {
563   char
564     *attribute,
565     format[MagickPathExtent],
566     name[MagickPathExtent],
567     *resource;
568
569   const StringInfo
570     *profile;
571
572   const unsigned char
573     *info;
574
575   long
576     start,
577     stop;
578
579   MagickBooleanType
580     status;
581
582   register ssize_t
583     i;
584
585   ssize_t
586     count,
587     id,
588     sub_number;
589
590   size_t
591     length;
592
593   /*
594     There are no newlines in path names, so it's safe as terminator.
595   */
596   profile=GetImageProfile(image,"8bim");
597   if (profile == (StringInfo *) NULL)
598     return(MagickFalse);
599   count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
600     format);
601   if ((count != 2) && (count != 3) && (count != 4))
602     return(MagickFalse);
603   if (count < 4)
604     (void) CopyMagickString(format,"SVG",MagickPathExtent);
605   if (count < 3)
606     *name='\0';
607   sub_number=1;
608   if (*name == '#')
609     sub_number=(ssize_t) StringToLong(&name[1]);
610   sub_number=MagickMax(sub_number,1L);
611   resource=(char *) NULL;
612   status=MagickFalse;
613   length=GetStringInfoLength(profile);
614   info=GetStringInfoDatum(profile);
615   while ((length > 0) && IfMagickFalse(status))
616   {
617     if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
618       continue;
619     if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
620       continue;
621     if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
622       continue;
623     if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
624       continue;
625     id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
626     if (id < (ssize_t) start)
627       continue;
628     if (id > (ssize_t) stop)
629       continue;
630     if (resource != (char *) NULL)
631       resource=DestroyString(resource);
632     count=(ssize_t) ReadPropertyByte(&info,&length);
633     if ((count != 0) && ((size_t) count <= length))
634       {
635         resource=(char *) NULL;
636         if (~((size_t) count) >= (MagickPathExtent-1))
637           resource=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent,
638             sizeof(*resource));
639         if (resource != (char *) NULL)
640           {
641             for (i=0; i < (ssize_t) count; i++)
642               resource[i]=(char) ReadPropertyByte(&info,&length);
643             resource[count]='\0';
644           }
645       }
646     if ((count & 0x01) == 0)
647       (void) ReadPropertyByte(&info,&length);
648     count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
649     if ((*name != '\0') && (*name != '#'))
650       if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
651         {
652           /*
653             No name match, scroll forward and try next.
654           */
655           info+=count;
656           length-=MagickMin(count,(ssize_t) length);
657           continue;
658         }
659     if ((*name == '#') && (sub_number != 1))
660       {
661         /*
662           No numbered match, scroll forward and try next.
663         */
664         sub_number--;
665         info+=count;
666         length-=MagickMin(count,(ssize_t) length);
667         continue;
668       }
669     /*
670       We have the resource of interest.
671     */
672     attribute=(char *) NULL;
673     if (~((size_t) count) >= (MagickPathExtent-1))
674       attribute=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent,
675         sizeof(*attribute));
676     if (attribute != (char *) NULL)
677       {
678         (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
679         attribute[count]='\0';
680         info+=count;
681         length-=MagickMin(count,(ssize_t) length);
682         if ((id <= 1999) || (id >= 2999))
683           (void) SetImageProperty((Image *) image,key,(const char *)
684             attribute,exception);
685         else
686           {
687             char
688               *path;
689
690             if (LocaleCompare(format,"svg") == 0)
691               path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
692                 image->columns,image->rows);
693             else
694               path=TracePSClippath((unsigned char *) attribute,(size_t) count,
695                 image->columns,image->rows);
696             (void) SetImageProperty((Image *) image,key,(const char *) path,
697               exception);
698             path=DestroyString(path);
699           }
700         attribute=DestroyString(attribute);
701         status=MagickTrue;
702       }
703   }
704   if (resource != (char *) NULL)
705     resource=DestroyString(resource);
706   return(status);
707 }
708
709 static inline unsigned short ReadPropertyShort(const EndianType endian,
710   const unsigned char *buffer)
711 {
712   unsigned short
713     value;
714
715   if (endian == LSBEndian)
716     {
717       value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
718       return((unsigned short) (value & 0xffff));
719     }
720   value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
721     ((unsigned char *) buffer)[1]);
722   return((unsigned short) (value & 0xffff));
723 }
724
725 static inline size_t ReadPropertyLong(const EndianType endian,
726   const unsigned char *buffer)
727 {
728   size_t
729     value;
730
731   if (endian == LSBEndian)
732     {
733       value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
734         (buffer[1] << 8 ) | (buffer[0]));
735       return((size_t) (value & 0xffffffff));
736     }
737   value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
738     (buffer[2] << 8) | buffer[3]);
739   return((size_t) (value & 0xffffffff));
740 }
741
742 static MagickBooleanType GetEXIFProperty(const Image *image,
743   const char *property,ExceptionInfo *exception)
744 {
745 #define MaxDirectoryStack  16
746 #define EXIF_DELIMITER  "\n"
747 #define EXIF_NUM_FORMATS  12
748 #define EXIF_FMT_BYTE  1
749 #define EXIF_FMT_STRING  2
750 #define EXIF_FMT_USHORT  3
751 #define EXIF_FMT_ULONG  4
752 #define EXIF_FMT_URATIONAL  5
753 #define EXIF_FMT_SBYTE  6
754 #define EXIF_FMT_UNDEFINED  7
755 #define EXIF_FMT_SSHORT  8
756 #define EXIF_FMT_SLONG  9
757 #define EXIF_FMT_SRATIONAL  10
758 #define EXIF_FMT_SINGLE  11
759 #define EXIF_FMT_DOUBLE  12
760 #define TAG_EXIF_OFFSET  0x8769
761 #define TAG_GPS_OFFSET  0x8825
762 #define TAG_INTEROP_OFFSET  0xa005
763
764 #define EXIFMultipleValues(size,format,arg) \
765 { \
766    ssize_t \
767      component; \
768  \
769    size_t \
770      length; \
771  \
772    unsigned char \
773      *p1; \
774  \
775    length=0; \
776    p1=p; \
777    for (component=0; component < components; component++) \
778    { \
779      length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \
780        format", ",arg); \
781      if (length >= (MagickPathExtent-1)) \
782        length=MagickPathExtent-1; \
783      p1+=size; \
784    } \
785    if (length > 1) \
786      buffer[length-2]='\0'; \
787    value=AcquireString(buffer); \
788 }
789
790 #define EXIFMultipleFractions(size,format,arg1,arg2) \
791 { \
792    ssize_t \
793      component; \
794  \
795    size_t \
796      length; \
797  \
798    unsigned char \
799      *p1; \
800  \
801    length=0; \
802    p1=p; \
803    for (component=0; component < components; component++) \
804    { \
805      length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \
806        format", ",(arg1),(arg2)); \
807      if (length >= (MagickPathExtent-1)) \
808        length=MagickPathExtent-1; \
809      p1+=size; \
810    } \
811    if (length > 1) \
812      buffer[length-2]='\0'; \
813    value=AcquireString(buffer); \
814 }
815
816   typedef struct _DirectoryInfo
817   {
818     const unsigned char
819       *directory;
820
821     size_t
822       entry;
823
824     ssize_t
825       offset;
826   } DirectoryInfo;
827
828   typedef struct _TagInfo
829   {
830     size_t
831       tag;
832
833     const char
834       *description;
835   } TagInfo;
836
837   static TagInfo
838     EXIFTag[] =
839     {
840       {  0x001, "exif:InteroperabilityIndex" },
841       {  0x002, "exif:InteroperabilityVersion" },
842       {  0x100, "exif:ImageWidth" },
843       {  0x101, "exif:ImageLength" },
844       {  0x102, "exif:BitsPerSample" },
845       {  0x103, "exif:Compression" },
846       {  0x106, "exif:PhotometricInterpretation" },
847       {  0x10a, "exif:FillOrder" },
848       {  0x10d, "exif:DocumentName" },
849       {  0x10e, "exif:ImageDescription" },
850       {  0x10f, "exif:Make" },
851       {  0x110, "exif:Model" },
852       {  0x111, "exif:StripOffsets" },
853       {  0x112, "exif:Orientation" },
854       {  0x115, "exif:SamplesPerPixel" },
855       {  0x116, "exif:RowsPerStrip" },
856       {  0x117, "exif:StripByteCounts" },
857       {  0x11a, "exif:XResolution" },
858       {  0x11b, "exif:YResolution" },
859       {  0x11c, "exif:PlanarConfiguration" },
860       {  0x11d, "exif:PageName" },
861       {  0x11e, "exif:XPosition" },
862       {  0x11f, "exif:YPosition" },
863       {  0x118, "exif:MinSampleValue" },
864       {  0x119, "exif:MaxSampleValue" },
865       {  0x120, "exif:FreeOffsets" },
866       {  0x121, "exif:FreeByteCounts" },
867       {  0x122, "exif:GrayResponseUnit" },
868       {  0x123, "exif:GrayResponseCurve" },
869       {  0x124, "exif:T4Options" },
870       {  0x125, "exif:T6Options" },
871       {  0x128, "exif:ResolutionUnit" },
872       {  0x12d, "exif:TransferFunction" },
873       {  0x131, "exif:Software" },
874       {  0x132, "exif:DateTime" },
875       {  0x13b, "exif:Artist" },
876       {  0x13e, "exif:WhitePoint" },
877       {  0x13f, "exif:PrimaryChromaticities" },
878       {  0x140, "exif:ColorMap" },
879       {  0x141, "exif:HalfToneHints" },
880       {  0x142, "exif:TileWidth" },
881       {  0x143, "exif:TileLength" },
882       {  0x144, "exif:TileOffsets" },
883       {  0x145, "exif:TileByteCounts" },
884       {  0x14a, "exif:SubIFD" },
885       {  0x14c, "exif:InkSet" },
886       {  0x14d, "exif:InkNames" },
887       {  0x14e, "exif:NumberOfInks" },
888       {  0x150, "exif:DotRange" },
889       {  0x151, "exif:TargetPrinter" },
890       {  0x152, "exif:ExtraSample" },
891       {  0x153, "exif:SampleFormat" },
892       {  0x154, "exif:SMinSampleValue" },
893       {  0x155, "exif:SMaxSampleValue" },
894       {  0x156, "exif:TransferRange" },
895       {  0x157, "exif:ClipPath" },
896       {  0x158, "exif:XClipPathUnits" },
897       {  0x159, "exif:YClipPathUnits" },
898       {  0x15a, "exif:Indexed" },
899       {  0x15b, "exif:JPEGTables" },
900       {  0x15f, "exif:OPIProxy" },
901       {  0x200, "exif:JPEGProc" },
902       {  0x201, "exif:JPEGInterchangeFormat" },
903       {  0x202, "exif:JPEGInterchangeFormatLength" },
904       {  0x203, "exif:JPEGRestartInterval" },
905       {  0x205, "exif:JPEGLosslessPredictors" },
906       {  0x206, "exif:JPEGPointTransforms" },
907       {  0x207, "exif:JPEGQTables" },
908       {  0x208, "exif:JPEGDCTables" },
909       {  0x209, "exif:JPEGACTables" },
910       {  0x211, "exif:YCbCrCoefficients" },
911       {  0x212, "exif:YCbCrSubSampling" },
912       {  0x213, "exif:YCbCrPositioning" },
913       {  0x214, "exif:ReferenceBlackWhite" },
914       {  0x2bc, "exif:ExtensibleMetadataPlatform" },
915       {  0x301, "exif:Gamma" },
916       {  0x302, "exif:ICCProfileDescriptor" },
917       {  0x303, "exif:SRGBRenderingIntent" },
918       {  0x320, "exif:ImageTitle" },
919       {  0x5001, "exif:ResolutionXUnit" },
920       {  0x5002, "exif:ResolutionYUnit" },
921       {  0x5003, "exif:ResolutionXLengthUnit" },
922       {  0x5004, "exif:ResolutionYLengthUnit" },
923       {  0x5005, "exif:PrintFlags" },
924       {  0x5006, "exif:PrintFlagsVersion" },
925       {  0x5007, "exif:PrintFlagsCrop" },
926       {  0x5008, "exif:PrintFlagsBleedWidth" },
927       {  0x5009, "exif:PrintFlagsBleedWidthScale" },
928       {  0x500A, "exif:HalftoneLPI" },
929       {  0x500B, "exif:HalftoneLPIUnit" },
930       {  0x500C, "exif:HalftoneDegree" },
931       {  0x500D, "exif:HalftoneShape" },
932       {  0x500E, "exif:HalftoneMisc" },
933       {  0x500F, "exif:HalftoneScreen" },
934       {  0x5010, "exif:JPEGQuality" },
935       {  0x5011, "exif:GridSize" },
936       {  0x5012, "exif:ThumbnailFormat" },
937       {  0x5013, "exif:ThumbnailWidth" },
938       {  0x5014, "exif:ThumbnailHeight" },
939       {  0x5015, "exif:ThumbnailColorDepth" },
940       {  0x5016, "exif:ThumbnailPlanes" },
941       {  0x5017, "exif:ThumbnailRawBytes" },
942       {  0x5018, "exif:ThumbnailSize" },
943       {  0x5019, "exif:ThumbnailCompressedSize" },
944       {  0x501a, "exif:ColorTransferFunction" },
945       {  0x501b, "exif:ThumbnailData" },
946       {  0x5020, "exif:ThumbnailImageWidth" },
947       {  0x5021, "exif:ThumbnailImageHeight" },
948       {  0x5022, "exif:ThumbnailBitsPerSample" },
949       {  0x5023, "exif:ThumbnailCompression" },
950       {  0x5024, "exif:ThumbnailPhotometricInterp" },
951       {  0x5025, "exif:ThumbnailImageDescription" },
952       {  0x5026, "exif:ThumbnailEquipMake" },
953       {  0x5027, "exif:ThumbnailEquipModel" },
954       {  0x5028, "exif:ThumbnailStripOffsets" },
955       {  0x5029, "exif:ThumbnailOrientation" },
956       {  0x502a, "exif:ThumbnailSamplesPerPixel" },
957       {  0x502b, "exif:ThumbnailRowsPerStrip" },
958       {  0x502c, "exif:ThumbnailStripBytesCount" },
959       {  0x502d, "exif:ThumbnailResolutionX" },
960       {  0x502e, "exif:ThumbnailResolutionY" },
961       {  0x502f, "exif:ThumbnailPlanarConfig" },
962       {  0x5030, "exif:ThumbnailResolutionUnit" },
963       {  0x5031, "exif:ThumbnailTransferFunction" },
964       {  0x5032, "exif:ThumbnailSoftwareUsed" },
965       {  0x5033, "exif:ThumbnailDateTime" },
966       {  0x5034, "exif:ThumbnailArtist" },
967       {  0x5035, "exif:ThumbnailWhitePoint" },
968       {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
969       {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
970       {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
971       {  0x5039, "exif:ThumbnailYCbCrPositioning" },
972       {  0x503A, "exif:ThumbnailRefBlackWhite" },
973       {  0x503B, "exif:ThumbnailCopyRight" },
974       {  0x5090, "exif:LuminanceTable" },
975       {  0x5091, "exif:ChrominanceTable" },
976       {  0x5100, "exif:FrameDelay" },
977       {  0x5101, "exif:LoopCount" },
978       {  0x5110, "exif:PixelUnit" },
979       {  0x5111, "exif:PixelPerUnitX" },
980       {  0x5112, "exif:PixelPerUnitY" },
981       {  0x5113, "exif:PaletteHistogram" },
982       {  0x1000, "exif:RelatedImageFileFormat" },
983       {  0x1001, "exif:RelatedImageLength" },
984       {  0x1002, "exif:RelatedImageWidth" },
985       {  0x800d, "exif:ImageID" },
986       {  0x80e3, "exif:Matteing" },
987       {  0x80e4, "exif:DataType" },
988       {  0x80e5, "exif:ImageDepth" },
989       {  0x80e6, "exif:TileDepth" },
990       {  0x828d, "exif:CFARepeatPatternDim" },
991       {  0x828e, "exif:CFAPattern2" },
992       {  0x828f, "exif:BatteryLevel" },
993       {  0x8298, "exif:Copyright" },
994       {  0x829a, "exif:ExposureTime" },
995       {  0x829d, "exif:FNumber" },
996       {  0x83bb, "exif:IPTC/NAA" },
997       {  0x84e3, "exif:IT8RasterPadding" },
998       {  0x84e5, "exif:IT8ColorTable" },
999       {  0x8649, "exif:ImageResourceInformation" },
1000       {  0x8769, "exif:ExifOffset" },
1001       {  0x8773, "exif:InterColorProfile" },
1002       {  0x8822, "exif:ExposureProgram" },
1003       {  0x8824, "exif:SpectralSensitivity" },
1004       {  0x8825, "exif:GPSInfo" },
1005       {  0x8827, "exif:ISOSpeedRatings" },
1006       {  0x8828, "exif:OECF" },
1007       {  0x8829, "exif:Interlace" },
1008       {  0x882a, "exif:TimeZoneOffset" },
1009       {  0x882b, "exif:SelfTimerMode" },
1010       {  0x9000, "exif:ExifVersion" },
1011       {  0x9003, "exif:DateTimeOriginal" },
1012       {  0x9004, "exif:DateTimeDigitized" },
1013       {  0x9101, "exif:ComponentsConfiguration" },
1014       {  0x9102, "exif:CompressedBitsPerPixel" },
1015       {  0x9201, "exif:ShutterSpeedValue" },
1016       {  0x9202, "exif:ApertureValue" },
1017       {  0x9203, "exif:BrightnessValue" },
1018       {  0x9204, "exif:ExposureBiasValue" },
1019       {  0x9205, "exif:MaxApertureValue" },
1020       {  0x9206, "exif:SubjectDistance" },
1021       {  0x9207, "exif:MeteringMode" },
1022       {  0x9208, "exif:LightSource" },
1023       {  0x9209, "exif:Flash" },
1024       {  0x920a, "exif:FocalLength" },
1025       {  0x920b, "exif:FlashEnergy" },
1026       {  0x920c, "exif:SpatialFrequencyResponse" },
1027       {  0x920d, "exif:Noise" },
1028       {  0x9211, "exif:ImageNumber" },
1029       {  0x9212, "exif:SecurityClassification" },
1030       {  0x9213, "exif:ImageHistory" },
1031       {  0x9214, "exif:SubjectArea" },
1032       {  0x9215, "exif:ExposureIndex" },
1033       {  0x9216, "exif:TIFF-EPStandardID" },
1034       {  0x927c, "exif:MakerNote" },
1035       {  0x9C9b, "exif:WinXP-Title" },
1036       {  0x9C9c, "exif:WinXP-Comments" },
1037       {  0x9C9d, "exif:WinXP-Author" },
1038       {  0x9C9e, "exif:WinXP-Keywords" },
1039       {  0x9C9f, "exif:WinXP-Subject" },
1040       {  0x9286, "exif:UserComment" },
1041       {  0x9290, "exif:SubSecTime" },
1042       {  0x9291, "exif:SubSecTimeOriginal" },
1043       {  0x9292, "exif:SubSecTimeDigitized" },
1044       {  0xa000, "exif:FlashPixVersion" },
1045       {  0xa001, "exif:ColorSpace" },
1046       {  0xa002, "exif:ExifImageWidth" },
1047       {  0xa003, "exif:ExifImageLength" },
1048       {  0xa004, "exif:RelatedSoundFile" },
1049       {  0xa005, "exif:InteroperabilityOffset" },
1050       {  0xa20b, "exif:FlashEnergy" },
1051       {  0xa20c, "exif:SpatialFrequencyResponse" },
1052       {  0xa20d, "exif:Noise" },
1053       {  0xa20e, "exif:FocalPlaneXResolution" },
1054       {  0xa20f, "exif:FocalPlaneYResolution" },
1055       {  0xa210, "exif:FocalPlaneResolutionUnit" },
1056       {  0xa214, "exif:SubjectLocation" },
1057       {  0xa215, "exif:ExposureIndex" },
1058       {  0xa216, "exif:TIFF/EPStandardID" },
1059       {  0xa217, "exif:SensingMethod" },
1060       {  0xa300, "exif:FileSource" },
1061       {  0xa301, "exif:SceneType" },
1062       {  0xa302, "exif:CFAPattern" },
1063       {  0xa401, "exif:CustomRendered" },
1064       {  0xa402, "exif:ExposureMode" },
1065       {  0xa403, "exif:WhiteBalance" },
1066       {  0xa404, "exif:DigitalZoomRatio" },
1067       {  0xa405, "exif:FocalLengthIn35mmFilm" },
1068       {  0xa406, "exif:SceneCaptureType" },
1069       {  0xa407, "exif:GainControl" },
1070       {  0xa408, "exif:Contrast" },
1071       {  0xa409, "exif:Saturation" },
1072       {  0xa40a, "exif:Sharpness" },
1073       {  0xa40b, "exif:DeviceSettingDescription" },
1074       {  0xa40c, "exif:SubjectDistanceRange" },
1075       {  0xa420, "exif:ImageUniqueID" },
1076       {  0xc4a5, "exif:PrintImageMatching" },
1077       {  0xa500, "exif:Gamma" },
1078       {  0xc640, "exif:CR2Slice" },
1079       { 0x10000, "exif:GPSVersionID" },
1080       { 0x10001, "exif:GPSLatitudeRef" },
1081       { 0x10002, "exif:GPSLatitude" },
1082       { 0x10003, "exif:GPSLongitudeRef" },
1083       { 0x10004, "exif:GPSLongitude" },
1084       { 0x10005, "exif:GPSAltitudeRef" },
1085       { 0x10006, "exif:GPSAltitude" },
1086       { 0x10007, "exif:GPSTimeStamp" },
1087       { 0x10008, "exif:GPSSatellites" },
1088       { 0x10009, "exif:GPSStatus" },
1089       { 0x1000a, "exif:GPSMeasureMode" },
1090       { 0x1000b, "exif:GPSDop" },
1091       { 0x1000c, "exif:GPSSpeedRef" },
1092       { 0x1000d, "exif:GPSSpeed" },
1093       { 0x1000e, "exif:GPSTrackRef" },
1094       { 0x1000f, "exif:GPSTrack" },
1095       { 0x10010, "exif:GPSImgDirectionRef" },
1096       { 0x10011, "exif:GPSImgDirection" },
1097       { 0x10012, "exif:GPSMapDatum" },
1098       { 0x10013, "exif:GPSDestLatitudeRef" },
1099       { 0x10014, "exif:GPSDestLatitude" },
1100       { 0x10015, "exif:GPSDestLongitudeRef" },
1101       { 0x10016, "exif:GPSDestLongitude" },
1102       { 0x10017, "exif:GPSDestBearingRef" },
1103       { 0x10018, "exif:GPSDestBearing" },
1104       { 0x10019, "exif:GPSDestDistanceRef" },
1105       { 0x1001a, "exif:GPSDestDistance" },
1106       { 0x1001b, "exif:GPSProcessingMethod" },
1107       { 0x1001c, "exif:GPSAreaInformation" },
1108       { 0x1001d, "exif:GPSDateStamp" },
1109       { 0x1001e, "exif:GPSDifferential" },
1110       { 0x00000, (const char *) NULL }
1111     };
1112
1113   const StringInfo
1114     *profile;
1115
1116   const unsigned char
1117     *directory,
1118     *exif;
1119
1120   DirectoryInfo
1121     directory_stack[MaxDirectoryStack];
1122
1123   EndianType
1124     endian;
1125
1126   MagickBooleanType
1127     status;
1128
1129   register ssize_t
1130     i;
1131
1132   size_t
1133     entry,
1134     length,
1135     number_entries,
1136     tag;
1137
1138   SplayTreeInfo
1139     *exif_resources;
1140
1141   ssize_t
1142     all,
1143     id,
1144     level,
1145     offset,
1146     tag_offset,
1147     tag_value;
1148
1149   static int
1150     tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1151
1152   /*
1153     If EXIF data exists, then try to parse the request for a tag.
1154   */
1155   profile=GetImageProfile(image,"exif");
1156   if (profile == (const StringInfo *) NULL)
1157     return(MagickFalse);
1158   if ((property == (const char *) NULL) || (*property == '\0'))
1159     return(MagickFalse);
1160   while (isspace((int) ((unsigned char) *property)) != 0)
1161     property++;
1162   if (strlen(property) <= 5)
1163     return(MagickFalse);
1164   all=0;
1165   tag=(~0UL);
1166   switch (*(property+5))
1167   {
1168     case '*':
1169     {
1170       /*
1171         Caller has asked for all the tags in the EXIF data.
1172       */
1173       tag=0;
1174       all=1; /* return the data in description=value format */
1175       break;
1176     }
1177     case '!':
1178     {
1179       tag=0;
1180       all=2; /* return the data in tagid=value format */
1181       break;
1182     }
1183     case '#':
1184     case '@':
1185     {
1186       int
1187         c;
1188
1189       size_t
1190         n;
1191
1192       /*
1193         Check for a hex based tag specification first.
1194       */
1195       tag=(*(property+5) == '@') ? 1UL : 0UL;
1196       property+=6;
1197       n=strlen(property);
1198       if (n != 4)
1199         return(MagickFalse);
1200       /*
1201         Parse tag specification as a hex number.
1202       */
1203       n/=4;
1204       do
1205       {
1206         for (i=(ssize_t) n-1L; i >= 0; i--)
1207         {
1208           c=(*property++);
1209           tag<<=4;
1210           if ((c >= '0') && (c <= '9'))
1211             tag|=(c-'0');
1212           else
1213             if ((c >= 'A') && (c <= 'F'))
1214               tag|=(c-('A'-10));
1215             else
1216               if ((c >= 'a') && (c <= 'f'))
1217                 tag|=(c-('a'-10));
1218               else
1219                 return(MagickFalse);
1220         }
1221       } while (*property != '\0');
1222       break;
1223     }
1224     default:
1225     {
1226       /*
1227         Try to match the text with a tag name instead.
1228       */
1229       for (i=0; ; i++)
1230       {
1231         if (EXIFTag[i].tag == 0)
1232           break;
1233         if (LocaleCompare(EXIFTag[i].description,property) == 0)
1234           {
1235             tag=(size_t) EXIFTag[i].tag;
1236             break;
1237           }
1238       }
1239       break;
1240     }
1241   }
1242   if (tag == (~0UL))
1243     return(MagickFalse);
1244   length=GetStringInfoLength(profile);
1245   exif=GetStringInfoDatum(profile);
1246   while (length != 0)
1247   {
1248     if (ReadPropertyByte(&exif,&length) != 0x45)
1249       continue;
1250     if (ReadPropertyByte(&exif,&length) != 0x78)
1251       continue;
1252     if (ReadPropertyByte(&exif,&length) != 0x69)
1253       continue;
1254     if (ReadPropertyByte(&exif,&length) != 0x66)
1255       continue;
1256     if (ReadPropertyByte(&exif,&length) != 0x00)
1257       continue;
1258     if (ReadPropertyByte(&exif,&length) != 0x00)
1259       continue;
1260     break;
1261   }
1262   if (length < 16)
1263     return(MagickFalse);
1264   id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
1265   endian=LSBEndian;
1266   if (id == 0x4949)
1267     endian=LSBEndian;
1268   else
1269     if (id == 0x4D4D)
1270       endian=MSBEndian;
1271     else
1272       return(MagickFalse);
1273   if (ReadPropertyShort(endian,exif+2) != 0x002a)
1274     return(MagickFalse);
1275   /*
1276     This the offset to the first IFD.
1277   */
1278   offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
1279   if ((offset < 0) || (size_t) offset >= length)
1280     return(MagickFalse);
1281   /*
1282     Set the pointer to the first IFD and follow it were it leads.
1283   */
1284   status=MagickFalse;
1285   directory=exif+offset;
1286   level=0;
1287   entry=0;
1288   tag_offset=0;
1289   exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1290     (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1291   do
1292   {
1293     /*
1294       If there is anything on the stack then pop it off.
1295     */
1296     if (level > 0)
1297       {
1298         level--;
1299         directory=directory_stack[level].directory;
1300         entry=directory_stack[level].entry;
1301         tag_offset=directory_stack[level].offset;
1302       }
1303     if ((directory < exif) || (directory > (exif+length-2)))
1304       break;
1305     /*
1306       Determine how many entries there are in the current IFD.
1307     */
1308     number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
1309     for ( ; entry < number_entries; entry++)
1310     {
1311       register unsigned char
1312         *p,
1313         *q;
1314
1315       size_t
1316         format;
1317
1318       ssize_t
1319         number_bytes,
1320         components;
1321
1322       q=(unsigned char *) (directory+(12*entry)+2);
1323       if (GetValueFromSplayTree(exif_resources,q) == q)
1324         break;
1325       (void) AddValueToSplayTree(exif_resources,q,q);
1326       tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
1327       format=(size_t) ((int) ReadPropertyShort(endian,q+2));
1328       if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1329         break;
1330       components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
1331       number_bytes=(size_t) components*tag_bytes[format];
1332       if (number_bytes < components)
1333         break;  /* prevent overflow */
1334       if (number_bytes <= 4)
1335         p=q+8;
1336       else
1337         {
1338           ssize_t
1339             offset;
1340
1341           /*
1342             The directory entry contains an offset.
1343           */
1344           offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
1345           if ((offset < 0) || (size_t) offset >= length)
1346             continue;
1347           if ((ssize_t) (offset+number_bytes) < offset)
1348             continue;  /* prevent overflow */
1349           if ((size_t) (offset+number_bytes) > length)
1350             continue;
1351           p=(unsigned char *) (exif+offset);
1352         }
1353       if ((all != 0) || (tag == (size_t) tag_value))
1354         {
1355           char
1356             buffer[MagickPathExtent],
1357             *value;
1358
1359           value=(char *) NULL;
1360           *buffer='\0';
1361           switch (format)
1362           {
1363             case EXIF_FMT_BYTE:
1364             case EXIF_FMT_UNDEFINED:
1365             {
1366               EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
1367               break;
1368             }
1369             case EXIF_FMT_SBYTE:
1370             {
1371               EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
1372               break;
1373             }
1374             case EXIF_FMT_SSHORT:
1375             {
1376               EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
1377               break;
1378             }
1379             case EXIF_FMT_USHORT:
1380             {
1381               EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
1382               break;
1383             }
1384             case EXIF_FMT_ULONG:
1385             {
1386               EXIFMultipleValues(4,"%.20g",(double)
1387                 ((int) ReadPropertyLong(endian,p1)));
1388               break;
1389             }
1390             case EXIF_FMT_SLONG:
1391             {
1392               EXIFMultipleValues(4,"%.20g",(double)
1393                 ((int) ReadPropertyLong(endian,p1)));
1394               break;
1395             }
1396             case EXIF_FMT_URATIONAL:
1397             {
1398               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
1399                 ((int) ReadPropertyLong(endian,p1)),(double)
1400                 ((int) ReadPropertyLong(endian,p1+4)));
1401               break;
1402             }
1403             case EXIF_FMT_SRATIONAL:
1404             {
1405               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
1406                 ((int) ReadPropertyLong(endian,p1)),(double)
1407                 ((int) ReadPropertyLong(endian,p1+4)));
1408               break;
1409             }
1410             case EXIF_FMT_SINGLE:
1411             {
1412               EXIFMultipleValues(4,"%f",(double) *(float *) p1);
1413               break;
1414             }
1415             case EXIF_FMT_DOUBLE:
1416             {
1417               EXIFMultipleValues(8,"%f",*(double *) p1);
1418               break;
1419             }
1420             default:
1421             case EXIF_FMT_STRING:
1422             {
1423               value=(char *) NULL;
1424               if (~((size_t) number_bytes) >= 1)
1425                 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1426                   sizeof(*value));
1427               if (value != (char *) NULL)
1428                 {
1429                   register ssize_t
1430                     i;
1431
1432                   for (i=0; i < (ssize_t) number_bytes; i++)
1433                   {
1434                     value[i]='.';
1435                     if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1436                       value[i]=(char) p[i];
1437                   }
1438                   value[i]='\0';
1439                 }
1440               break;
1441             }
1442           }
1443           if (value != (char *) NULL)
1444             {
1445               char
1446                 *key;
1447
1448               register const char
1449                 *p;
1450
1451               key=AcquireString(property);
1452               switch (all)
1453               {
1454                 case 1:
1455                 {
1456                   const char
1457                     *description;
1458
1459                   register ssize_t
1460                     i;
1461
1462                   description="unknown";
1463                   for (i=0; ; i++)
1464                   {
1465                     if (EXIFTag[i].tag == 0)
1466                       break;
1467                     if ((ssize_t) EXIFTag[i].tag == tag_value)
1468                       {
1469                         description=EXIFTag[i].description;
1470                         break;
1471                       }
1472                   }
1473                   (void) FormatLocaleString(key,MagickPathExtent,"%s",description);
1474                   if (level == 2)
1475                     (void) SubstituteString(&key,"exif:","exif:thumbnail:");
1476                   break;
1477                 }
1478                 case 2:
1479                 {
1480                   if (tag_value < 0x10000)
1481                     (void) FormatLocaleString(key,MagickPathExtent,"#%04lx",
1482                       (unsigned long) tag_value);
1483                   else
1484                     if (tag_value < 0x20000)
1485                       (void) FormatLocaleString(key,MagickPathExtent,"@%04lx",
1486                         (unsigned long) (tag_value & 0xffff));
1487                     else
1488                       (void) FormatLocaleString(key,MagickPathExtent,"unknown");
1489                   break;
1490                 }
1491                 default:
1492                 {
1493                   if (level == 2)
1494                     (void) SubstituteString(&key,"exif:","exif:thumbnail:");
1495                 }
1496               }
1497               p=(const char *) NULL;
1498               if (image->properties != (void *) NULL)
1499                 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1500                   image->properties,key);
1501               if (p == (const char *) NULL)
1502                 (void) SetImageProperty((Image *) image,key,value,exception);
1503               value=DestroyString(value);
1504               key=DestroyString(key);
1505               status=MagickTrue;
1506             }
1507         }
1508         if ((tag_value == TAG_EXIF_OFFSET) ||
1509             (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
1510           {
1511             ssize_t
1512               offset;
1513
1514             offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
1515             if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1516               {
1517                 ssize_t
1518                   tag_offset1;
1519
1520                 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1521                   0);
1522                 directory_stack[level].directory=directory;
1523                 entry++;
1524                 directory_stack[level].entry=entry;
1525                 directory_stack[level].offset=tag_offset;
1526                 level++;
1527                 directory_stack[level].directory=exif+offset;
1528                 directory_stack[level].offset=tag_offset1;
1529                 directory_stack[level].entry=0;
1530                 level++;
1531                 if ((directory+2+(12*number_entries)) > (exif+length))
1532                   break;
1533                 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
1534                   number_entries)));
1535                 if ((offset != 0) && ((size_t) offset < length) &&
1536                     (level < (MaxDirectoryStack-2)))
1537                   {
1538                     directory_stack[level].directory=exif+offset;
1539                     directory_stack[level].entry=0;
1540                     directory_stack[level].offset=tag_offset1;
1541                     level++;
1542                   }
1543               }
1544             break;
1545           }
1546     }
1547   } while (level > 0);
1548   exif_resources=DestroySplayTree(exif_resources);
1549   return(status);
1550 }
1551
1552 static MagickBooleanType GetICCProperty(const Image *image,const char *property,
1553   ExceptionInfo *exception)
1554 {
1555   const StringInfo
1556     *profile;
1557
1558   magick_unreferenced(property);
1559
1560   profile=GetImageProfile(image,"icc");
1561   if (profile == (StringInfo *) NULL)
1562     profile=GetImageProfile(image,"icm");
1563   if (profile == (StringInfo *) NULL)
1564     return(MagickFalse);
1565   if (GetStringInfoLength(profile) < 128)
1566     return(MagickFalse);  /* minimum ICC profile length */
1567 #if defined(MAGICKCORE_LCMS_DELEGATE)
1568   {
1569     cmsHPROFILE
1570       icc_profile;
1571
1572     icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
1573       (cmsUInt32Number) GetStringInfoLength(profile));
1574     if (icc_profile != (cmsHPROFILE *) NULL)
1575       {
1576 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
1577         const char
1578           *name;
1579
1580         name=cmsTakeProductName(icc_profile);
1581         if (name != (const char *) NULL)
1582           (void) SetImageProperty((Image *) image,"icc:name",name,exception);
1583 #else
1584         char
1585           info[MagickPathExtent];
1586
1587         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
1588           "en","US",info,MagickPathExtent);
1589         (void) SetImageProperty((Image *) image,"icc:description",info,
1590           exception);
1591         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,
1592           "en","US",info,MagickPathExtent);
1593         (void) SetImageProperty((Image *) image,"icc:manufacturer",info,
1594           exception);
1595         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en",
1596           "US",info,MagickPathExtent);
1597         (void) SetImageProperty((Image *) image,"icc:model",info,exception);
1598         (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,
1599           "en","US",info,MagickPathExtent);
1600         (void) SetImageProperty((Image *) image,"icc:copyright",info,exception);
1601 #endif
1602         (void) cmsCloseProfile(icc_profile);
1603       }
1604   }
1605 #endif
1606   return(MagickTrue);
1607 }
1608
1609 static MagickBooleanType SkipXMPValue(const char *value)
1610 {
1611   if (value == (const char*) NULL)
1612     return(MagickTrue);
1613   while (*value != '\0')
1614   {
1615     if (isspace((int) ((unsigned char) *value)) == 0)
1616       return(MagickFalse);
1617     value++;
1618   }
1619   return(MagickTrue);
1620 }
1621
1622 static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
1623 {
1624   char
1625     *xmp_profile;
1626
1627   const char
1628     *content;
1629
1630   const StringInfo
1631     *profile;
1632
1633   ExceptionInfo
1634     *exception;
1635
1636   MagickBooleanType
1637     status;
1638
1639   register const char
1640     *p;
1641
1642   XMLTreeInfo
1643     *child,
1644     *description,
1645     *node,
1646     *rdf,
1647     *xmp;
1648
1649   profile=GetImageProfile(image,"xmp");
1650   if (profile == (StringInfo *) NULL)
1651     return(MagickFalse);
1652   if ((property == (const char *) NULL) || (*property == '\0'))
1653     return(MagickFalse);
1654   xmp_profile=StringInfoToString(profile);
1655   if (xmp_profile == (char *) NULL)
1656     return(MagickFalse);
1657   for (p=xmp_profile; *p != '\0'; p++)
1658     if ((*p == '<') && (*(p+1) == 'x'))
1659       break;
1660   exception=AcquireExceptionInfo();
1661   xmp=NewXMLTree((char *) p,exception);
1662   xmp_profile=DestroyString(xmp_profile);
1663   exception=DestroyExceptionInfo(exception);
1664   if (xmp == (XMLTreeInfo *) NULL)
1665     return(MagickFalse);
1666   status=MagickFalse;
1667   rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1668   if (rdf != (XMLTreeInfo *) NULL)
1669     {
1670       if (image->properties == (void *) NULL)
1671         ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1672           RelinquishMagickMemory,RelinquishMagickMemory);
1673       description=GetXMLTreeChild(rdf,"rdf:Description");
1674       while (description != (XMLTreeInfo *) NULL)
1675       {
1676         node=GetXMLTreeChild(description,(const char *) NULL);
1677         while (node != (XMLTreeInfo *) NULL)
1678         {
1679           child=GetXMLTreeChild(node,(const char *) NULL);
1680           content=GetXMLTreeContent(node);
1681           if ((child == (XMLTreeInfo *) NULL) &&
1682               (SkipXMPValue(content) == MagickFalse))
1683             (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1684               ConstantString(GetXMLTreeTag(node)),ConstantString(content));
1685           while (child != (XMLTreeInfo *) NULL)
1686           {
1687             content=GetXMLTreeContent(child);
1688             if (SkipXMPValue(content) == MagickFalse)
1689               (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1690                 ConstantString(GetXMLTreeTag(child)),ConstantString(content));
1691             child=GetXMLTreeSibling(child);
1692           }
1693           node=GetXMLTreeSibling(node);
1694         }
1695         description=GetNextXMLTreeTag(description);
1696       }
1697     }
1698   xmp=DestroyXMLTree(xmp);
1699   return(status);
1700 }
1701
1702 static char *TracePSClippath(const unsigned char *blob,size_t length,
1703   const size_t magick_unused(columns),const size_t magick_unused(rows))
1704 {
1705   char
1706     *path,
1707     *message;
1708
1709   MagickBooleanType
1710     in_subpath;
1711
1712   PointInfo
1713     first[3],
1714     last[3],
1715     point[3];
1716
1717   register ssize_t
1718     i,
1719     x;
1720
1721   ssize_t
1722     knot_count,
1723     selector,
1724     y;
1725
1726   path=AcquireString((char *) NULL);
1727   if (path == (char *) NULL)
1728     return((char *) NULL);
1729   message=AcquireString((char *) NULL);
1730   (void) FormatLocaleString(message,MagickPathExtent,"/ClipImage\n");
1731   (void) ConcatenateString(&path,message);
1732   (void) FormatLocaleString(message,MagickPathExtent,"{\n");
1733   (void) ConcatenateString(&path,message);
1734   (void) FormatLocaleString(message,MagickPathExtent,"  /c {curveto} bind def\n");
1735   (void) ConcatenateString(&path,message);
1736   (void) FormatLocaleString(message,MagickPathExtent,"  /l {lineto} bind def\n");
1737   (void) ConcatenateString(&path,message);
1738   (void) FormatLocaleString(message,MagickPathExtent,"  /m {moveto} bind def\n");
1739   (void) ConcatenateString(&path,message);
1740   (void) FormatLocaleString(message,MagickPathExtent,
1741     "  /v {currentpoint 6 2 roll curveto} bind def\n");
1742   (void) ConcatenateString(&path,message);
1743   (void) FormatLocaleString(message,MagickPathExtent,
1744     "  /y {2 copy curveto} bind def\n");
1745   (void) ConcatenateString(&path,message);
1746   (void) FormatLocaleString(message,MagickPathExtent,
1747     "  /z {closepath} bind def\n");
1748   (void) ConcatenateString(&path,message);
1749   (void) FormatLocaleString(message,MagickPathExtent,"  newpath\n");
1750   (void) ConcatenateString(&path,message);
1751   /*
1752     The clipping path format is defined in "Adobe Photoshop File
1753     Formats Specification" version 6.0 downloadable from adobe.com.
1754   */
1755   (void) ResetMagickMemory(point,0,sizeof(point));
1756   (void) ResetMagickMemory(first,0,sizeof(first));
1757   (void) ResetMagickMemory(last,0,sizeof(last));
1758   knot_count=0;
1759   in_subpath=MagickFalse;
1760   while (length > 0)
1761   {
1762     selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
1763     switch (selector)
1764     {
1765       case 0:
1766       case 3:
1767       {
1768         if (knot_count != 0)
1769           {
1770             blob+=24;
1771             length-=MagickMin(24,(ssize_t) length);
1772             break;
1773           }
1774         /*
1775           Expected subpath length record.
1776         */
1777         knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
1778         blob+=22;
1779         length-=MagickMin(22,(ssize_t) length);
1780         break;
1781       }
1782       case 1:
1783       case 2:
1784       case 4:
1785       case 5:
1786       {
1787         if (knot_count == 0)
1788           {
1789             /*
1790               Unexpected subpath knot
1791             */
1792             blob+=24;
1793             length-=MagickMin(24,(ssize_t) length);
1794             break;
1795           }
1796         /*
1797           Add sub-path knot
1798         */
1799         for (i=0; i < 3; i++)
1800         {
1801           size_t
1802             xx,
1803             yy;
1804
1805           yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1806           xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1807           x=(ssize_t) xx;
1808           if (xx > 2147483647)
1809             x=(ssize_t) xx-4294967295U-1;
1810           y=(ssize_t) yy;
1811           if (yy > 2147483647)
1812             y=(ssize_t) yy-4294967295U-1;
1813           point[i].x=(double) x/4096/4096;
1814           point[i].y=1.0-(double) y/4096/4096;
1815         }
1816         if( IfMagickFalse(in_subpath) )
1817           {
1818             (void) FormatLocaleString(message,MagickPathExtent,"  %g %g m\n",
1819               point[1].x,point[1].y);
1820             for (i=0; i < 3; i++)
1821             {
1822               first[i]=point[i];
1823               last[i]=point[i];
1824             }
1825           }
1826         else
1827           {
1828             /*
1829               Handle special cases when Bezier curves are used to describe
1830               corners and straight lines.
1831             */
1832             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1833                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
1834               (void) FormatLocaleString(message,MagickPathExtent,
1835                 "  %g %g l\n",point[1].x,point[1].y);
1836             else
1837               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
1838                 (void) FormatLocaleString(message,MagickPathExtent,
1839                   "  %g %g %g %g v\n",point[0].x,point[0].y,
1840                   point[1].x,point[1].y);
1841               else
1842                 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
1843                   (void) FormatLocaleString(message,MagickPathExtent,
1844                     "  %g %g %g %g y\n",last[2].x,last[2].y,
1845                     point[1].x,point[1].y);
1846                 else
1847                   (void) FormatLocaleString(message,MagickPathExtent,
1848                     "  %g %g %g %g %g %g c\n",last[2].x,
1849                     last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
1850             for (i=0; i < 3; i++)
1851               last[i]=point[i];
1852           }
1853         (void) ConcatenateString(&path,message);
1854         in_subpath=MagickTrue;
1855         knot_count--;
1856         /*
1857           Close the subpath if there are no more knots.
1858         */
1859         if (knot_count == 0)
1860           {
1861             /*
1862               Same special handling as above except we compare to the
1863               first point in the path and close the path.
1864             */
1865             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1866                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
1867               (void) FormatLocaleString(message,MagickPathExtent,
1868                 "  %g %g l z\n",first[1].x,first[1].y);
1869             else
1870               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
1871                 (void) FormatLocaleString(message,MagickPathExtent,
1872                   "  %g %g %g %g v z\n",first[0].x,first[0].y,
1873                   first[1].x,first[1].y);
1874               else
1875                 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
1876                   (void) FormatLocaleString(message,MagickPathExtent,
1877                     "  %g %g %g %g y z\n",last[2].x,last[2].y,
1878                     first[1].x,first[1].y);
1879                 else
1880                   (void) FormatLocaleString(message,MagickPathExtent,
1881                     "  %g %g %g %g %g %g c z\n",last[2].x,
1882                     last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
1883             (void) ConcatenateString(&path,message);
1884             in_subpath=MagickFalse;
1885           }
1886         break;
1887       }
1888       case 6:
1889       case 7:
1890       case 8:
1891       default:
1892       {
1893         blob+=24;
1894         length-=MagickMin(24,(ssize_t) length);
1895         break;
1896       }
1897     }
1898   }
1899   /*
1900     Returns an empty PS path if the path has no knots.
1901   */
1902   (void) FormatLocaleString(message,MagickPathExtent,"  eoclip\n");
1903   (void) ConcatenateString(&path,message);
1904   (void) FormatLocaleString(message,MagickPathExtent,"} bind def");
1905   (void) ConcatenateString(&path,message);
1906   message=DestroyString(message);
1907   return(path);
1908 }
1909
1910 static char *TraceSVGClippath(const unsigned char *blob,size_t length,
1911   const size_t columns,const size_t rows)
1912 {
1913   char
1914     *path,
1915     *message;
1916
1917   MagickBooleanType
1918     in_subpath;
1919
1920   PointInfo
1921     first[3],
1922     last[3],
1923     point[3];
1924
1925   register ssize_t
1926     i;
1927
1928   ssize_t
1929     knot_count,
1930     selector,
1931     x,
1932     y;
1933
1934   path=AcquireString((char *) NULL);
1935   if (path == (char *) NULL)
1936     return((char *) NULL);
1937   message=AcquireString((char *) NULL);
1938     (void) FormatLocaleString(message,MagickPathExtent,
1939     ("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
1940     "<svg xmlns=\"http://www.w3.org/2000/svg\""
1941     " width=\"%.20g\" height=\"%.20g\">\n"
1942     "<g>\n"
1943     "<path fill-rule=\"evenodd\" style=\"fill:#00000000;stroke:#00000000;"
1944     "stroke-width:0;stroke-antialiasing:false\" d=\"\n"),
1945     (double) columns,(double) rows);
1946   (void) ConcatenateString(&path,message);
1947   (void) ResetMagickMemory(point,0,sizeof(point));
1948   (void) ResetMagickMemory(first,0,sizeof(first));
1949   (void) ResetMagickMemory(last,0,sizeof(last));
1950   knot_count=0;
1951   in_subpath=MagickFalse;
1952   while (length != 0)
1953   {
1954     selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
1955     switch (selector)
1956     {
1957       case 0:
1958       case 3:
1959       {
1960         if (knot_count != 0)
1961           {
1962             blob+=24;
1963             length-=MagickMin(24,(ssize_t) length);
1964             break;
1965           }
1966         /*
1967           Expected subpath length record.
1968         */
1969         knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
1970         blob+=22;
1971         length-=MagickMin(22,(ssize_t) length);
1972         break;
1973       }
1974       case 1:
1975       case 2:
1976       case 4:
1977       case 5:
1978       {
1979         if (knot_count == 0)
1980           {
1981             /*
1982               Unexpected subpath knot.
1983             */
1984             blob+=24;
1985             length-=MagickMin(24,(ssize_t) length);
1986             break;
1987           }
1988         /*
1989           Add sub-path knot
1990         */
1991         for (i=0; i < 3; i++)
1992         {
1993           unsigned int
1994             xx,
1995             yy;
1996
1997           yy=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length));
1998           xx=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length));
1999           x=(ssize_t) xx;
2000           if (xx > 2147483647)
2001             x=(ssize_t) xx-4294967295U-1;
2002           y=(ssize_t) yy;
2003           if (yy > 2147483647)
2004             y=(ssize_t) yy-4294967295U-1;
2005           point[i].x=(double) x*columns/4096/4096;
2006           point[i].y=(double) y*rows/4096/4096;
2007         }
2008         if (in_subpath == MagickFalse)
2009           {
2010             (void) FormatLocaleString(message,MagickPathExtent,"M %g %g\n",
2011               point[1].x,point[1].y);
2012             for (i=0; i < 3; i++)
2013             {
2014               first[i]=point[i];
2015               last[i]=point[i];
2016             }
2017           }
2018         else
2019           {
2020             /*
2021               Handle special cases when Bezier curves are used to describe
2022               corners and straight lines.
2023             */
2024             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2025                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
2026               (void) FormatLocaleString(message,MagickPathExtent,
2027                 "L %g %g\n",point[1].x,point[1].y);
2028             else
2029               (void) FormatLocaleString(message,MagickPathExtent,
2030                 "C %g %g %g %g %g %g\n",last[2].x,
2031                 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
2032             for (i=0; i < 3; i++)
2033               last[i]=point[i];
2034           }
2035         (void) ConcatenateString(&path,message);
2036         in_subpath=MagickTrue;
2037         knot_count--;
2038         /*
2039           Close the subpath if there are no more knots.
2040         */
2041         if (knot_count == 0)
2042           {
2043            /*
2044               Same special handling as above except we compare to the
2045               first point in the path and close the path.
2046             */
2047             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2048                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
2049               (void) FormatLocaleString(message,MagickPathExtent,
2050                 "L %g %g Z\n",first[1].x,first[1].y);
2051             else
2052               (void) FormatLocaleString(message,MagickPathExtent,
2053                 "C %g %g %g %g %g %g Z\n",last[2].x,
2054                 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
2055             (void) ConcatenateString(&path,message);
2056             in_subpath=MagickFalse;
2057           }
2058         break;
2059       }
2060       case 6:
2061       case 7:
2062       case 8:
2063       default:
2064       {
2065         blob+=24;
2066         length-=MagickMin(24,(ssize_t) length);
2067         break;
2068       }
2069     }
2070   }
2071   /*
2072     Return an empty SVG image if the path does not have knots.
2073   */
2074   (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n");
2075   message=DestroyString(message);
2076   return(path);
2077 }
2078
2079 MagickExport const char *GetImageProperty(const Image *image,
2080   const char *property,ExceptionInfo *exception)
2081 {
2082   register const char
2083     *p;
2084
2085   assert(image != (Image *) NULL);
2086   assert(image->signature == MagickCoreSignature);
2087   if (IfMagickTrue(image->debug))
2088     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2089   p=(const char *) NULL;
2090   if (image->properties != (void *) NULL)
2091     {
2092       if (property == (const char *) NULL)
2093         {
2094           ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
2095           p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
2096             image->properties);
2097           return(p);
2098         }
2099         p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2100           image->properties,property);
2101         if (p != (const char *) NULL)
2102           return(p);
2103     }
2104   if ((property == (const char *) NULL) ||
2105       (strchr(property,':') == (char *) NULL))
2106     return(p);
2107   switch (*property)
2108   {
2109     case '8':
2110     {
2111       if (LocaleNCompare("8bim:",property,5) == 0)
2112         {
2113           (void) Get8BIMProperty(image,property,exception);
2114           break;
2115         }
2116       break;
2117     }
2118     case 'E':
2119     case 'e':
2120     {
2121       if (LocaleNCompare("exif:",property,5) == 0)
2122         {
2123           (void) GetEXIFProperty(image,property,exception);
2124           break;
2125         }
2126       break;
2127     }
2128     case 'I':
2129     case 'i':
2130     {
2131       if ((LocaleNCompare("icc:",property,4) == 0) ||
2132           (LocaleNCompare("icm:",property,4) == 0))
2133         {
2134           (void) GetICCProperty(image,property,exception);
2135           break;
2136         }
2137       if (LocaleNCompare("iptc:",property,5) == 0)
2138         {
2139           (void) GetIPTCProperty(image,property,exception);
2140           break;
2141         }
2142       break;
2143     }
2144     case 'X':
2145     case 'x':
2146     {
2147       if (LocaleNCompare("xmp:",property,4) == 0)
2148         {
2149           (void) GetXMPProperty(image,property);
2150           break;
2151         }
2152       break;
2153     }
2154     default:
2155       break;
2156   }
2157   if (image->properties != (void *) NULL)
2158     {
2159       p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2160         image->properties,property);
2161       return(p);
2162     }
2163   return((const char *) NULL);
2164 }
2165 \f
2166 /*
2167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2168 %                                                                             %
2169 %                                                                             %
2170 %                                                                             %
2171 +   G e t M a g i c k P r o p e r t y                                         %
2172 %                                                                             %
2173 %                                                                             %
2174 %                                                                             %
2175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2176 %
2177 %  GetMagickProperty() gets attributes or calculated values that is associated
2178 %  with a fixed known property name, or single letter property. It may be
2179 %  called if no image is defined (IMv7), in which case only global image_info
2180 %  values are available:
2181 %
2182 %    \n   newline
2183 %    \r   carriage return
2184 %    <    less-than character.
2185 %    >    greater-than character.
2186 %    &    ampersand character.
2187 %    %%   a percent sign
2188 %    %b   file size of image read in
2189 %    %c   comment meta-data property
2190 %    %d   directory component of path
2191 %    %e   filename extension or suffix
2192 %    %f   filename (including suffix)
2193 %    %g   layer canvas page geometry   (equivalent to "%Wx%H%X%Y")
2194 %    %h   current image height in pixels
2195 %    %i   image filename (note: becomes output filename for "info:")
2196 %    %k   CALCULATED: number of unique colors
2197 %    %l   label meta-data property
2198 %    %m   image file format (file magic)
2199 %    %n   number of images in current image sequence
2200 %    %o   output filename  (used for delegates)
2201 %    %p   index of image in current image list
2202 %    %q   quantum depth (compile-time constant)
2203 %    %r   image class and colorspace
2204 %    %s   scene number (from input unless re-assigned)
2205 %    %t   filename without directory or extension (suffix)
2206 %    %u   unique temporary filename (used for delegates)
2207 %    %w   current width in pixels
2208 %    %x   x resolution (density)
2209 %    %y   y resolution (density)
2210 %    %z   image depth (as read in unless modified, image save depth)
2211 %    %A   image transparency channel enabled (true/false)
2212 %    %C   image compression type
2213 %    %D   image GIF dispose method
2214 %    %G   original image size (%wx%h; before any resizes)
2215 %    %H   page (canvas) height
2216 %    %M   Magick filename (original file exactly as given,  including read mods)
2217 %    %O   page (canvas) offset ( = %X%Y )
2218 %    %P   page (canvas) size ( = %Wx%H )
2219 %    %Q   image compression quality ( 0 = default )
2220 %    %S   ?? scenes ??
2221 %    %T   image time delay (in centi-seconds)
2222 %    %U   image resolution units
2223 %    %W   page (canvas) width
2224 %    %X   page (canvas) x offset (including sign)
2225 %    %Y   page (canvas) y offset (including sign)
2226 %    %Z   unique filename (used for delegates)
2227 %    %@   CALCULATED: trim bounding box (without actually trimming)
2228 %    %#   CALCULATED: 'signature' hash of image values
2229 %
2230 %  This routine only handles specifically known properties.  It does not
2231 %  handle special prefixed properties, profiles, or expressions. Nor does
2232 %  it return any free-form property strings.
2233 %
2234 %  The returned string is stored in a structure somewhere, and should not be
2235 %  directly freed.  If the string was generated (common) the string will be
2236 %  stored as as either as artifact or option 'get-property'.  These may be
2237 %  deleted (cleaned up) when no longer required, but neither artifact or
2238 %  option is guranteed to exist.
2239 %
2240 %  The format of the GetMagickProperty method is:
2241 %
2242 %      const char *GetMagickProperty(ImageInfo *image_info,Image *image,
2243 %        const char *property,ExceptionInfo *exception)
2244 %
2245 %  A description of each parameter follows:
2246 %
2247 %    o image_info: the image info (optional)
2248 %
2249 %    o image: the image (optional)
2250 %
2251 %    o key: the key.
2252 %
2253 %    o exception: return any errors or warnings in this structure.
2254 %
2255 */
2256 #define WarnNoImageReturn(format,arg) \
2257   if (image == (Image *) NULL ) { \
2258     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2259         "NoImageForProperty",format,arg); \
2260     return((const char *) NULL); \
2261   }
2262 #define WarnNoImageInfoReturn(format,arg) \
2263   if (image_info == (ImageInfo *) NULL ) { \
2264     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2265         "NoImageInfoForProperty",format,arg); \
2266     return((const char *) NULL); \
2267   }
2268
2269 static const char *GetMagickPropertyLetter(ImageInfo *image_info,
2270   Image *image,const char letter,ExceptionInfo *exception)
2271 {
2272   char
2273     value[MagickPathExtent];  /* formated string to store as a returned artifact */
2274
2275   const char
2276     *string;     /* return a string already stored somewher */
2277
2278   if (image != (Image *) NULL && IfMagickTrue(image->debug))
2279     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2280   else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2281     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
2282
2283   *value='\0';           /* formatted string */
2284   string=(char *) NULL;  /* constant string reference */
2285
2286   /* Get properities that are directly defined by images */
2287   switch (letter)
2288   {
2289     case 'b':  /* image size read in - in bytes */
2290     {
2291       WarnNoImageReturn("\"%%%c\"",letter);
2292       (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
2293         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 (IfMagickTrue(SetImageGray(image,exception)))
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(MagickBooleanOptions,
2473         (ssize_t) 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 && IfMagickTrue(image->debug))
2651     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2652   else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
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 && IfMagickTrue(image->debug))
3240     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3241   else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
3242     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
3243
3244   if (embed_text == (const char *) NULL)
3245     return((char *) NULL);
3246   p=embed_text;
3247
3248   if (*p == '\0')
3249     return(ConstantString(""));
3250
3251   /* handle a '@' replace string from file */
3252   if (*p == '@') {
3253      p++;
3254      if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
3255        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3256          "UnableToAccessPath","%s",p);
3257        return((char *) NULL);
3258      }
3259      return(FileToString(p,~0UL,exception));
3260   }
3261
3262   /*
3263     Translate any embedded format characters.
3264   */
3265   interpret_text=AcquireString(embed_text); /* new string with extra space */
3266   extent=MagickPathExtent;                     /* allocated space in string */
3267   number=MagickFalse;                       /* is last char a number? */
3268   for (q=interpret_text; *p!='\0'; number=isdigit(*p) ? MagickTrue : MagickFalse,p++)
3269   {
3270     *q='\0';
3271     ExtendInterpretText(MagickPathExtent);
3272     /*
3273       Look for the various escapes, (and handle other specials)
3274     */
3275     switch (*p) {
3276       case '\\':
3277         switch (*(p+1)) {
3278           case '\0':
3279             continue;
3280           case 'r':       /* convert to RETURN */
3281             *q++='\r';
3282             p++;
3283             continue;
3284           case 'n':       /* convert to NEWLINE */
3285             *q++='\n';
3286             p++;
3287             continue;
3288           case '\n':      /* EOL removal UNIX,MacOSX */
3289             p++;
3290             continue;
3291           case '\r':      /* EOL removal DOS,Windows */
3292             p++;
3293             if (*p == '\n') /* return-newline EOL */
3294               p++;
3295             continue;
3296           default:
3297             p++;
3298             *q++=(*p);
3299         }
3300         continue;
3301       case '&':
3302         if (LocaleNCompare("&lt;",p,4) == 0)
3303           *q++='<', p+=3;
3304         else if (LocaleNCompare("&gt;",p,4) == 0)
3305           *q++='>', p+=3;
3306         else if (LocaleNCompare("&amp;",p,5) == 0)
3307           *q++='&', p+=4;
3308         else
3309           *q++=(*p);
3310         continue;
3311       case '%':
3312         break;      /* continue to next set of handlers */
3313       default:
3314         *q++=(*p);  /* any thing else is 'as normal' */
3315         continue;
3316     }
3317     p++; /* advance beyond the percent */
3318
3319     /*
3320       Doubled Percent - or percent at end of string
3321     */
3322     if ((*p == '\0') || (*p == '\'') || (*p == '"'))
3323       p--;
3324     if (*p == '%') {
3325         *q++='%';
3326         continue;
3327       }
3328     /*
3329       Single letter escapes  %c
3330     */
3331     if ( *p != '[' ) {
3332       const char
3333         *string;
3334
3335       /* But only if not preceeded by a number! */
3336       if (number != MagickFalse) {
3337         *q++='%'; /* do NOT substitute the percent */
3338         p--;      /* back up one */
3339         continue;
3340       }
3341       string=GetMagickPropertyLetter(image_info,image,*p, exception);
3342       if (string != (char *) NULL)
3343         {
3344           AppendString2Text(string);
3345           if (image != (Image *) NULL)
3346             (void)DeleteImageArtifact(image,"get-property");
3347           if (image_info != (ImageInfo *) NULL)
3348             (void)DeleteImageOption(image_info,"get-property");
3349           continue;
3350         }
3351       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3352         "UnknownImageProperty","\"%%%c\"",*p);
3353       continue;
3354     }
3355
3356     /*
3357       Braced Percent Escape  %[...]
3358     */
3359     {
3360       char
3361         pattern[2*MagickPathExtent];
3362
3363       const char
3364         *key,
3365         *string;
3366
3367       register ssize_t
3368         len;
3369
3370       ssize_t
3371         depth;
3372
3373       /* get the property name framed by the %[...] */
3374       p++;  /* advance p to just inside the opening brace */
3375       depth=1;
3376       if ( *p == ']' ) {
3377         (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3378           "UnknownImageProperty","\"%%[]\"");
3379         break;
3380       }
3381       for (len=0; len<(MagickPathExtent-1L) && (*p != '\0');)
3382       {
3383         /* skip escaped braces within braced pattern */
3384         if ( (*p == '\\') && (*(p+1) != '\0') ) {
3385           pattern[len++]=(*p++);
3386           pattern[len++]=(*p++);
3387           continue;
3388         }
3389         if (*p == '[')
3390           depth++;
3391         if (*p == ']')
3392           depth--;
3393         if (depth <= 0)
3394           break;
3395         pattern[len++]=(*p++);
3396       }
3397       pattern[len]='\0';
3398       /* Check for unmatched final ']' for "%[...]" */
3399       if ( depth != 0 ) {
3400         if (len >= 64) {  /* truncate string for error message */
3401           pattern[61] = '.';
3402           pattern[62] = '.';
3403           pattern[63] = '.';
3404           pattern[64] = '\0';
3405         }
3406         (void) ThrowMagickException(exception,GetMagickModule(),
3407             OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
3408         interpret_text=DestroyString(interpret_text);
3409         return((char *) NULL);
3410       }
3411
3412       /*
3413         Special Lookup Prefixes %[prefix:...]
3414       */
3415       /* fx - value calculator */
3416       if (LocaleNCompare("fx:",pattern,3) == 0)
3417         {
3418           FxInfo
3419             *fx_info;
3420
3421           double
3422             value;
3423
3424           MagickBooleanType
3425             status;
3426
3427           if (image == (Image *) NULL ) {
3428             (void) ThrowMagickException(exception,GetMagickModule(),
3429                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3430             continue; /* else no image to retrieve artifact */
3431           }
3432           fx_info=AcquireFxInfo(image,pattern+3,exception);
3433           status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
3434             &value,exception);
3435           fx_info=DestroyFxInfo(fx_info);
3436           if (status != MagickFalse)
3437             {
3438               char
3439                 result[MagickPathExtent];
3440
3441               (void) FormatLocaleString(result,MagickPathExtent,"%.*g",
3442                 GetMagickPrecision(),(double) value);
3443               AppendString2Text(result);
3444             }
3445           continue;
3446         }
3447       /* pixel - color value calculator */
3448       if (LocaleNCompare("pixel:",pattern,6) == 0)
3449         {
3450           FxInfo
3451             *fx_info;
3452
3453           double
3454             value;
3455
3456           MagickStatusType
3457             status;
3458
3459           PixelInfo
3460             pixel;
3461
3462           if (image == (Image *) NULL ) {
3463             (void) ThrowMagickException(exception,GetMagickModule(),
3464                 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3465             continue; /* else no image to retrieve artifact */
3466           }
3467           GetPixelInfo(image,&pixel);
3468           fx_info=AcquireFxInfo(image,pattern+6,exception);
3469           status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
3470             &value,exception);
3471           pixel.red=(double) QuantumRange*value;
3472           status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
3473             &value,exception);
3474           pixel.green=(double) QuantumRange*value;
3475           status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
3476             &value,exception);
3477           pixel.blue=(double) QuantumRange*value;
3478           if (image->colorspace == CMYKColorspace)
3479             {
3480               status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
3481                 &value,exception);
3482               pixel.black=(double) QuantumRange*value;
3483             }
3484           status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
3485             &value,exception);
3486           pixel.alpha=(double) QuantumRange*value;
3487           fx_info=DestroyFxInfo(fx_info);
3488           if (status != MagickFalse)
3489             {
3490               char
3491                 name[MagickPathExtent];
3492
3493               (void) QueryColorname(image,&pixel,SVGCompliance,name,
3494                 exception);
3495               AppendString2Text(name);
3496             }
3497           continue;
3498         }
3499       /* option - direct global option lookup (with globbing) */
3500       if (LocaleNCompare("option:",pattern,7) == 0)
3501       {
3502         if (image_info == (ImageInfo *) NULL ) {
3503           (void) ThrowMagickException(exception,GetMagickModule(),
3504               OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3505           continue; /* else no image to retrieve artifact */
3506         }
3507         if (IsGlob(pattern+7) != MagickFalse)
3508         {
3509           ResetImageOptionIterator(image_info);
3510           while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
3511             if (GlobExpression(key,pattern+7,MagickTrue) != MagickFalse)
3512               {
3513                 string=GetImageOption(image_info,key);
3514                 if (string != (const char *) NULL)
3515                   AppendKeyValue2Text(key,string);
3516                 /* else - assertion failure? key found but no string value! */
3517               }
3518           continue;
3519         }
3520         string=GetImageOption(image_info,pattern+7);
3521         if (string == (char *) NULL)
3522           goto PropertyLookupFailure; /* no artifact of this specifc name */
3523         AppendString2Text(string);
3524         continue;
3525       }
3526       /* artifact - direct image artifact lookup (with glob) */
3527       if (LocaleNCompare("artifact:",pattern,9) == 0)
3528       {
3529         if (image == (Image *) NULL ) {
3530           (void) ThrowMagickException(exception,GetMagickModule(),
3531               OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3532           continue; /* else no image to retrieve artifact */
3533         }
3534         if (IsGlob(pattern+9) != MagickFalse)
3535         {
3536           ResetImageArtifactIterator(image);
3537           while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
3538             if (GlobExpression(key,pattern+9,MagickTrue) != MagickFalse)
3539               {
3540                 string=GetImageArtifact(image,key);
3541                 if (string != (const char *) NULL)
3542                   AppendKeyValue2Text(key,string);
3543                 /* else - assertion failure? key found but no string value! */
3544               }
3545           continue;
3546         }
3547         string=GetImageArtifact(image,pattern+9);
3548         if (string == (char *) NULL)
3549           goto PropertyLookupFailure; /* no artifact of this specifc name */
3550         AppendString2Text(string);
3551         continue;
3552       }
3553       /* property - direct image property lookup (with glob) */
3554       if (LocaleNCompare("property:",pattern,9) == 0)
3555       {
3556         if (image == (Image *) NULL ) {
3557           (void) ThrowMagickException(exception,GetMagickModule(),
3558               OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3559           continue; /* else no image to retrieve artifact */
3560         }
3561         if (IsGlob(pattern+9) != MagickFalse)
3562         {
3563           ResetImagePropertyIterator(image);
3564           while ((key=GetNextImageProperty(image)) != (const char *) NULL)
3565             if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
3566               {
3567                 string=GetImageProperty(image,key,exception);
3568                 if (string != (const char *) NULL)
3569                   AppendKeyValue2Text(key,string);
3570                 /* else - assertion failure? */
3571               }
3572           continue;
3573         }
3574         string=GetImageProperty(image,pattern+9,exception);
3575         if (string == (char *) NULL)
3576           goto PropertyLookupFailure; /* no artifact of this specifc name */
3577         AppendString2Text(string);
3578         continue;
3579       }
3580       /* Properties without special prefix.
3581          This handles attributes, properties, and profiles such as %[exif:...]
3582          Note the profile properties may also include a glob expansion pattern.
3583       */
3584       if (image != (Image *) NULL)
3585         {
3586           string=GetImageProperty(image,pattern,exception);
3587           if (string != (const char *) NULL)
3588             {
3589               AppendString2Text(string);
3590               if (image != (Image *) NULL)
3591                 (void)DeleteImageArtifact(image,"get-property");
3592               if (image_info != (ImageInfo *) NULL)
3593                 (void)DeleteImageOption(image_info,"get-property");
3594               continue;
3595             }
3596         }
3597       /*
3598         Handle property 'glob' patterns
3599         Such as:  %[*]   %[user:array_??]  %[filename:e*]
3600       */
3601       if (IsGlob(pattern) != MagickFalse)
3602         {
3603           if (image == (Image *) NULL)
3604             continue; /* else no image to retrieve proprty - no list */
3605           ResetImagePropertyIterator(image);
3606           while ((key=GetNextImageProperty(image)) != (const char *) NULL)
3607             if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
3608               {
3609                 string=GetImageProperty(image,key,exception);
3610                 if (string != (const char *) NULL)
3611                   AppendKeyValue2Text(key,string);
3612                 /* else - assertion failure? */
3613               }
3614           continue;
3615         }
3616       /*
3617         Look for a known property or image attribute
3618         Such as  %[basename]  %[denisty]  %[delay]
3619         Also handles a braced single letter:  %[b] %[G] %[g]
3620       */
3621       string=GetMagickProperty(image_info,image,pattern,exception);
3622       if (string != (const char *) NULL)
3623         {
3624           AppendString2Text(string);
3625           continue;
3626         }
3627       /*
3628         Look for a per-image Artifact
3629         This includes option lookup (FUTURE: interpreted according to image)
3630       */
3631       if (image != (Image *) NULL)
3632         {
3633           string=GetImageArtifact(image,pattern);
3634           if (string != (char *) NULL)
3635             {
3636               AppendString2Text(string);
3637               continue;
3638             }
3639         }
3640       else
3641         /* no image, so direct 'option' lookup (no delayed percent escapes) */
3642         if (image_info != (ImageInfo *) NULL)
3643           {
3644             string=GetImageOption(image_info,pattern);
3645             if (string != (char *) NULL)
3646               {
3647                 AppendString2Text(string);
3648                 continue;
3649               }
3650           }
3651 PropertyLookupFailure:
3652       /*
3653         Failed to find any match anywhere!
3654       */
3655       if (len >= 64) {  /* truncate string for error message */
3656         pattern[61] = '.';
3657         pattern[62] = '.';
3658         pattern[63] = '.';
3659         pattern[64] = '\0';
3660       }
3661       (void) ThrowMagickException(exception,GetMagickModule(),
3662           OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
3663       /* continue */
3664     } /* Braced Percent Escape */
3665
3666   } /* for each char in 'embed_text' */
3667   *q='\0';
3668   return(interpret_text);
3669 }
3670 \f
3671 /*
3672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3673 %                                                                             %
3674 %                                                                             %
3675 %                                                                             %
3676 %   R e m o v e I m a g e P r o p e r t y                                     %
3677 %                                                                             %
3678 %                                                                             %
3679 %                                                                             %
3680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3681 %
3682 %  RemoveImageProperty() removes a property from the image and returns its
3683 %  value.
3684 %
3685 %  In this case the ConstantString() value returned should be freed by the
3686 %  caller when finished.
3687 %
3688 %  The format of the RemoveImageProperty method is:
3689 %
3690 %      char *RemoveImageProperty(Image *image,const char *property)
3691 %
3692 %  A description of each parameter follows:
3693 %
3694 %    o image: the image.
3695 %
3696 %    o property: the image property.
3697 %
3698 */
3699 MagickExport char *RemoveImageProperty(Image *image,
3700   const char *property)
3701 {
3702   char
3703     *value;
3704
3705   assert(image != (Image *) NULL);
3706   assert(image->signature == MagickCoreSignature);
3707   if (image->debug != MagickFalse)
3708     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3709       image->filename);
3710   if (image->properties == (void *) NULL)
3711     return((char *) NULL);
3712   value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3713     property);
3714   return(value);
3715 }
3716 \f
3717 /*
3718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3719 %                                                                             %
3720 %                                                                             %
3721 %                                                                             %
3722 %   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                       %
3723 %                                                                             %
3724 %                                                                             %
3725 %                                                                             %
3726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3727 %
3728 %  ResetImagePropertyIterator() resets the image properties iterator.  Use it
3729 %  in conjunction with GetNextImageProperty() to iterate over all the values
3730 %  associated with an image property.
3731 %
3732 %  The format of the ResetImagePropertyIterator method is:
3733 %
3734 %      ResetImagePropertyIterator(Image *image)
3735 %
3736 %  A description of each parameter follows:
3737 %
3738 %    o image: the image.
3739 %
3740 */
3741 MagickExport void ResetImagePropertyIterator(const Image *image)
3742 {
3743   assert(image != (Image *) NULL);
3744   assert(image->signature == MagickCoreSignature);
3745   if (image->debug != MagickFalse)
3746     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3747       image->filename);
3748   if (image->properties == (void *) NULL)
3749     return;
3750   ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3751 }
3752 \f
3753 /*
3754 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3755 %                                                                             %
3756 %                                                                             %
3757 %                                                                             %
3758 %   S e t I m a g e P r o p e r t y                                           %
3759 %                                                                             %
3760 %                                                                             %
3761 %                                                                             %
3762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3763 %
3764 %  SetImageProperty() saves the given string value either to specific known
3765 %  attribute or to a freeform property string.
3766 %
3767 %  Attempting to set a property that is normally calculated will produce
3768 %  an exception.
3769 %
3770 %  The format of the SetImageProperty method is:
3771 %
3772 %      MagickBooleanType SetImageProperty(Image *image,const char *property,
3773 %        const char *value,ExceptionInfo *exception)
3774 %
3775 %  A description of each parameter follows:
3776 %
3777 %    o image: the image.
3778 %
3779 %    o property: the image property.
3780 %
3781 %    o values: the image property values.
3782 %
3783 %    o exception: return any errors or warnings in this structure.
3784 %
3785 */
3786 MagickExport MagickBooleanType SetImageProperty(Image *image,
3787   const char *property,const char *value,ExceptionInfo *exception)
3788 {
3789   MagickBooleanType
3790     status;
3791
3792   MagickStatusType
3793     flags;
3794
3795   assert(image != (Image *) NULL);
3796   assert(image->signature == MagickCoreSignature);
3797   if (image->debug != MagickFalse)
3798     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3799   if (image->properties == (void *) NULL)
3800     image->properties=NewSplayTree(CompareSplayTreeString,
3801       RelinquishMagickMemory,RelinquishMagickMemory);  /* create splay-tree */
3802   if (value == (const char *) NULL)
3803     return(DeleteImageProperty(image,property));  /* delete if NULL */
3804   status=MagickTrue;
3805   if (strlen(property) <= 1)
3806     {
3807       /*
3808         Do not 'set' single letter properties - read only shorthand.
3809        */
3810       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3811         "SetReadOnlyProperty","`%s'",property);
3812       return(MagickFalse);
3813     }
3814
3815   /* FUTURE: binary chars or quotes in key should produce a error */
3816   /* Set attributes with known names or special prefixes
3817      return result is found, or break to set a free form properity
3818   */
3819   switch (*property)
3820   {
3821 #if 0  /* Percent escape's sets values with this prefix: for later use
3822           Throwing an exception causes this setting to fail */
3823     case '8':
3824     {
3825       if (LocaleNCompare("8bim:",property,5) == 0)
3826         {
3827           (void) ThrowMagickException(exception,GetMagickModule(),
3828                OptionError,"SetReadOnlyProperty","`%s'",property);
3829           return(MagickFalse);
3830         }
3831       break;
3832     }
3833 #endif
3834     case 'B':
3835     case 'b':
3836     {
3837       if (LocaleCompare("background",property) == 0)
3838         {
3839           (void) QueryColorCompliance(value,AllCompliance,
3840                &image->background_color,exception);
3841           /* check for FUTURE: value exception?? */
3842           /* also add user input to splay tree */
3843         }
3844       break; /* not an attribute, add as a property */
3845     }
3846     case 'C':
3847     case 'c':
3848     {
3849       if (LocaleCompare("channels",property) == 0)
3850         {
3851           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3852             "SetReadOnlyProperty","`%s'",property);
3853           return(MagickFalse);
3854         }
3855       if (LocaleCompare("colorspace",property) == 0)
3856         {
3857           ssize_t
3858             colorspace;
3859
3860           colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
3861             value);
3862           if (colorspace < 0)
3863             return(MagickFalse); /* FUTURE: value exception?? */
3864           return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
3865         }
3866       if (LocaleCompare("compose",property) == 0)
3867         {
3868           ssize_t
3869             compose;
3870
3871           compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
3872           if (compose < 0)
3873             return(MagickFalse); /* FUTURE: value exception?? */
3874           image->compose=(CompositeOperator) compose;
3875           return(MagickTrue);
3876         }
3877       if (LocaleCompare("compress",property) == 0)
3878         {
3879           ssize_t
3880             compression;
3881
3882           compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
3883             value);
3884           if (compression < 0)
3885             return(MagickFalse); /* FUTURE: value exception?? */
3886           image->compression=(CompressionType) compression;
3887           return(MagickTrue);
3888         }
3889       break; /* not an attribute, add as a property */
3890     }
3891     case 'D':
3892     case 'd':
3893     {
3894       if (LocaleCompare("delay",property) == 0)
3895         {
3896           GeometryInfo
3897             geometry_info;
3898
3899           flags=ParseGeometry(value,&geometry_info);
3900           if ((flags & GreaterValue) != 0)
3901             {
3902               if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3903                 image->delay=(size_t) floor(geometry_info.rho+0.5);
3904             }
3905           else
3906             if ((flags & LessValue) != 0)
3907               {
3908                 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
3909                   image->delay=(ssize_t)
3910                     floor(geometry_info.sigma+0.5);
3911               }
3912             else
3913               image->delay=(size_t) floor(geometry_info.rho+0.5);
3914           if ((flags & SigmaValue) != 0)
3915             image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
3916           return(MagickTrue);
3917         }
3918       if (LocaleCompare("delay_units",property) == 0)
3919         {
3920           (void) ThrowMagickException(exception,GetMagickModule(),
3921                OptionError,"SetReadOnlyProperty","`%s'",property);
3922           return(MagickFalse);
3923         }
3924       if (LocaleCompare("density",property) == 0)
3925         {
3926           GeometryInfo
3927             geometry_info;
3928
3929           flags=ParseGeometry(value,&geometry_info);
3930           image->resolution.x=geometry_info.rho;
3931           image->resolution.y=geometry_info.sigma;
3932           if ((flags & SigmaValue) == 0)
3933             image->resolution.y=image->resolution.x;
3934           return(MagickTrue);
3935         }
3936       if (LocaleCompare("depth",property) == 0)
3937         {
3938           image->depth=StringToUnsignedLong(value);
3939           return(MagickTrue);
3940         }
3941       if (LocaleCompare("dispose",property) == 0)
3942         {
3943           ssize_t
3944             dispose;
3945
3946           dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
3947           if (dispose < 0)
3948             return(MagickFalse); /* FUTURE: value exception?? */
3949           image->dispose=(DisposeType) dispose;
3950           return(MagickTrue);
3951         }
3952       break; /* not an attribute, add as a property */
3953     }
3954 #if 0  /* Percent escape's sets values with this prefix: for later use
3955           Throwing an exception causes this setting to fail */
3956     case 'E':
3957     case 'e':
3958     {
3959       if (LocaleNCompare("exif:",property,5) == 0)
3960         {
3961           (void) ThrowMagickException(exception,GetMagickModule(),
3962                OptionError,"SetReadOnlyProperty","`%s'",property);
3963           return(MagickFalse);
3964         }
3965       break; /* not an attribute, add as a property */
3966     }
3967     case 'F':
3968     case 'f':
3969     {
3970       if (LocaleNCompare("fx:",property,3) == 0)
3971         {
3972           (void) ThrowMagickException(exception,GetMagickModule(),
3973                OptionError,"SetReadOnlyProperty","`%s'",property);
3974           return(MagickFalse);
3975         }
3976       break; /* not an attribute, add as a property */
3977     }
3978 #endif
3979     case 'G':
3980     case 'g':
3981     {
3982       if (LocaleCompare("gamma",property) == 0)
3983         {
3984           image->gamma=StringToDouble(value,(char **) NULL);
3985           return(MagickTrue);
3986         }
3987       if (LocaleCompare("gravity",property) == 0)
3988         {
3989           ssize_t
3990             gravity;
3991
3992           gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
3993           if (gravity < 0)
3994             return(MagickFalse); /* FUTURE: value exception?? */
3995           image->gravity=(GravityType) gravity;
3996           return(MagickTrue);
3997         }
3998       break; /* not an attribute, add as a property */
3999     }
4000     case 'H':
4001     case 'h':
4002     {
4003       if (LocaleCompare("height",property) == 0)
4004         {
4005           (void) ThrowMagickException(exception,GetMagickModule(),
4006                OptionError,"SetReadOnlyProperty","`%s'",property);
4007           return(MagickFalse);
4008         }
4009       break; /* not an attribute, add as a property */
4010     }
4011     case 'I':
4012     case 'i':
4013     {
4014       if (LocaleCompare("intensity",property) == 0)
4015         {
4016           ssize_t
4017             intensity;
4018
4019           intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,
4020             value);
4021           if (intensity < 0)
4022             return(MagickFalse);
4023           image->intensity=(PixelIntensityMethod) intensity;
4024           return(MagickTrue);
4025         }
4026       if (LocaleCompare("intent",property) == 0)
4027         {
4028           ssize_t
4029             rendering_intent;
4030
4031           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
4032             value);
4033           if (rendering_intent < 0)
4034             return(MagickFalse); /* FUTURE: value exception?? */
4035           image->rendering_intent=(RenderingIntent) rendering_intent;
4036           return(MagickTrue);
4037         }
4038       if (LocaleCompare("interpolate",property) == 0)
4039         {
4040           ssize_t
4041             interpolate;
4042
4043           interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
4044             value);
4045           if (interpolate < 0)
4046             return(MagickFalse); /* FUTURE: value exception?? */
4047           image->interpolate=(PixelInterpolateMethod) interpolate;
4048           return(MagickTrue);
4049         }
4050 #if 0  /* Percent escape's sets values with this prefix: for later use
4051           Throwing an exception causes this setting to fail */
4052       if (LocaleNCompare("iptc:",property,5) == 0)
4053         {
4054           (void) ThrowMagickException(exception,GetMagickModule(),
4055                OptionError,"SetReadOnlyProperty","`%s'",property);
4056           return(MagickFalse);
4057         }
4058 #endif
4059       break; /* not an attribute, add as a property */
4060     }
4061     case 'K':
4062     case 'k':
4063       if (LocaleCompare("kurtosis",property) == 0)
4064         {
4065           (void) ThrowMagickException(exception,GetMagickModule(),
4066                OptionError,"SetReadOnlyProperty","`%s'",property);
4067           return(MagickFalse);
4068         }
4069       break; /* not an attribute, add as a property */
4070     case 'L':
4071     case 'l':
4072     {
4073       if (LocaleCompare("loop",property) == 0)
4074         {
4075           image->iterations=StringToUnsignedLong(value);
4076           return(MagickTrue);
4077         }
4078       break; /* not an attribute, add as a property */
4079     }
4080     case 'M':
4081     case 'm':
4082       if ( (LocaleCompare("magick",property) == 0) ||
4083            (LocaleCompare("max",property) == 0) ||
4084            (LocaleCompare("mean",property) == 0) ||
4085            (LocaleCompare("min",property) == 0) ||
4086            (LocaleCompare("min",property) == 0) )
4087         {
4088           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
4089              "SetReadOnlyProperty","`%s'",property);
4090           return(MagickFalse);
4091         }
4092       break; /* not an attribute, add as a property */
4093     case 'O':
4094     case 'o':
4095       if (LocaleCompare("opaque",property) == 0)
4096         {
4097           (void) ThrowMagickException(exception,GetMagickModule(),
4098                OptionError,"SetReadOnlyProperty","`%s'",property);
4099           return(MagickFalse);
4100         }
4101       break; /* not an attribute, add as a property */
4102     case 'P':
4103     case 'p':
4104     {
4105       if (LocaleCompare("page",property) == 0)
4106         {
4107           char
4108             *geometry;
4109
4110           geometry=GetPageGeometry(value);
4111           flags=ParseAbsoluteGeometry(geometry,&image->page);
4112           geometry=DestroyString(geometry);
4113           return(MagickTrue);
4114         }
4115 #if 0  /* Percent escape's sets values with this prefix: for later use
4116           Throwing an exception causes this setting to fail */
4117       if (LocaleNCompare("pixel:",property,6) == 0)
4118         {
4119           (void) ThrowMagickException(exception,GetMagickModule(),
4120                OptionError,"SetReadOnlyProperty","`%s'",property);
4121           return(MagickFalse);
4122         }
4123 #endif
4124       if (LocaleCompare("profile",property) == 0)
4125         {
4126           ImageInfo
4127             *image_info;
4128
4129           StringInfo
4130             *profile;
4131
4132           image_info=AcquireImageInfo();
4133           (void) CopyMagickString(image_info->filename,value,MagickPathExtent);
4134           (void) SetImageInfo(image_info,1,exception);
4135           profile=FileToStringInfo(image_info->filename,~0UL,exception);
4136           if (profile != (StringInfo *) NULL)
4137             status=SetImageProfile(image,image_info->magick,profile,exception);
4138           image_info=DestroyImageInfo(image_info);
4139           return(MagickTrue);
4140         }
4141       break; /* not an attribute, add as a property */
4142     }
4143     case 'R':
4144     case 'r':
4145     {
4146       if (LocaleCompare("rendering-intent",property) == 0)
4147         {
4148           ssize_t
4149             rendering_intent;
4150
4151           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
4152             value);
4153           if (rendering_intent < 0)
4154             return(MagickFalse); /* FUTURE: value exception?? */
4155           image->rendering_intent=(RenderingIntent) rendering_intent;
4156           return(MagickTrue);
4157         }
4158       break; /* not an attribute, add as a property */
4159     }
4160     case 'S':
4161     case 's':
4162       if ( (LocaleCompare("size",property) == 0) ||
4163            (LocaleCompare("skewness",property) == 0) ||
4164            (LocaleCompare("scenes",property) == 0) ||
4165            (LocaleCompare("standard-deviation",property) == 0) )
4166         {
4167           (void) ThrowMagickException(exception,GetMagickModule(),
4168                OptionError,"SetReadOnlyProperty","`%s'",property);
4169           return(MagickFalse);
4170         }
4171       break; /* not an attribute, add as a property */
4172     case 'T':
4173     case 't':
4174     {
4175       if (LocaleCompare("tile-offset",property) == 0)
4176         {
4177           char
4178             *geometry;
4179
4180           geometry=GetPageGeometry(value);
4181           flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
4182           geometry=DestroyString(geometry);
4183           return(MagickTrue);
4184         }
4185       break; /* not an attribute, add as a property */
4186     }
4187     case 'U':
4188     case 'u':
4189     {
4190       if (LocaleCompare("units",property) == 0)
4191         {
4192           ssize_t
4193             units;
4194
4195           units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
4196           if (units < 0)
4197             return(MagickFalse); /* FUTURE: value exception?? */
4198           image->units=(ResolutionType) units;
4199           return(MagickTrue);
4200         }
4201       break; /* not an attribute, add as a property */
4202     }
4203     case 'V':
4204     case 'v':
4205     {
4206       if (LocaleCompare("version",property) == 0)
4207         {
4208           (void) ThrowMagickException(exception,GetMagickModule(),
4209                OptionError,"SetReadOnlyProperty","`%s'",property);
4210           return(MagickFalse);
4211         }
4212       break; /* not an attribute, add as a property */
4213     }
4214     case 'W':
4215     case 'w':
4216     {
4217       if (LocaleCompare("width",property) == 0)
4218         {
4219           (void) ThrowMagickException(exception,GetMagickModule(),
4220                OptionError,"SetReadOnlyProperty","`%s'",property);
4221           return(MagickFalse);
4222         }
4223       break; /* not an attribute, add as a property */
4224     }
4225 #if 0  /* Percent escape's sets values with this prefix: for later use
4226           Throwing an exception causes this setting to fail */
4227     case 'X':
4228     case 'x':
4229     {
4230       if (LocaleNCompare("xmp:",property,4) == 0)
4231         {
4232           (void) ThrowMagickException(exception,GetMagickModule(),
4233                OptionError,"SetReadOnlyProperty","`%s'",property);
4234           return(MagickFalse);
4235         }
4236       break; /* not an attribute, add as a property */
4237     }
4238 #endif
4239   }
4240   /* Default: not an attribute, add as a property */
4241   status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
4242     ConstantString(property),ConstantString(value));
4243   /* FUTURE: error if status is bad? */
4244   return(status);
4245 }