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