blob: b3312af572765dc5ec6a541775f1e81011753b02 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% March 2000 %
18% %
19% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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*/
dirk6015d442013-10-19 15:07:40 +000039
40
cristy3ed852e2009-09-05 21:47:34 +000041/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
anthony2fbb5952012-05-05 12:35:30 +000045#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/attribute.h"
47#include "MagickCore/cache.h"
cristyafcb0492013-10-15 01:11:56 +000048#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/color.h"
cristyd228c032012-06-03 15:01:15 +000050#include "MagickCore/color-private.h"
cristy3d9f5ba2012-06-26 13:37:31 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/compare.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/effect.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/fx.h"
59#include "MagickCore/fx-private.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/histogram.h"
63#include "MagickCore/image.h"
cristy4c08aed2011-07-01 19:47:50 +000064#include "MagickCore/layer.h"
cristy7832dc22011-09-05 01:21:53 +000065#include "MagickCore/locale-private.h"
cristy4c08aed2011-07-01 19:47:50 +000066#include "MagickCore/list.h"
67#include "MagickCore/magick.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.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"
cristy7832dc22011-09-05 01:21:53 +000077#include "MagickCore/signature.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/statistic.h"
79#include "MagickCore/string_.h"
80#include "MagickCore/string-private.h"
81#include "MagickCore/token.h"
cristy7832dc22011-09-05 01:21:53 +000082#include "MagickCore/token-private.h"
cristy4c08aed2011-07-01 19:47:50 +000083#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000084#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000085#include "MagickCore/version.h"
86#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000087#include "MagickCore/xml-tree-private.h"
dirk6015d442013-10-19 15:07:40 +000088#if defined(MAGICKCORE_LCMS_DELEGATE)
89#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
90#include <lcms/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
cristy3ed852e2009-09-05 21:47:34 +0000101/*
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103% %
104% %
105% %
106% C l o n e I m a g e P r o p e r t i e s %
107% %
108% %
109% %
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%
anthonyd2f39e02012-08-20 12:29:28 +0000112% CloneImageProperties() clones all the image properties to another image.
cristy3ed852e2009-09-05 21:47:34 +0000113%
114% The format of the CloneImageProperties method is:
115%
116% MagickBooleanType CloneImageProperties(Image *image,
117% const Image *clone_image)
118%
119% A description of each parameter follows:
120%
121% o image: the image.
122%
123% o clone_image: the clone image.
124%
125*/
126MagickExport MagickBooleanType CloneImageProperties(Image *image,
127 const Image *clone_image)
128{
129 assert(image != (Image *) NULL);
130 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000131 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000132 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
133 assert(clone_image != (const Image *) NULL);
134 assert(clone_image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000135 if( IfMagickTrue(clone_image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000136 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
137 clone_image->filename);
138 (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
139 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
140 MaxTextExtent);
141 image->compression=clone_image->compression;
142 image->quality=clone_image->quality;
143 image->depth=clone_image->depth;
144 image->background_color=clone_image->background_color;
145 image->border_color=clone_image->border_color;
146 image->matte_color=clone_image->matte_color;
147 image->transparent_color=clone_image->transparent_color;
148 image->gamma=clone_image->gamma;
149 image->chromaticity=clone_image->chromaticity;
150 image->rendering_intent=clone_image->rendering_intent;
151 image->black_point_compensation=clone_image->black_point_compensation;
152 image->units=clone_image->units;
153 image->montage=(char *) NULL;
154 image->directory=(char *) NULL;
155 (void) CloneString(&image->geometry,clone_image->geometry);
156 image->offset=clone_image->offset;
cristy2a11bef2011-10-28 18:33:11 +0000157 image->resolution.x=clone_image->resolution.x;
158 image->resolution.y=clone_image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +0000159 image->page=clone_image->page;
160 image->tile_offset=clone_image->tile_offset;
161 image->extract_info=clone_image->extract_info;
cristy3ed852e2009-09-05 21:47:34 +0000162 image->filter=clone_image->filter;
cristy3ed852e2009-09-05 21:47:34 +0000163 image->fuzz=clone_image->fuzz;
cristyff848fa2013-06-10 23:34:40 +0000164 image->intensity=clone_image->intensity;
cristy3ed852e2009-09-05 21:47:34 +0000165 image->interlace=clone_image->interlace;
166 image->interpolate=clone_image->interpolate;
167 image->endian=clone_image->endian;
168 image->gravity=clone_image->gravity;
169 image->compose=clone_image->compose;
170 image->scene=clone_image->scene;
171 image->orientation=clone_image->orientation;
172 image->dispose=clone_image->dispose;
173 image->delay=clone_image->delay;
174 image->ticks_per_second=clone_image->ticks_per_second;
175 image->iterations=clone_image->iterations;
176 image->total_colors=clone_image->total_colors;
177 image->taint=clone_image->taint;
178 image->progress_monitor=clone_image->progress_monitor;
179 image->client_data=clone_image->client_data;
180 image->start_loop=clone_image->start_loop;
181 image->error=clone_image->error;
182 image->signature=clone_image->signature;
183 if (clone_image->properties != (void *) NULL)
184 {
185 if (image->properties != (void *) NULL)
186 DestroyImageProperties(image);
187 image->properties=CloneSplayTree((SplayTreeInfo *)
188 clone_image->properties,(void *(*)(void *)) ConstantString,
189 (void *(*)(void *)) ConstantString);
190 }
191 return(MagickTrue);
192}
dirk6015d442013-10-19 15:07:40 +0000193
194
cristy3ed852e2009-09-05 21:47:34 +0000195/*
196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197% %
198% %
199% %
200% D e f i n e I m a g e P r o p e r t y %
201% %
202% %
203% %
204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205%
anthonyd2f39e02012-08-20 12:29:28 +0000206% DefineImageProperty() associates an assignment string of the form
anthonya322a832013-04-27 06:28:03 +0000207% "key=value" with an artifact or options. It is equivelent to
208% SetImageProperty()
cristy3ed852e2009-09-05 21:47:34 +0000209%
210% The format of the DefineImageProperty method is:
211%
cristy31197622013-03-06 02:07:54 +0000212% MagickBooleanType DefineImageProperty(Image *image,const char *property,
213% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000214%
215% A description of each parameter follows:
216%
217% o image: the image.
218%
219% o property: the image property.
220%
cristyd15e6592011-10-15 00:13:06 +0000221% o exception: return any errors or warnings in this structure.
222%
cristy3ed852e2009-09-05 21:47:34 +0000223*/
224MagickExport MagickBooleanType DefineImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000225 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000226{
227 char
228 key[MaxTextExtent],
229 value[MaxTextExtent];
230
231 register char
232 *p;
233
234 assert(image != (Image *) NULL);
235 assert(property != (const char *) NULL);
236 (void) CopyMagickString(key,property,MaxTextExtent-1);
237 for (p=key; *p != '\0'; p++)
238 if (*p == '=')
239 break;
240 *value='\0';
241 if (*p == '=')
242 (void) CopyMagickString(value,p+1,MaxTextExtent);
243 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000244 return(SetImageProperty(image,key,value,exception));
cristy3ed852e2009-09-05 21:47:34 +0000245}
dirk6015d442013-10-19 15:07:40 +0000246
247
cristy3ed852e2009-09-05 21:47:34 +0000248/*
249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250% %
251% %
252% %
253% D e l e t e I m a g e P r o p e r t y %
254% %
255% %
256% %
257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258%
259% DeleteImageProperty() deletes an image property.
260%
261% The format of the DeleteImageProperty method is:
262%
263% MagickBooleanType DeleteImageProperty(Image *image,const char *property)
264%
265% A description of each parameter follows:
266%
267% o image: the image.
268%
269% o property: the image property.
270%
271*/
272MagickExport MagickBooleanType DeleteImageProperty(Image *image,
273 const char *property)
274{
275 assert(image != (Image *) NULL);
276 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000277 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000278 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
279 image->filename);
280 if (image->properties == (void *) NULL)
281 return(MagickFalse);
282 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
283}
dirk6015d442013-10-19 15:07:40 +0000284
285
cristy3ed852e2009-09-05 21:47:34 +0000286/*
287%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288% %
289% %
290% %
291% D e s t r o y I m a g e P r o p e r t i e s %
292% %
293% %
294% %
295%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
296%
anthony643c6132012-11-07 14:50:28 +0000297% DestroyImageProperties() destroys all properties and associated memory
anthonyd2f39e02012-08-20 12:29:28 +0000298% attached to the given image.
cristy3ed852e2009-09-05 21:47:34 +0000299%
300% The format of the DestroyDefines method is:
301%
302% void DestroyImageProperties(Image *image)
303%
304% A description of each parameter follows:
305%
306% o image: the image.
307%
308*/
309MagickExport void DestroyImageProperties(Image *image)
310{
311 assert(image != (Image *) NULL);
312 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000313 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
315 image->filename);
316 if (image->properties != (void *) NULL)
317 image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
318 image->properties);
319}
dirk6015d442013-10-19 15:07:40 +0000320
321
cristy3ed852e2009-09-05 21:47:34 +0000322/*
323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324% %
325% %
326% %
327% F o r m a t I m a g e P r o p e r t y %
328% %
329% %
330% %
331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332%
333% FormatImageProperty() permits formatted property/value pairs to be saved as
glennrp2cc891a2010-12-24 13:44:32 +0000334% an image property.
cristy3ed852e2009-09-05 21:47:34 +0000335%
336% The format of the FormatImageProperty method is:
337%
338% MagickBooleanType FormatImageProperty(Image *image,const char *property,
339% const char *format,...)
340%
341% A description of each parameter follows.
342%
343% o image: The image.
344%
345% o property: The attribute property.
346%
347% o format: A string describing the format to use to write the remaining
348% arguments.
349%
350*/
cristydb584ae2011-05-20 14:50:53 +0000351MagickExport MagickBooleanType FormatImageProperty(Image *image,
352 const char *property,const char *format,...)
cristy3ed852e2009-09-05 21:47:34 +0000353{
354 char
355 value[MaxTextExtent];
356
cristyc82a27b2011-10-21 01:07:16 +0000357 ExceptionInfo
358 *exception;
359
360 MagickBooleanType
361 status;
362
cristy20ec7592011-05-29 01:28:05 +0000363 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000364 n;
365
cristy3ed852e2009-09-05 21:47:34 +0000366 va_list
367 operands;
368
369 va_start(operands,format);
cristydb584ae2011-05-20 14:50:53 +0000370 n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
371 (void) n;
cristy3ed852e2009-09-05 21:47:34 +0000372 va_end(operands);
cristyc82a27b2011-10-21 01:07:16 +0000373 exception=AcquireExceptionInfo();
374 status=SetImageProperty(image,property,value,exception);
375 exception=DestroyExceptionInfo(exception);
376 return(status);
cristy3ed852e2009-09-05 21:47:34 +0000377}
dirk6015d442013-10-19 15:07:40 +0000378
379
cristy3ed852e2009-09-05 21:47:34 +0000380/*
381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
382% %
383% %
384% %
385% G e t I m a g e P r o p e r t y %
386% %
387% %
388% %
389%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390%
391% GetImageProperty() gets a value associated with an image property.
392%
anthonya322a832013-04-27 06:28:03 +0000393% This includes, profile prefixes, such as "exif:", "iptc:" and "8bim:"
394% It does not handle non-prifile prefixes, such as "fx:", "option:", or
395% "artifact:".
396%
397% The returned string is stored as a properity of the same name for faster
398% lookup later. It should NOT be freed by the caller.
anthonyd2f39e02012-08-20 12:29:28 +0000399%
cristy3ed852e2009-09-05 21:47:34 +0000400% The format of the GetImageProperty method is:
401%
cristyd15e6592011-10-15 00:13:06 +0000402% const char *GetImageProperty(const Image *image,const char *key,
403% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000404%
405% A description of each parameter follows:
406%
407% o image: the image.
408%
409% o key: the key.
410%
cristyd15e6592011-10-15 00:13:06 +0000411% o exception: return any errors or warnings in this structure.
412%
cristy3ed852e2009-09-05 21:47:34 +0000413*/
414
415static char
cristybb503372010-05-27 20:51:26 +0000416 *TracePSClippath(const unsigned char *,size_t,const size_t,
417 const size_t),
418 *TraceSVGClippath(const unsigned char *,size_t,const size_t,
419 const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000420
cristyd15e6592011-10-15 00:13:06 +0000421static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
422 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000423{
424 char
425 *attribute,
426 *message;
427
428 const StringInfo
429 *profile;
430
cristycee97112010-05-28 00:44:52 +0000431 long
cristy3ed852e2009-09-05 21:47:34 +0000432 count,
433 dataset,
434 record;
435
cristybb503372010-05-27 20:51:26 +0000436 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000437 i;
438
439 size_t
440 length;
441
442 profile=GetImageProfile(image,"iptc");
443 if (profile == (StringInfo *) NULL)
444 profile=GetImageProfile(image,"8bim");
445 if (profile == (StringInfo *) NULL)
446 return(MagickFalse);
447 count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
448 if (count != 2)
449 return(MagickFalse);
450 attribute=(char *) NULL;
cristybb503372010-05-27 20:51:26 +0000451 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
453 length=1;
cristybb503372010-05-27 20:51:26 +0000454 if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
cristy3ed852e2009-09-05 21:47:34 +0000455 continue;
456 length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
457 length|=GetStringInfoDatum(profile)[i+4];
cristycee97112010-05-28 00:44:52 +0000458 if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
459 ((long) GetStringInfoDatum(profile)[i+2] == record))
cristy3ed852e2009-09-05 21:47:34 +0000460 {
461 message=(char *) NULL;
462 if (~length >= 1)
463 message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
464 if (message != (char *) NULL)
465 {
466 (void) CopyMagickString(message,(char *) GetStringInfoDatum(
467 profile)+i+5,length+1);
468 (void) ConcatenateString(&attribute,message);
469 (void) ConcatenateString(&attribute,";");
470 message=DestroyString(message);
471 }
472 }
473 i+=5;
474 }
475 if ((attribute == (char *) NULL) || (*attribute == ';'))
476 {
477 if (attribute != (char *) NULL)
478 attribute=DestroyString(attribute);
479 return(MagickFalse);
480 }
481 attribute[strlen(attribute)-1]='\0';
cristyd15e6592011-10-15 00:13:06 +0000482 (void) SetImageProperty((Image *) image,key,(const char *) attribute,
483 exception);
cristy3ed852e2009-09-05 21:47:34 +0000484 attribute=DestroyString(attribute);
485 return(MagickTrue);
486}
487
cristybb503372010-05-27 20:51:26 +0000488static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000489{
490 if (x > y)
491 return(x);
492 return(y);
493}
494
cristy4a8b0172012-02-03 16:39:53 +0000495static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
496{
497 if (x < y)
498 return(x);
499 return(y);
500}
501
cristy3ed852e2009-09-05 21:47:34 +0000502static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
503{
504 int
505 c;
506
507 if (*length < 1)
508 return(EOF);
509 c=(int) (*(*p)++);
510 (*length)--;
511 return(c);
512}
513
cristybb503372010-05-27 20:51:26 +0000514static inline size_t ReadPropertyMSBLong(const unsigned char **p,
cristy3ed852e2009-09-05 21:47:34 +0000515 size_t *length)
516{
517 int
518 c;
519
cristybb503372010-05-27 20:51:26 +0000520 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000521 i;
522
523 unsigned char
524 buffer[4];
525
cristybb503372010-05-27 20:51:26 +0000526 size_t
cristy3ed852e2009-09-05 21:47:34 +0000527 value;
528
529 if (*length < 4)
530 return(~0UL);
531 for (i=0; i < 4; i++)
532 {
533 c=(int) (*(*p)++);
534 (*length)--;
535 buffer[i]=(unsigned char) c;
536 }
cristybb503372010-05-27 20:51:26 +0000537 value=(size_t) (buffer[0] << 24);
cristy3ed852e2009-09-05 21:47:34 +0000538 value|=buffer[1] << 16;
539 value|=buffer[2] << 8;
540 value|=buffer[3];
541 return(value & 0xffffffff);
542}
543
544static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
545 size_t *length)
546{
547 int
548 c;
549
cristybb503372010-05-27 20:51:26 +0000550 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000551 i;
552
553 unsigned char
554 buffer[2];
555
556 unsigned short
557 value;
558
559 if (*length < 2)
dirk93b02b72013-11-16 16:03:36 +0000560 return((unsigned short) ~0);
cristy3ed852e2009-09-05 21:47:34 +0000561 for (i=0; i < 2; i++)
562 {
563 c=(int) (*(*p)++);
564 (*length)--;
565 buffer[i]=(unsigned char) c;
566 }
567 value=(unsigned short) (buffer[0] << 8);
568 value|=buffer[1];
569 return((unsigned short) (value & 0xffff));
570}
571
cristyd15e6592011-10-15 00:13:06 +0000572static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
573 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000574{
575 char
576 *attribute,
577 format[MaxTextExtent],
578 name[MaxTextExtent],
579 *resource;
580
581 const StringInfo
582 *profile;
583
584 const unsigned char
585 *info;
586
cristycee97112010-05-28 00:44:52 +0000587 long
cristy3ed852e2009-09-05 21:47:34 +0000588 start,
cristycee97112010-05-28 00:44:52 +0000589 stop;
cristy3ed852e2009-09-05 21:47:34 +0000590
591 MagickBooleanType
592 status;
593
cristybb503372010-05-27 20:51:26 +0000594 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000595 i;
596
597 ssize_t
cristycee97112010-05-28 00:44:52 +0000598 count,
599 id,
600 sub_number;
cristy3ed852e2009-09-05 21:47:34 +0000601
602 size_t
603 length;
604
605 /*
glennrp2cc891a2010-12-24 13:44:32 +0000606 There are no newlines in path names, so it's safe as terminator.
cristy3ed852e2009-09-05 21:47:34 +0000607 */
608 profile=GetImageProfile(image,"8bim");
609 if (profile == (StringInfo *) NULL)
610 return(MagickFalse);
611 count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
612 format);
613 if ((count != 2) && (count != 3) && (count != 4))
614 return(MagickFalse);
615 if (count < 4)
616 (void) CopyMagickString(format,"SVG",MaxTextExtent);
617 if (count < 3)
618 *name='\0';
619 sub_number=1;
620 if (*name == '#')
cristyad740052010-07-03 01:38:03 +0000621 sub_number=(ssize_t) StringToLong(&name[1]);
cristy3ed852e2009-09-05 21:47:34 +0000622 sub_number=MagickMax(sub_number,1L);
623 resource=(char *) NULL;
624 status=MagickFalse;
625 length=GetStringInfoLength(profile);
626 info=GetStringInfoDatum(profile);
anthony2fbb5952012-05-05 12:35:30 +0000627 while ((length > 0) && IfMagickFalse(status))
cristy3ed852e2009-09-05 21:47:34 +0000628 {
629 if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
630 continue;
631 if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
632 continue;
633 if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
634 continue;
635 if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
636 continue;
cristy4a8b0172012-02-03 16:39:53 +0000637 id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
cristycee97112010-05-28 00:44:52 +0000638 if (id < (ssize_t) start)
cristy3ed852e2009-09-05 21:47:34 +0000639 continue;
cristycee97112010-05-28 00:44:52 +0000640 if (id > (ssize_t) stop)
cristy3ed852e2009-09-05 21:47:34 +0000641 continue;
642 if (resource != (char *) NULL)
643 resource=DestroyString(resource);
644 count=(ssize_t) ReadPropertyByte(&info,&length);
645 if ((count != 0) && ((size_t) count <= length))
646 {
647 resource=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +0000648 if (~((size_t) count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000649 resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
650 sizeof(*resource));
651 if (resource != (char *) NULL)
652 {
cristybb503372010-05-27 20:51:26 +0000653 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000654 resource[i]=(char) ReadPropertyByte(&info,&length);
655 resource[count]='\0';
656 }
657 }
658 if ((count & 0x01) == 0)
659 (void) ReadPropertyByte(&info,&length);
cristy55a91cd2010-12-01 00:57:40 +0000660 count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
cristy3ed852e2009-09-05 21:47:34 +0000661 if ((*name != '\0') && (*name != '#'))
662 if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
663 {
664 /*
665 No name match, scroll forward and try next.
666 */
667 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000668 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000669 continue;
670 }
671 if ((*name == '#') && (sub_number != 1))
672 {
673 /*
674 No numbered match, scroll forward and try next.
675 */
676 sub_number--;
677 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000678 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000679 continue;
680 }
681 /*
682 We have the resource of interest.
683 */
684 attribute=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +0000685 if (~((size_t) count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000686 attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
687 sizeof(*attribute));
688 if (attribute != (char *) NULL)
689 {
690 (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
691 attribute[count]='\0';
692 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000693 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000694 if ((id <= 1999) || (id >= 2999))
695 (void) SetImageProperty((Image *) image,key,(const char *)
cristyd15e6592011-10-15 00:13:06 +0000696 attribute,exception);
cristy3ed852e2009-09-05 21:47:34 +0000697 else
698 {
699 char
700 *path;
701
702 if (LocaleCompare(format,"svg") == 0)
703 path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
704 image->columns,image->rows);
705 else
706 path=TracePSClippath((unsigned char *) attribute,(size_t) count,
707 image->columns,image->rows);
cristyd15e6592011-10-15 00:13:06 +0000708 (void) SetImageProperty((Image *) image,key,(const char *) path,
709 exception);
cristy3ed852e2009-09-05 21:47:34 +0000710 path=DestroyString(path);
711 }
712 attribute=DestroyString(attribute);
713 status=MagickTrue;
714 }
715 }
716 if (resource != (char *) NULL)
717 resource=DestroyString(resource);
718 return(status);
719}
720
721static inline unsigned short ReadPropertyShort(const EndianType endian,
722 const unsigned char *buffer)
723{
724 unsigned short
725 value;
726
cristy1715f792012-11-22 16:56:12 +0000727 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +0000728 {
cristy1715f792012-11-22 16:56:12 +0000729 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
cristy3ed852e2009-09-05 21:47:34 +0000730 return((unsigned short) (value & 0xffff));
731 }
cristy1715f792012-11-22 16:56:12 +0000732 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
733 ((unsigned char *) buffer)[1]);
cristy3ed852e2009-09-05 21:47:34 +0000734 return((unsigned short) (value & 0xffff));
735}
736
cristybb503372010-05-27 20:51:26 +0000737static inline size_t ReadPropertyLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +0000738 const unsigned char *buffer)
739{
cristybb503372010-05-27 20:51:26 +0000740 size_t
cristy3ed852e2009-09-05 21:47:34 +0000741 value;
742
cristy1715f792012-11-22 16:56:12 +0000743 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +0000744 {
cristy1715f792012-11-22 16:56:12 +0000745 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
746 (buffer[1] << 8 ) | (buffer[0]));
cristybb503372010-05-27 20:51:26 +0000747 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000748 }
cristy1715f792012-11-22 16:56:12 +0000749 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
750 (buffer[2] << 8) | buffer[3]);
cristybb503372010-05-27 20:51:26 +0000751 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000752}
753
754static MagickBooleanType GetEXIFProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +0000755 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000756{
757#define MaxDirectoryStack 16
758#define EXIF_DELIMITER "\n"
759#define EXIF_NUM_FORMATS 12
760#define EXIF_FMT_BYTE 1
761#define EXIF_FMT_STRING 2
762#define EXIF_FMT_USHORT 3
763#define EXIF_FMT_ULONG 4
764#define EXIF_FMT_URATIONAL 5
765#define EXIF_FMT_SBYTE 6
766#define EXIF_FMT_UNDEFINED 7
767#define EXIF_FMT_SSHORT 8
768#define EXIF_FMT_SLONG 9
769#define EXIF_FMT_SRATIONAL 10
770#define EXIF_FMT_SINGLE 11
771#define EXIF_FMT_DOUBLE 12
772#define TAG_EXIF_OFFSET 0x8769
773#define TAG_GPS_OFFSET 0x8825
774#define TAG_INTEROP_OFFSET 0xa005
775
cristy96ea4862011-11-22 00:45:47 +0000776#define EXIFMultipleValues(size,format,arg) \
cristy3ed852e2009-09-05 21:47:34 +0000777{ \
cristybb503372010-05-27 20:51:26 +0000778 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000779 component; \
780 \
781 size_t \
782 length; \
783 \
784 unsigned char \
785 *p1; \
786 \
787 length=0; \
788 p1=p; \
789 for (component=0; component < components; component++) \
790 { \
cristyb51dff52011-05-19 16:55:47 +0000791 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristy3ed852e2009-09-05 21:47:34 +0000792 format", ",arg); \
cristy37e0b382011-06-07 13:31:21 +0000793 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000794 length=MaxTextExtent-1; \
795 p1+=size; \
796 } \
797 if (length > 1) \
798 buffer[length-2]='\0'; \
799 value=AcquireString(buffer); \
800}
801
cristy96ea4862011-11-22 00:45:47 +0000802#define EXIFMultipleFractions(size,format,arg1,arg2) \
cristy3ed852e2009-09-05 21:47:34 +0000803{ \
cristybb503372010-05-27 20:51:26 +0000804 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000805 component; \
806 \
807 size_t \
808 length; \
809 \
810 unsigned char \
811 *p1; \
812 \
813 length=0; \
814 p1=p; \
815 for (component=0; component < components; component++) \
816 { \
cristyb51dff52011-05-19 16:55:47 +0000817 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristyd66c1082012-03-28 18:17:37 +0000818 format", ",(arg1),(arg2)); \
cristy37e0b382011-06-07 13:31:21 +0000819 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000820 length=MaxTextExtent-1; \
821 p1+=size; \
822 } \
823 if (length > 1) \
824 buffer[length-2]='\0'; \
825 value=AcquireString(buffer); \
826}
827
828 typedef struct _DirectoryInfo
829 {
830 const unsigned char
831 *directory;
832
cristybb503372010-05-27 20:51:26 +0000833 size_t
cristy3d8d1f12012-02-29 01:59:28 +0000834 entry;
835
836 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000837 offset;
838 } DirectoryInfo;
839
840 typedef struct _TagInfo
841 {
cristybb503372010-05-27 20:51:26 +0000842 size_t
cristy3ed852e2009-09-05 21:47:34 +0000843 tag;
844
845 const char
846 *description;
847 } TagInfo;
848
849 static TagInfo
850 EXIFTag[] =
851 {
852 { 0x001, "exif:InteroperabilityIndex" },
853 { 0x002, "exif:InteroperabilityVersion" },
854 { 0x100, "exif:ImageWidth" },
855 { 0x101, "exif:ImageLength" },
856 { 0x102, "exif:BitsPerSample" },
857 { 0x103, "exif:Compression" },
858 { 0x106, "exif:PhotometricInterpretation" },
859 { 0x10a, "exif:FillOrder" },
860 { 0x10d, "exif:DocumentName" },
861 { 0x10e, "exif:ImageDescription" },
862 { 0x10f, "exif:Make" },
863 { 0x110, "exif:Model" },
864 { 0x111, "exif:StripOffsets" },
865 { 0x112, "exif:Orientation" },
866 { 0x115, "exif:SamplesPerPixel" },
867 { 0x116, "exif:RowsPerStrip" },
868 { 0x117, "exif:StripByteCounts" },
869 { 0x11a, "exif:XResolution" },
870 { 0x11b, "exif:YResolution" },
871 { 0x11c, "exif:PlanarConfiguration" },
872 { 0x11d, "exif:PageName" },
873 { 0x11e, "exif:XPosition" },
874 { 0x11f, "exif:YPosition" },
875 { 0x118, "exif:MinSampleValue" },
876 { 0x119, "exif:MaxSampleValue" },
877 { 0x120, "exif:FreeOffsets" },
878 { 0x121, "exif:FreeByteCounts" },
879 { 0x122, "exif:GrayResponseUnit" },
880 { 0x123, "exif:GrayResponseCurve" },
881 { 0x124, "exif:T4Options" },
882 { 0x125, "exif:T6Options" },
883 { 0x128, "exif:ResolutionUnit" },
884 { 0x12d, "exif:TransferFunction" },
885 { 0x131, "exif:Software" },
886 { 0x132, "exif:DateTime" },
887 { 0x13b, "exif:Artist" },
888 { 0x13e, "exif:WhitePoint" },
889 { 0x13f, "exif:PrimaryChromaticities" },
890 { 0x140, "exif:ColorMap" },
891 { 0x141, "exif:HalfToneHints" },
892 { 0x142, "exif:TileWidth" },
893 { 0x143, "exif:TileLength" },
894 { 0x144, "exif:TileOffsets" },
895 { 0x145, "exif:TileByteCounts" },
896 { 0x14a, "exif:SubIFD" },
897 { 0x14c, "exif:InkSet" },
898 { 0x14d, "exif:InkNames" },
899 { 0x14e, "exif:NumberOfInks" },
900 { 0x150, "exif:DotRange" },
901 { 0x151, "exif:TargetPrinter" },
902 { 0x152, "exif:ExtraSample" },
903 { 0x153, "exif:SampleFormat" },
904 { 0x154, "exif:SMinSampleValue" },
905 { 0x155, "exif:SMaxSampleValue" },
906 { 0x156, "exif:TransferRange" },
907 { 0x157, "exif:ClipPath" },
908 { 0x158, "exif:XClipPathUnits" },
909 { 0x159, "exif:YClipPathUnits" },
910 { 0x15a, "exif:Indexed" },
911 { 0x15b, "exif:JPEGTables" },
912 { 0x15f, "exif:OPIProxy" },
913 { 0x200, "exif:JPEGProc" },
914 { 0x201, "exif:JPEGInterchangeFormat" },
915 { 0x202, "exif:JPEGInterchangeFormatLength" },
916 { 0x203, "exif:JPEGRestartInterval" },
917 { 0x205, "exif:JPEGLosslessPredictors" },
918 { 0x206, "exif:JPEGPointTransforms" },
919 { 0x207, "exif:JPEGQTables" },
920 { 0x208, "exif:JPEGDCTables" },
921 { 0x209, "exif:JPEGACTables" },
922 { 0x211, "exif:YCbCrCoefficients" },
923 { 0x212, "exif:YCbCrSubSampling" },
924 { 0x213, "exif:YCbCrPositioning" },
925 { 0x214, "exif:ReferenceBlackWhite" },
926 { 0x2bc, "exif:ExtensibleMetadataPlatform" },
927 { 0x301, "exif:Gamma" },
928 { 0x302, "exif:ICCProfileDescriptor" },
929 { 0x303, "exif:SRGBRenderingIntent" },
930 { 0x320, "exif:ImageTitle" },
931 { 0x5001, "exif:ResolutionXUnit" },
932 { 0x5002, "exif:ResolutionYUnit" },
933 { 0x5003, "exif:ResolutionXLengthUnit" },
934 { 0x5004, "exif:ResolutionYLengthUnit" },
935 { 0x5005, "exif:PrintFlags" },
936 { 0x5006, "exif:PrintFlagsVersion" },
937 { 0x5007, "exif:PrintFlagsCrop" },
938 { 0x5008, "exif:PrintFlagsBleedWidth" },
939 { 0x5009, "exif:PrintFlagsBleedWidthScale" },
940 { 0x500A, "exif:HalftoneLPI" },
941 { 0x500B, "exif:HalftoneLPIUnit" },
942 { 0x500C, "exif:HalftoneDegree" },
943 { 0x500D, "exif:HalftoneShape" },
944 { 0x500E, "exif:HalftoneMisc" },
945 { 0x500F, "exif:HalftoneScreen" },
946 { 0x5010, "exif:JPEGQuality" },
947 { 0x5011, "exif:GridSize" },
948 { 0x5012, "exif:ThumbnailFormat" },
949 { 0x5013, "exif:ThumbnailWidth" },
950 { 0x5014, "exif:ThumbnailHeight" },
951 { 0x5015, "exif:ThumbnailColorDepth" },
952 { 0x5016, "exif:ThumbnailPlanes" },
953 { 0x5017, "exif:ThumbnailRawBytes" },
954 { 0x5018, "exif:ThumbnailSize" },
955 { 0x5019, "exif:ThumbnailCompressedSize" },
956 { 0x501a, "exif:ColorTransferFunction" },
957 { 0x501b, "exif:ThumbnailData" },
958 { 0x5020, "exif:ThumbnailImageWidth" },
959 { 0x5021, "exif:ThumbnailImageHeight" },
960 { 0x5022, "exif:ThumbnailBitsPerSample" },
961 { 0x5023, "exif:ThumbnailCompression" },
962 { 0x5024, "exif:ThumbnailPhotometricInterp" },
963 { 0x5025, "exif:ThumbnailImageDescription" },
964 { 0x5026, "exif:ThumbnailEquipMake" },
965 { 0x5027, "exif:ThumbnailEquipModel" },
966 { 0x5028, "exif:ThumbnailStripOffsets" },
967 { 0x5029, "exif:ThumbnailOrientation" },
968 { 0x502a, "exif:ThumbnailSamplesPerPixel" },
969 { 0x502b, "exif:ThumbnailRowsPerStrip" },
970 { 0x502c, "exif:ThumbnailStripBytesCount" },
971 { 0x502d, "exif:ThumbnailResolutionX" },
972 { 0x502e, "exif:ThumbnailResolutionY" },
973 { 0x502f, "exif:ThumbnailPlanarConfig" },
974 { 0x5030, "exif:ThumbnailResolutionUnit" },
975 { 0x5031, "exif:ThumbnailTransferFunction" },
976 { 0x5032, "exif:ThumbnailSoftwareUsed" },
977 { 0x5033, "exif:ThumbnailDateTime" },
978 { 0x5034, "exif:ThumbnailArtist" },
979 { 0x5035, "exif:ThumbnailWhitePoint" },
980 { 0x5036, "exif:ThumbnailPrimaryChromaticities" },
981 { 0x5037, "exif:ThumbnailYCbCrCoefficients" },
982 { 0x5038, "exif:ThumbnailYCbCrSubsampling" },
983 { 0x5039, "exif:ThumbnailYCbCrPositioning" },
984 { 0x503A, "exif:ThumbnailRefBlackWhite" },
985 { 0x503B, "exif:ThumbnailCopyRight" },
986 { 0x5090, "exif:LuminanceTable" },
987 { 0x5091, "exif:ChrominanceTable" },
988 { 0x5100, "exif:FrameDelay" },
989 { 0x5101, "exif:LoopCount" },
990 { 0x5110, "exif:PixelUnit" },
991 { 0x5111, "exif:PixelPerUnitX" },
992 { 0x5112, "exif:PixelPerUnitY" },
993 { 0x5113, "exif:PaletteHistogram" },
994 { 0x1000, "exif:RelatedImageFileFormat" },
995 { 0x1001, "exif:RelatedImageLength" },
996 { 0x1002, "exif:RelatedImageWidth" },
997 { 0x800d, "exif:ImageID" },
998 { 0x80e3, "exif:Matteing" },
999 { 0x80e4, "exif:DataType" },
1000 { 0x80e5, "exif:ImageDepth" },
1001 { 0x80e6, "exif:TileDepth" },
1002 { 0x828d, "exif:CFARepeatPatternDim" },
1003 { 0x828e, "exif:CFAPattern2" },
1004 { 0x828f, "exif:BatteryLevel" },
1005 { 0x8298, "exif:Copyright" },
1006 { 0x829a, "exif:ExposureTime" },
1007 { 0x829d, "exif:FNumber" },
1008 { 0x83bb, "exif:IPTC/NAA" },
1009 { 0x84e3, "exif:IT8RasterPadding" },
1010 { 0x84e5, "exif:IT8ColorTable" },
1011 { 0x8649, "exif:ImageResourceInformation" },
1012 { 0x8769, "exif:ExifOffset" },
1013 { 0x8773, "exif:InterColorProfile" },
1014 { 0x8822, "exif:ExposureProgram" },
1015 { 0x8824, "exif:SpectralSensitivity" },
1016 { 0x8825, "exif:GPSInfo" },
1017 { 0x8827, "exif:ISOSpeedRatings" },
1018 { 0x8828, "exif:OECF" },
1019 { 0x8829, "exif:Interlace" },
1020 { 0x882a, "exif:TimeZoneOffset" },
1021 { 0x882b, "exif:SelfTimerMode" },
1022 { 0x9000, "exif:ExifVersion" },
1023 { 0x9003, "exif:DateTimeOriginal" },
1024 { 0x9004, "exif:DateTimeDigitized" },
1025 { 0x9101, "exif:ComponentsConfiguration" },
1026 { 0x9102, "exif:CompressedBitsPerPixel" },
1027 { 0x9201, "exif:ShutterSpeedValue" },
1028 { 0x9202, "exif:ApertureValue" },
1029 { 0x9203, "exif:BrightnessValue" },
1030 { 0x9204, "exif:ExposureBiasValue" },
1031 { 0x9205, "exif:MaxApertureValue" },
1032 { 0x9206, "exif:SubjectDistance" },
1033 { 0x9207, "exif:MeteringMode" },
1034 { 0x9208, "exif:LightSource" },
1035 { 0x9209, "exif:Flash" },
1036 { 0x920a, "exif:FocalLength" },
1037 { 0x920b, "exif:FlashEnergy" },
1038 { 0x920c, "exif:SpatialFrequencyResponse" },
1039 { 0x920d, "exif:Noise" },
1040 { 0x9211, "exif:ImageNumber" },
1041 { 0x9212, "exif:SecurityClassification" },
1042 { 0x9213, "exif:ImageHistory" },
1043 { 0x9214, "exif:SubjectArea" },
1044 { 0x9215, "exif:ExposureIndex" },
1045 { 0x9216, "exif:TIFF-EPStandardID" },
1046 { 0x927c, "exif:MakerNote" },
1047 { 0x9C9b, "exif:WinXP-Title" },
1048 { 0x9C9c, "exif:WinXP-Comments" },
1049 { 0x9C9d, "exif:WinXP-Author" },
1050 { 0x9C9e, "exif:WinXP-Keywords" },
1051 { 0x9C9f, "exif:WinXP-Subject" },
1052 { 0x9286, "exif:UserComment" },
1053 { 0x9290, "exif:SubSecTime" },
1054 { 0x9291, "exif:SubSecTimeOriginal" },
1055 { 0x9292, "exif:SubSecTimeDigitized" },
1056 { 0xa000, "exif:FlashPixVersion" },
1057 { 0xa001, "exif:ColorSpace" },
1058 { 0xa002, "exif:ExifImageWidth" },
1059 { 0xa003, "exif:ExifImageLength" },
1060 { 0xa004, "exif:RelatedSoundFile" },
1061 { 0xa005, "exif:InteroperabilityOffset" },
1062 { 0xa20b, "exif:FlashEnergy" },
1063 { 0xa20c, "exif:SpatialFrequencyResponse" },
1064 { 0xa20d, "exif:Noise" },
1065 { 0xa20e, "exif:FocalPlaneXResolution" },
1066 { 0xa20f, "exif:FocalPlaneYResolution" },
1067 { 0xa210, "exif:FocalPlaneResolutionUnit" },
1068 { 0xa214, "exif:SubjectLocation" },
1069 { 0xa215, "exif:ExposureIndex" },
1070 { 0xa216, "exif:TIFF/EPStandardID" },
1071 { 0xa217, "exif:SensingMethod" },
1072 { 0xa300, "exif:FileSource" },
1073 { 0xa301, "exif:SceneType" },
1074 { 0xa302, "exif:CFAPattern" },
1075 { 0xa401, "exif:CustomRendered" },
1076 { 0xa402, "exif:ExposureMode" },
1077 { 0xa403, "exif:WhiteBalance" },
1078 { 0xa404, "exif:DigitalZoomRatio" },
1079 { 0xa405, "exif:FocalLengthIn35mmFilm" },
1080 { 0xa406, "exif:SceneCaptureType" },
1081 { 0xa407, "exif:GainControl" },
1082 { 0xa408, "exif:Contrast" },
1083 { 0xa409, "exif:Saturation" },
1084 { 0xa40a, "exif:Sharpness" },
1085 { 0xa40b, "exif:DeviceSettingDescription" },
1086 { 0xa40c, "exif:SubjectDistanceRange" },
1087 { 0xa420, "exif:ImageUniqueID" },
1088 { 0xc4a5, "exif:PrintImageMatching" },
cristyb3fea0e2009-11-28 01:46:20 +00001089 { 0xa500, "exif:Gamma" },
1090 { 0xc640, "exif:CR2Slice" },
cristy3ed852e2009-09-05 21:47:34 +00001091 { 0x10000, "exif:GPSVersionID" },
1092 { 0x10001, "exif:GPSLatitudeRef" },
1093 { 0x10002, "exif:GPSLatitude" },
1094 { 0x10003, "exif:GPSLongitudeRef" },
1095 { 0x10004, "exif:GPSLongitude" },
1096 { 0x10005, "exif:GPSAltitudeRef" },
1097 { 0x10006, "exif:GPSAltitude" },
1098 { 0x10007, "exif:GPSTimeStamp" },
1099 { 0x10008, "exif:GPSSatellites" },
1100 { 0x10009, "exif:GPSStatus" },
1101 { 0x1000a, "exif:GPSMeasureMode" },
1102 { 0x1000b, "exif:GPSDop" },
1103 { 0x1000c, "exif:GPSSpeedRef" },
1104 { 0x1000d, "exif:GPSSpeed" },
1105 { 0x1000e, "exif:GPSTrackRef" },
1106 { 0x1000f, "exif:GPSTrack" },
1107 { 0x10010, "exif:GPSImgDirectionRef" },
1108 { 0x10011, "exif:GPSImgDirection" },
1109 { 0x10012, "exif:GPSMapDatum" },
1110 { 0x10013, "exif:GPSDestLatitudeRef" },
1111 { 0x10014, "exif:GPSDestLatitude" },
1112 { 0x10015, "exif:GPSDestLongitudeRef" },
1113 { 0x10016, "exif:GPSDestLongitude" },
1114 { 0x10017, "exif:GPSDestBearingRef" },
1115 { 0x10018, "exif:GPSDestBearing" },
1116 { 0x10019, "exif:GPSDestDistanceRef" },
1117 { 0x1001a, "exif:GPSDestDistance" },
1118 { 0x1001b, "exif:GPSProcessingMethod" },
1119 { 0x1001c, "exif:GPSAreaInformation" },
1120 { 0x1001d, "exif:GPSDateStamp" },
1121 { 0x1001e, "exif:GPSDifferential" },
cristy6e617e62012-09-29 14:12:23 +00001122 { 0x00000, (const char *) NULL }
cristy3ed852e2009-09-05 21:47:34 +00001123 };
1124
1125 const StringInfo
1126 *profile;
1127
1128 const unsigned char
1129 *directory,
1130 *exif;
1131
1132 DirectoryInfo
1133 directory_stack[MaxDirectoryStack];
1134
1135 EndianType
1136 endian;
1137
cristy929ea322011-02-21 15:21:35 +00001138 MagickBooleanType
1139 status;
cristy3ed852e2009-09-05 21:47:34 +00001140
cristybb503372010-05-27 20:51:26 +00001141 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001142 i;
1143
1144 size_t
cristy3ed852e2009-09-05 21:47:34 +00001145 entry,
cristy929ea322011-02-21 15:21:35 +00001146 length,
cristy3ed852e2009-09-05 21:47:34 +00001147 number_entries,
cristy3ed852e2009-09-05 21:47:34 +00001148 tag;
1149
cristy142ab012012-02-02 02:26:18 +00001150 SplayTreeInfo
1151 *exif_resources;
1152
cristy929ea322011-02-21 15:21:35 +00001153 ssize_t
1154 all,
1155 id,
1156 level,
1157 offset,
cristy3d8d1f12012-02-29 01:59:28 +00001158 tag_offset,
cristy929ea322011-02-21 15:21:35 +00001159 tag_value;
1160
1161 static int
1162 tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1163
cristy3ed852e2009-09-05 21:47:34 +00001164 /*
1165 If EXIF data exists, then try to parse the request for a tag.
1166 */
1167 profile=GetImageProfile(image,"exif");
1168 if (profile == (StringInfo *) NULL)
1169 return(MagickFalse);
1170 if ((property == (const char *) NULL) || (*property == '\0'))
1171 return(MagickFalse);
1172 while (isspace((int) ((unsigned char) *property)) != 0)
1173 property++;
1174 all=0;
1175 tag=(~0UL);
1176 switch (*(property+5))
1177 {
1178 case '*':
1179 {
1180 /*
1181 Caller has asked for all the tags in the EXIF data.
1182 */
1183 tag=0;
1184 all=1; /* return the data in description=value format */
1185 break;
1186 }
1187 case '!':
1188 {
1189 tag=0;
1190 all=2; /* return the data in tagid=value format */
1191 break;
1192 }
1193 case '#':
1194 case '@':
1195 {
1196 int
1197 c;
1198
1199 size_t
1200 n;
1201
1202 /*
1203 Check for a hex based tag specification first.
1204 */
1205 tag=(*(property+5) == '@') ? 1UL : 0UL;
1206 property+=6;
1207 n=strlen(property);
1208 if (n != 4)
1209 return(MagickFalse);
1210 /*
1211 Parse tag specification as a hex number.
1212 */
1213 n/=4;
1214 do
1215 {
cristybb503372010-05-27 20:51:26 +00001216 for (i=(ssize_t) n-1L; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +00001217 {
1218 c=(*property++);
1219 tag<<=4;
1220 if ((c >= '0') && (c <= '9'))
1221 tag|=(c-'0');
1222 else
1223 if ((c >= 'A') && (c <= 'F'))
1224 tag|=(c-('A'-10));
1225 else
1226 if ((c >= 'a') && (c <= 'f'))
1227 tag|=(c-('a'-10));
1228 else
1229 return(MagickFalse);
1230 }
1231 } while (*property != '\0');
1232 break;
1233 }
1234 default:
1235 {
1236 /*
1237 Try to match the text with a tag name instead.
1238 */
1239 for (i=0; ; i++)
1240 {
1241 if (EXIFTag[i].tag == 0)
1242 break;
1243 if (LocaleCompare(EXIFTag[i].description,property) == 0)
1244 {
cristybb503372010-05-27 20:51:26 +00001245 tag=(size_t) EXIFTag[i].tag;
cristy3ed852e2009-09-05 21:47:34 +00001246 break;
1247 }
1248 }
1249 break;
1250 }
1251 }
1252 if (tag == (~0UL))
1253 return(MagickFalse);
1254 length=GetStringInfoLength(profile);
1255 exif=GetStringInfoDatum(profile);
1256 while (length != 0)
1257 {
1258 if (ReadPropertyByte(&exif,&length) != 0x45)
1259 continue;
1260 if (ReadPropertyByte(&exif,&length) != 0x78)
1261 continue;
1262 if (ReadPropertyByte(&exif,&length) != 0x69)
1263 continue;
1264 if (ReadPropertyByte(&exif,&length) != 0x66)
1265 continue;
1266 if (ReadPropertyByte(&exif,&length) != 0x00)
1267 continue;
1268 if (ReadPropertyByte(&exif,&length) != 0x00)
1269 continue;
1270 break;
1271 }
1272 if (length < 16)
1273 return(MagickFalse);
cristyf0696672012-02-01 23:43:06 +00001274 id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
cristy3ed852e2009-09-05 21:47:34 +00001275 endian=LSBEndian;
1276 if (id == 0x4949)
1277 endian=LSBEndian;
1278 else
1279 if (id == 0x4D4D)
1280 endian=MSBEndian;
1281 else
1282 return(MagickFalse);
1283 if (ReadPropertyShort(endian,exif+2) != 0x002a)
1284 return(MagickFalse);
1285 /*
1286 This the offset to the first IFD.
1287 */
cristy55a91cd2010-12-01 00:57:40 +00001288 offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
cristy64de1572012-02-29 02:44:11 +00001289 if ((offset < 0) || (size_t) offset >= length)
cristy3ed852e2009-09-05 21:47:34 +00001290 return(MagickFalse);
1291 /*
1292 Set the pointer to the first IFD and follow it were it leads.
1293 */
cristy929ea322011-02-21 15:21:35 +00001294 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001295 directory=exif+offset;
1296 level=0;
1297 entry=0;
1298 tag_offset=0;
cristy142ab012012-02-02 02:26:18 +00001299 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1300 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1301 do
cristy3ed852e2009-09-05 21:47:34 +00001302 {
1303 /*
1304 If there is anything on the stack then pop it off.
1305 */
1306 if (level > 0)
1307 {
1308 level--;
1309 directory=directory_stack[level].directory;
1310 entry=directory_stack[level].entry;
1311 tag_offset=directory_stack[level].offset;
1312 }
1313 /*
1314 Determine how many entries there are in the current IFD.
1315 */
cristy4a8b0172012-02-03 16:39:53 +00001316 number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
cristy3ed852e2009-09-05 21:47:34 +00001317 for ( ; entry < number_entries; entry++)
1318 {
cristy3ed852e2009-09-05 21:47:34 +00001319 register unsigned char
1320 *p,
1321 *q;
1322
1323 size_t
anthony90ebc0d2012-04-28 08:10:02 +00001324 format;
cristy3ed852e2009-09-05 21:47:34 +00001325
cristy9d314ff2011-03-09 01:30:28 +00001326 ssize_t
anthony90ebc0d2012-04-28 08:10:02 +00001327 number_bytes,
cristy9d314ff2011-03-09 01:30:28 +00001328 components;
cristy3ed852e2009-09-05 21:47:34 +00001329
cristyf0696672012-02-01 23:43:06 +00001330 q=(unsigned char *) (directory+(12*entry)+2);
cristy142ab012012-02-02 02:26:18 +00001331 if (GetValueFromSplayTree(exif_resources,q) == q)
1332 break;
1333 (void) AddValueToSplayTree(exif_resources,q,q);
cristyf0696672012-02-01 23:43:06 +00001334 tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
1335 format=(size_t) ((int) ReadPropertyShort(endian,q+2));
cristy3ed852e2009-09-05 21:47:34 +00001336 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1337 break;
cristy55a91cd2010-12-01 00:57:40 +00001338 components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00001339 number_bytes=(size_t) components*tag_bytes[format];
cristy5f06bc12012-04-03 12:47:36 +00001340 if (number_bytes < components)
1341 break; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001342 if (number_bytes <= 4)
1343 p=q+8;
1344 else
1345 {
1346 ssize_t
1347 offset;
1348
1349 /*
1350 The directory entry contains an offset.
1351 */
cristy55a91cd2010-12-01 00:57:40 +00001352 offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
anthony90ebc0d2012-04-28 08:10:02 +00001353 if ((ssize_t) (offset+number_bytes) < offset)
1354 continue; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001355 if ((size_t) (offset+number_bytes) > length)
1356 continue;
1357 p=(unsigned char *) (exif+offset);
1358 }
cristy90e72a02012-09-29 01:08:57 +00001359 if ((all != 0) || (tag == (size_t) tag_value))
cristy3ed852e2009-09-05 21:47:34 +00001360 {
1361 char
1362 buffer[MaxTextExtent],
1363 *value;
1364
cristy7ad8a132012-03-28 22:42:29 +00001365 value=(char *) NULL;
1366 *buffer='\0';
cristy3ed852e2009-09-05 21:47:34 +00001367 switch (format)
1368 {
1369 case EXIF_FMT_BYTE:
1370 case EXIF_FMT_UNDEFINED:
1371 {
cristy13adad02011-08-16 19:22:15 +00001372 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001373 break;
1374 }
1375 case EXIF_FMT_SBYTE:
1376 {
cristye8c25f92010-06-03 00:53:06 +00001377 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001378 break;
1379 }
1380 case EXIF_FMT_SSHORT:
1381 {
1382 EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
1383 break;
1384 }
1385 case EXIF_FMT_USHORT:
1386 {
1387 EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
1388 break;
1389 }
1390 case EXIF_FMT_ULONG:
1391 {
cristye8c25f92010-06-03 00:53:06 +00001392 EXIFMultipleValues(4,"%.20g",(double)
cristy4a8b0172012-02-03 16:39:53 +00001393 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001394 break;
1395 }
1396 case EXIF_FMT_SLONG:
1397 {
cristye8c25f92010-06-03 00:53:06 +00001398 EXIFMultipleValues(4,"%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001399 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001400 break;
1401 }
1402 case EXIF_FMT_URATIONAL:
1403 {
cristye8c25f92010-06-03 00:53:06 +00001404 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristyf0696672012-02-01 23:43:06 +00001405 ((int) ReadPropertyLong(endian,p1)),(double)
1406 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001407 break;
1408 }
1409 case EXIF_FMT_SRATIONAL:
1410 {
cristye8c25f92010-06-03 00:53:06 +00001411 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001412 ((int) ReadPropertyLong(endian,p1)),(double)
1413 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001414 break;
1415 }
1416 case EXIF_FMT_SINGLE:
1417 {
1418 EXIFMultipleValues(4,"%f",(double) *(float *) p1);
1419 break;
1420 }
1421 case EXIF_FMT_DOUBLE:
1422 {
1423 EXIFMultipleValues(8,"%f",*(double *) p1);
1424 break;
1425 }
1426 default:
1427 case EXIF_FMT_STRING:
1428 {
1429 value=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +00001430 if (~((size_t) number_bytes) >= 1)
cristy3ed852e2009-09-05 21:47:34 +00001431 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1432 sizeof(*value));
1433 if (value != (char *) NULL)
1434 {
cristybb503372010-05-27 20:51:26 +00001435 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001436 i;
1437
cristybb503372010-05-27 20:51:26 +00001438 for (i=0; i < (ssize_t) number_bytes; i++)
cristy3ed852e2009-09-05 21:47:34 +00001439 {
1440 value[i]='.';
1441 if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1442 value[i]=(char) p[i];
1443 }
1444 value[i]='\0';
1445 }
1446 break;
1447 }
1448 }
1449 if (value != (char *) NULL)
1450 {
1451 char
cristy6e617e62012-09-29 14:12:23 +00001452 *key;
cristy3ed852e2009-09-05 21:47:34 +00001453
1454 register const char
1455 *p;
1456
cristy6e617e62012-09-29 14:12:23 +00001457 key=AcquireString(property);
1458 if (level == 2)
1459 (void) SubstituteString(&key,"exif:","exif:thumbnail:");
cristy3ed852e2009-09-05 21:47:34 +00001460 switch (all)
1461 {
1462 case 1:
1463 {
1464 const char
1465 *description;
1466
cristybb503372010-05-27 20:51:26 +00001467 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001468 i;
1469
1470 description="unknown";
1471 for (i=0; ; i++)
1472 {
1473 if (EXIFTag[i].tag == 0)
1474 break;
cristybb503372010-05-27 20:51:26 +00001475 if ((ssize_t) EXIFTag[i].tag == tag_value)
cristy3ed852e2009-09-05 21:47:34 +00001476 {
1477 description=EXIFTag[i].description;
1478 break;
1479 }
1480 }
cristy96ea4862011-11-22 00:45:47 +00001481 (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
cristy3ed852e2009-09-05 21:47:34 +00001482 break;
1483 }
1484 case 2:
1485 {
1486 if (tag_value < 0x10000)
cristyb51dff52011-05-19 16:55:47 +00001487 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001488 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001489 else
1490 if (tag_value < 0x20000)
cristyb51dff52011-05-19 16:55:47 +00001491 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001492 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001493 else
cristyb51dff52011-05-19 16:55:47 +00001494 (void) FormatLocaleString(key,MaxTextExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001495 break;
1496 }
1497 }
1498 p=(const char *) NULL;
1499 if (image->properties != (void *) NULL)
1500 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1501 image->properties,key);
1502 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001503 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001504 value=DestroyString(value);
cristy6e617e62012-09-29 14:12:23 +00001505 key=DestroyString(key);
cristy929ea322011-02-21 15:21:35 +00001506 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001507 }
1508 }
1509 if ((tag_value == TAG_EXIF_OFFSET) ||
cristy4a8b0172012-02-03 16:39:53 +00001510 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
cristy3ed852e2009-09-05 21:47:34 +00001511 {
cristy3d8d1f12012-02-29 01:59:28 +00001512 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001513 offset;
1514
cristy3d8d1f12012-02-29 01:59:28 +00001515 offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
1516 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00001517 {
cristy3d8d1f12012-02-29 01:59:28 +00001518 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001519 tag_offset1;
1520
cristy3d8d1f12012-02-29 01:59:28 +00001521 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1522 0);
cristy3ed852e2009-09-05 21:47:34 +00001523 directory_stack[level].directory=directory;
1524 entry++;
1525 directory_stack[level].entry=entry;
1526 directory_stack[level].offset=tag_offset;
1527 level++;
1528 directory_stack[level].directory=exif+offset;
1529 directory_stack[level].offset=tag_offset1;
1530 directory_stack[level].entry=0;
1531 level++;
1532 if ((directory+2+(12*number_entries)) > (exif+length))
1533 break;
cristy3d8d1f12012-02-29 01:59:28 +00001534 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
cristy4a8b0172012-02-03 16:39:53 +00001535 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00001536 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00001537 (level < (MaxDirectoryStack-2)))
1538 {
1539 directory_stack[level].directory=exif+offset;
1540 directory_stack[level].entry=0;
1541 directory_stack[level].offset=tag_offset1;
1542 level++;
1543 }
1544 }
1545 break;
1546 }
1547 }
cristy142ab012012-02-02 02:26:18 +00001548 } while (level > 0);
1549 exif_resources=DestroySplayTree(exif_resources);
cristy929ea322011-02-21 15:21:35 +00001550 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001551}
1552
cristy13adad02011-08-16 19:22:15 +00001553static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001554{
1555 char
1556 *xmp_profile;
1557
1558 const StringInfo
1559 *profile;
1560
1561 ExceptionInfo
1562 *exception;
1563
1564 MagickBooleanType
1565 status;
1566
1567 register const char
1568 *p;
1569
1570 XMLTreeInfo
1571 *child,
1572 *description,
1573 *node,
1574 *rdf,
1575 *xmp;
1576
1577 profile=GetImageProfile(image,"xmp");
1578 if (profile == (StringInfo *) NULL)
1579 return(MagickFalse);
1580 if ((property == (const char *) NULL) || (*property == '\0'))
1581 return(MagickFalse);
1582 xmp_profile=StringInfoToString(profile);
1583 if (xmp_profile == (char *) NULL)
1584 return(MagickFalse);
1585 for (p=xmp_profile; *p != '\0'; p++)
1586 if ((*p == '<') && (*(p+1) == 'x'))
1587 break;
1588 exception=AcquireExceptionInfo();
1589 xmp=NewXMLTree((char *) p,exception);
1590 xmp_profile=DestroyString(xmp_profile);
1591 exception=DestroyExceptionInfo(exception);
1592 if (xmp == (XMLTreeInfo *) NULL)
1593 return(MagickFalse);
1594 status=MagickFalse;
1595 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1596 if (rdf != (XMLTreeInfo *) NULL)
1597 {
1598 if (image->properties == (void *) NULL)
1599 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1600 RelinquishMagickMemory,RelinquishMagickMemory);
1601 description=GetXMLTreeChild(rdf,"rdf:Description");
1602 while (description != (XMLTreeInfo *) NULL)
1603 {
1604 node=GetXMLTreeChild(description,(const char *) NULL);
1605 while (node != (XMLTreeInfo *) NULL)
1606 {
1607 child=GetXMLTreeChild(node,(const char *) NULL);
1608 if (child == (XMLTreeInfo *) NULL)
1609 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1610 ConstantString(GetXMLTreeTag(node)),
1611 ConstantString(GetXMLTreeContent(node)));
1612 while (child != (XMLTreeInfo *) NULL)
1613 {
1614 if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
1615 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1616 ConstantString(GetXMLTreeTag(child)),
1617 ConstantString(GetXMLTreeContent(child)));
1618 child=GetXMLTreeSibling(child);
1619 }
1620 node=GetXMLTreeSibling(node);
1621 }
1622 description=GetNextXMLTreeTag(description);
1623 }
1624 }
1625 xmp=DestroyXMLTree(xmp);
1626 return(status);
1627}
1628
1629static char *TracePSClippath(const unsigned char *blob,size_t length,
cristyf5d0a1a2012-02-03 12:33:10 +00001630 const size_t magick_unused(columns),const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001631{
1632 char
1633 *path,
1634 *message;
1635
cristy3ed852e2009-09-05 21:47:34 +00001636 MagickBooleanType
1637 in_subpath;
1638
1639 PointInfo
1640 first[3],
1641 last[3],
1642 point[3];
1643
cristybb503372010-05-27 20:51:26 +00001644 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001645 i,
1646 x;
1647
cristy9d314ff2011-03-09 01:30:28 +00001648 ssize_t
1649 knot_count,
1650 selector,
1651 y;
1652
cristy3ed852e2009-09-05 21:47:34 +00001653 path=AcquireString((char *) NULL);
1654 if (path == (char *) NULL)
1655 return((char *) NULL);
1656 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001657 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001658 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001659 (void) FormatLocaleString(message,MaxTextExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001660 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001661 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001662 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001663 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001664 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001665 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001666 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001667 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001668 " /v {currentpoint 6 2 roll curveto} bind def\n");
1669 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001670 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001671 " /y {2 copy curveto} bind def\n");
1672 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001673 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001674 " /z {closepath} bind def\n");
1675 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001676 (void) FormatLocaleString(message,MaxTextExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001677 (void) ConcatenateString(&path,message);
1678 /*
1679 The clipping path format is defined in "Adobe Photoshop File
1680 Formats Specification" version 6.0 downloadable from adobe.com.
1681 */
1682 (void) ResetMagickMemory(point,0,sizeof(point));
1683 (void) ResetMagickMemory(first,0,sizeof(first));
1684 (void) ResetMagickMemory(last,0,sizeof(last));
1685 knot_count=0;
1686 in_subpath=MagickFalse;
1687 while (length > 0)
1688 {
cristy4a8b0172012-02-03 16:39:53 +00001689 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001690 switch (selector)
1691 {
1692 case 0:
1693 case 3:
1694 {
1695 if (knot_count != 0)
1696 {
1697 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001698 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001699 break;
1700 }
1701 /*
1702 Expected subpath length record.
1703 */
cristy4a8b0172012-02-03 16:39:53 +00001704 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001705 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001706 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001707 break;
1708 }
1709 case 1:
1710 case 2:
1711 case 4:
1712 case 5:
1713 {
1714 if (knot_count == 0)
1715 {
1716 /*
1717 Unexpected subpath knot
1718 */
1719 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001720 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001721 break;
1722 }
1723 /*
1724 Add sub-path knot
1725 */
1726 for (i=0; i < 3; i++)
1727 {
cristy13b9a2e2010-11-10 14:03:51 +00001728 size_t
cristy97433202009-10-27 02:05:08 +00001729 xx,
1730 yy;
1731
cristy4a8b0172012-02-03 16:39:53 +00001732 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1733 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001734 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001735 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001736 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001737 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001738 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001739 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001740 point[i].x=(double) x/4096/4096;
1741 point[i].y=1.0-(double) y/4096/4096;
1742 }
anthony2fbb5952012-05-05 12:35:30 +00001743 if( IfMagickFalse(in_subpath) )
cristy3ed852e2009-09-05 21:47:34 +00001744 {
cristyb51dff52011-05-19 16:55:47 +00001745 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001746 point[1].x,point[1].y);
1747 for (i=0; i < 3; i++)
1748 {
1749 first[i]=point[i];
1750 last[i]=point[i];
1751 }
1752 }
1753 else
1754 {
1755 /*
1756 Handle special cases when Bezier curves are used to describe
1757 corners and straight lines.
1758 */
1759 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1760 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001761 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001762 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001763 else
1764 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001765 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001766 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001767 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001768 else
1769 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001770 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001771 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001772 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001773 else
cristyb51dff52011-05-19 16:55:47 +00001774 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001775 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001776 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001777 for (i=0; i < 3; i++)
1778 last[i]=point[i];
1779 }
1780 (void) ConcatenateString(&path,message);
1781 in_subpath=MagickTrue;
1782 knot_count--;
1783 /*
1784 Close the subpath if there are no more knots.
1785 */
1786 if (knot_count == 0)
1787 {
1788 /*
1789 Same special handling as above except we compare to the
1790 first point in the path and close the path.
1791 */
1792 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1793 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001794 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001795 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001796 else
1797 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001798 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001799 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001800 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001801 else
1802 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001803 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001804 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001805 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001806 else
cristyb51dff52011-05-19 16:55:47 +00001807 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001808 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001809 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001810 (void) ConcatenateString(&path,message);
1811 in_subpath=MagickFalse;
1812 }
1813 break;
1814 }
1815 case 6:
1816 case 7:
1817 case 8:
1818 default:
1819 {
1820 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001821 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001822 break;
1823 }
1824 }
1825 }
1826 /*
1827 Returns an empty PS path if the path has no knots.
1828 */
cristyb51dff52011-05-19 16:55:47 +00001829 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001830 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001831 (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001832 (void) ConcatenateString(&path,message);
1833 message=DestroyString(message);
1834 return(path);
1835}
1836
1837static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001838 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001839{
1840 char
1841 *path,
1842 *message;
1843
cristy3ed852e2009-09-05 21:47:34 +00001844 MagickBooleanType
1845 in_subpath;
1846
1847 PointInfo
1848 first[3],
1849 last[3],
1850 point[3];
1851
cristybb503372010-05-27 20:51:26 +00001852 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001853 i;
1854
cristy9d314ff2011-03-09 01:30:28 +00001855 ssize_t
1856 knot_count,
1857 selector,
1858 x,
1859 y;
1860
cristy3ed852e2009-09-05 21:47:34 +00001861 path=AcquireString((char *) NULL);
1862 if (path == (char *) NULL)
1863 return((char *) NULL);
1864 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001865 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001866 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
1867 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001868 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001869 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001870 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001871 (void) FormatLocaleString(message,MaxTextExtent,"<g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001872 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001873 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001874 "<path style=\"fill:#00000000;stroke:#00000000;");
1875 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001876 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001877 "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
1878 (void) ConcatenateString(&path,message);
1879 (void) ResetMagickMemory(point,0,sizeof(point));
1880 (void) ResetMagickMemory(first,0,sizeof(first));
1881 (void) ResetMagickMemory(last,0,sizeof(last));
1882 knot_count=0;
1883 in_subpath=MagickFalse;
1884 while (length != 0)
1885 {
cristy4a8b0172012-02-03 16:39:53 +00001886 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001887 switch (selector)
1888 {
1889 case 0:
1890 case 3:
1891 {
1892 if (knot_count != 0)
1893 {
1894 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001895 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001896 break;
1897 }
1898 /*
1899 Expected subpath length record.
1900 */
cristy4a8b0172012-02-03 16:39:53 +00001901 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001902 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001903 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001904 break;
1905 }
1906 case 1:
1907 case 2:
1908 case 4:
1909 case 5:
1910 {
1911 if (knot_count == 0)
1912 {
1913 /*
1914 Unexpected subpath knot.
1915 */
1916 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001917 length-=MagickMin(24,(ssize_t) length);
cristy97433202009-10-27 02:05:08 +00001918 break;
1919 }
1920 /*
1921 Add sub-path knot
1922 */
1923 for (i=0; i < 3; i++)
1924 {
cristyd4982b42011-03-21 14:24:23 +00001925 size_t
cristy97433202009-10-27 02:05:08 +00001926 xx,
1927 yy;
1928
cristy4a8b0172012-02-03 16:39:53 +00001929 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1930 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001931 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001932 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001933 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001934 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001935 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001936 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00001937 point[i].x=(double) x*columns/4096/4096;
1938 point[i].y=(double) y*rows/4096/4096;
1939 }
anthony2fbb5952012-05-05 12:35:30 +00001940 if( IfMagickFalse(in_subpath) )
cristy97433202009-10-27 02:05:08 +00001941 {
dirk8eef4032013-11-13 20:11:20 +00001942 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy97433202009-10-27 02:05:08 +00001943 point[1].x,point[1].y);
1944 for (i=0; i < 3; i++)
1945 {
1946 first[i]=point[i];
1947 last[i]=point[i];
1948 }
cristy3ed852e2009-09-05 21:47:34 +00001949 }
1950 else
1951 {
cristy9eb18472013-11-11 14:55:48 +00001952 /*
1953 Handle special cases when Bezier curves are used to describe
1954 corners and straight lines.
1955 */
cristy97433202009-10-27 02:05:08 +00001956 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1957 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001958 (void) FormatLocaleString(message,MaxTextExtent,
cristy9eb18472013-11-11 14:55:48 +00001959 " %g %g l\n",point[1].x,point[1].y);
1960 else
1961 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
1962 (void) FormatLocaleString(message,MaxTextExtent,
1963 " %g %g %g %g v\n",point[0].x,point[0].y,
1964 point[1].x,point[1].y);
1965 else
1966 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
1967 (void) FormatLocaleString(message,MaxTextExtent,
1968 " %g %g %g %g y\n",last[2].x,last[2].y,
1969 point[1].x,point[1].y);
1970 else
1971 (void) FormatLocaleString(message,MaxTextExtent,
1972 " %g %g %g %g %g %g c\n",last[2].x,
1973 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001974 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00001975 last[i]=point[i];
1976 }
1977 (void) ConcatenateString(&path,message);
1978 in_subpath=MagickTrue;
1979 knot_count--;
1980 /*
1981 Close the subpath if there are no more knots.
1982 */
1983 if (knot_count == 0)
1984 {
cristy9eb18472013-11-11 14:55:48 +00001985 /*
1986 Same special handling as above except we compare to the
1987 first point in the path and close the path.
1988 */
cristy97433202009-10-27 02:05:08 +00001989 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1990 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001991 (void) FormatLocaleString(message,MaxTextExtent,
cristy9eb18472013-11-11 14:55:48 +00001992 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001993 else
cristy9eb18472013-11-11 14:55:48 +00001994 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001995 (void) FormatLocaleString(message,MaxTextExtent,
cristy9eb18472013-11-11 14:55:48 +00001996 " %g %g %g %g v z\n",first[0].x,first[0].y,
1997 first[1].x,first[1].y);
1998 else
1999 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
2000 (void) FormatLocaleString(message,MaxTextExtent,
2001 " %g %g %g %g y z\n",last[2].x,last[2].y,
2002 first[1].x,first[1].y);
2003 else
2004 (void) FormatLocaleString(message,MaxTextExtent,
2005 " %g %g %g %g %g %g c z\n",last[2].x,
2006 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
2007 (void) ConcatenateString(&path,message);
cristy97433202009-10-27 02:05:08 +00002008 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002009 }
cristy97433202009-10-27 02:05:08 +00002010 break;
cristy3ed852e2009-09-05 21:47:34 +00002011 }
2012 case 6:
2013 case 7:
2014 case 8:
2015 default:
2016 {
2017 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00002018 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00002019 break;
2020 }
2021 }
2022 }
2023 /*
2024 Return an empty SVG image if the path does not have knots.
2025 */
cristyb51dff52011-05-19 16:55:47 +00002026 (void) FormatLocaleString(message,MaxTextExtent,"\"/>\n");
cristy3ed852e2009-09-05 21:47:34 +00002027 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00002028 (void) FormatLocaleString(message,MaxTextExtent,"</g>\n");
cristy3ed852e2009-09-05 21:47:34 +00002029 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00002030 (void) FormatLocaleString(message,MaxTextExtent,"</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00002031 (void) ConcatenateString(&path,message);
2032 message=DestroyString(message);
2033 return(path);
2034}
2035
2036MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00002037 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002038{
cristy3ed852e2009-09-05 21:47:34 +00002039 register const char
2040 *p;
2041
2042 assert(image != (Image *) NULL);
2043 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00002044 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00002045 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonya322a832013-04-27 06:28:03 +00002046
cristy3ed852e2009-09-05 21:47:34 +00002047 p=(const char *) NULL;
anthonydb2a1d62013-04-29 01:34:58 +00002048 /* if property is in splay tree - return it and we are done */
cristy27f7af22010-06-21 12:23:21 +00002049 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002050 {
cristy5eb9fc82011-02-21 15:05:41 +00002051 if (property == (const char *) NULL)
2052 {
2053 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
2054 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
2055 image->properties);
2056 return(p);
2057 }
anthonya322a832013-04-27 06:28:03 +00002058 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2059 image->properties,property);
2060 if (p != (const char *) NULL)
2061 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002062 }
cristy5eb9fc82011-02-21 15:05:41 +00002063 if ((property == (const char *) NULL) ||
2064 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002065 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002066 switch (*property)
2067 {
2068 case '8':
2069 {
2070 if (LocaleNCompare("8bim:",property,5) == 0)
2071 {
anthony2fbb5952012-05-05 12:35:30 +00002072 if( IfMagickTrue(Get8BIMProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002073 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002074 {
2075 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2076 image->properties,property);
2077 return(p);
2078 }
2079 }
2080 break;
2081 }
2082 case 'E':
2083 case 'e':
2084 {
2085 if (LocaleNCompare("exif:",property,5) == 0)
2086 {
anthony2fbb5952012-05-05 12:35:30 +00002087 if( IfMagickTrue(GetEXIFProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002088 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002089 {
2090 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2091 image->properties,property);
2092 return(p);
2093 }
2094 }
2095 break;
2096 }
cristy3ed852e2009-09-05 21:47:34 +00002097 case 'I':
2098 case 'i':
2099 {
2100 if (LocaleNCompare("iptc:",property,5) == 0)
2101 {
anthony2fbb5952012-05-05 12:35:30 +00002102 if( IfMagickTrue(GetIPTCProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002103 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002104 {
2105 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2106 image->properties,property);
2107 return(p);
2108 }
2109 }
2110 break;
2111 }
cristy3ed852e2009-09-05 21:47:34 +00002112 case 'X':
2113 case 'x':
2114 {
2115 if (LocaleNCompare("xmp:",property,4) == 0)
2116 {
anthony2fbb5952012-05-05 12:35:30 +00002117 if( IfMagickTrue(GetXMPProperty(image,property)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002118 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002119 {
2120 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2121 image->properties,property);
2122 return(p);
2123 }
2124 }
2125 break;
2126 }
cristy5eb9fc82011-02-21 15:05:41 +00002127 default:
2128 break;
cristy3ed852e2009-09-05 21:47:34 +00002129 }
2130 return(p);
2131}
dirk6015d442013-10-19 15:07:40 +00002132
2133
cristy3ed852e2009-09-05 21:47:34 +00002134/*
2135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2136% %
2137% %
2138% %
2139+ G e t M a g i c k P r o p e r t y %
2140% %
2141% %
2142% %
2143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2144%
anthony06762232012-04-29 11:45:40 +00002145% GetMagickProperty() gets attributes or calculated values that is associated
anthonya322a832013-04-27 06:28:03 +00002146% with a fixed known property name, or single letter property. It may be
anthonydb2a1d62013-04-29 01:34:58 +00002147% called if no image is defined (IMv7), in which case only global image_info
2148% values are available.
anthony06762232012-04-29 11:45:40 +00002149%
anthonydb2a1d62013-04-29 01:34:58 +00002150% This routine only handles specifically known properties. It does not
2151% handle special prefixed properties, profiles, or expressions. Nor does
2152% it return any free-form property strings.
anthony06762232012-04-29 11:45:40 +00002153%
anthonydb2a1d62013-04-29 01:34:58 +00002154% The returned string is stored in a structure somewhere, and should not be
2155% directly freed. If the string was generated (common) the string will be
2156% stored as as either as artifact or option 'get-property'. These may be
2157% deleted (cleaned up) when no longer required, but neither artifact or
2158% option is guranteed to exist.
cristy3ed852e2009-09-05 21:47:34 +00002159%
2160% The format of the GetMagickProperty method is:
2161%
anthonydb2a1d62013-04-29 01:34:58 +00002162% const char *GetMagickProperty(ImageInfo *image_info,Image *image,
cristy97fc9f32012-06-13 21:04:44 +00002163% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002164%
2165% A description of each parameter follows:
2166%
anthonydb2a1d62013-04-29 01:34:58 +00002167% o image_info: the image info (optional)
cristy3ed852e2009-09-05 21:47:34 +00002168%
anthonydb2a1d62013-04-29 01:34:58 +00002169% o image: the image (optional)
cristy3ed852e2009-09-05 21:47:34 +00002170%
2171% o key: the key.
2172%
cristyd15e6592011-10-15 00:13:06 +00002173% o exception: return any errors or warnings in this structure.
2174%
cristy3ed852e2009-09-05 21:47:34 +00002175*/
anthonya322a832013-04-27 06:28:03 +00002176#define WarnNoImageReturn(format,arg) \
2177 if (image == (Image *) NULL ) { \
2178 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2179 "NoImageForProperty",format,arg); \
2180 return((const char *)NULL); \
2181 }
anthonydb2a1d62013-04-29 01:34:58 +00002182#define WarnNoImageInfoReturn(format,arg) \
2183 if (image == (Image *) NULL ) { \
2184 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2185 "NoImageInfoForProperty",format,arg); \
2186 return((const char *)NULL); \
2187 }
anthonya322a832013-04-27 06:28:03 +00002188
anthonydb2a1d62013-04-29 01:34:58 +00002189static const char *GetMagickPropertyLetter(ImageInfo *image_info,
anthony2fbb5952012-05-05 12:35:30 +00002190 Image *image,const char letter,ExceptionInfo *exception)
2191{
2192 char
anthonya322a832013-04-27 06:28:03 +00002193 value[MaxTextExtent]; /* formated string to store as a returned artifact */
anthony2fbb5952012-05-05 12:35:30 +00002194
anthony2cfa1a12012-05-12 05:18:07 +00002195 const char
anthonya322a832013-04-27 06:28:03 +00002196 *string; /* return a string already stored somewher */
anthony2cfa1a12012-05-12 05:18:07 +00002197
2198 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002199 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonydb2a1d62013-04-29 01:34:58 +00002200 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2201 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
2202
anthonya322a832013-04-27 06:28:03 +00002203 *value='\0'; /* formatted string */
cristy68064d72012-06-13 20:56:14 +00002204 string=(char *) NULL; /* constant string reference */
anthonya322a832013-04-27 06:28:03 +00002205
2206 /* Get properities that are directly defined by images */
anthony2fbb5952012-05-05 12:35:30 +00002207 switch (letter)
2208 {
anthony2fbb5952012-05-05 12:35:30 +00002209 case 'b': /* image size read in - in bytes */
2210 {
anthonya322a832013-04-27 06:28:03 +00002211 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002212 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2213 ((MagickOffsetType) image->extent));
2214 if (image->extent != (MagickSizeType) ((size_t) image->extent))
2215 (void) FormatMagickSize(image->extent,MagickFalse,value);
2216 ConcatenateMagickString(value,"B",MaxTextExtent);
2217 break;
2218 }
anthony7bb7aee2012-05-15 00:09:16 +00002219 case 'c': /* image comment property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002220 {
anthonya322a832013-04-27 06:28:03 +00002221 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002222 string=GetImageProperty(image,"comment",exception);
anthonya322a832013-04-27 06:28:03 +00002223 if ( string == (const char *) NULL )
anthony7bb7aee2012-05-15 00:09:16 +00002224 string="";
anthony2fbb5952012-05-05 12:35:30 +00002225 break;
2226 }
2227 case 'd': /* Directory component of filename */
2228 {
anthonya322a832013-04-27 06:28:03 +00002229 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002230 GetPathComponent(image->magick_filename,HeadPath,value);
anthonya322a832013-04-27 06:28:03 +00002231 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002232 break;
2233 }
2234 case 'e': /* Filename extension (suffix) of image file */
2235 {
anthonya322a832013-04-27 06:28:03 +00002236 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002237 GetPathComponent(image->magick_filename,ExtensionPath,value);
anthonya322a832013-04-27 06:28:03 +00002238 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002239 break;
2240 }
2241 case 'f': /* Filename without directory component */
2242 {
anthonya322a832013-04-27 06:28:03 +00002243 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002244 GetPathComponent(image->magick_filename,TailPath,value);
anthonya322a832013-04-27 06:28:03 +00002245 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002246 break;
2247 }
2248 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */
2249 {
anthonya322a832013-04-27 06:28:03 +00002250 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002251 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002252 (double) image->page.width,(double) image->page.height,
2253 (double) image->page.x,(double) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002254 break;
2255 }
2256 case 'h': /* Image height (current) */
2257 {
anthonya322a832013-04-27 06:28:03 +00002258 WarnNoImageReturn("\"%%%c\"",letter);
cristyec6934f2012-08-14 18:38:40 +00002259 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2260 (image->rows != 0 ? image->rows : image->magick_rows));
anthony2fbb5952012-05-05 12:35:30 +00002261 break;
2262 }
anthonya322a832013-04-27 06:28:03 +00002263 case 'i': /* Filename last used for an image (read or write) */
anthony2fbb5952012-05-05 12:35:30 +00002264 {
anthonya322a832013-04-27 06:28:03 +00002265 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002266 string=image->filename;
anthony2fbb5952012-05-05 12:35:30 +00002267 break;
2268 }
2269 case 'k': /* Number of unique colors */
2270 {
cristyec6934f2012-08-14 18:38:40 +00002271 /*
2272 FUTURE: ensure this does not generate the formatted comment!
2273 */
anthonya322a832013-04-27 06:28:03 +00002274 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002275 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002276 GetNumberColors(image,(FILE *) NULL,exception));
anthony2fbb5952012-05-05 12:35:30 +00002277 break;
2278 }
cristy97fc9f32012-06-13 21:04:44 +00002279 case 'l': /* Image label property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002280 {
anthonya322a832013-04-27 06:28:03 +00002281 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002282 string=GetImageProperty(image,"label",exception);
anthonya322a832013-04-27 06:28:03 +00002283 if ( string == (const char *) NULL)
anthony7bb7aee2012-05-15 00:09:16 +00002284 string="";
anthony2fbb5952012-05-05 12:35:30 +00002285 break;
2286 }
2287 case 'm': /* Image format (file magick) */
2288 {
anthonya322a832013-04-27 06:28:03 +00002289 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002290 string=image->magick;
anthony2fbb5952012-05-05 12:35:30 +00002291 break;
2292 }
2293 case 'n': /* Number of images in the list. */
2294 {
anthonya322a832013-04-27 06:28:03 +00002295 if ( image != (Image *) NULL )
2296 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2297 GetImageListLength(image));
2298 else
2299 string="0"; /* no images or scenes */
anthony2fbb5952012-05-05 12:35:30 +00002300 break;
2301 }
2302 case 'o': /* Output Filename - for delegate use only */
anthonydb2a1d62013-04-29 01:34:58 +00002303 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002304 string=image_info->filename;
anthony2fbb5952012-05-05 12:35:30 +00002305 break;
anthonya322a832013-04-27 06:28:03 +00002306 case 'p': /* Image index in current image list */
anthony2fbb5952012-05-05 12:35:30 +00002307 {
anthonya322a832013-04-27 06:28:03 +00002308 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002309 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002310 GetImageIndexInList(image));
anthony2fbb5952012-05-05 12:35:30 +00002311 break;
2312 }
2313 case 'q': /* Quantum depth of image in memory */
2314 {
anthonya322a832013-04-27 06:28:03 +00002315 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002316 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2317 MAGICKCORE_QUANTUM_DEPTH);
2318 break;
2319 }
anthonya322a832013-04-27 06:28:03 +00002320 case 'r': /* Image storage class, colorspace, and alpha enabled. */
anthony2fbb5952012-05-05 12:35:30 +00002321 {
2322 ColorspaceType
2323 colorspace;
2324
anthonya322a832013-04-27 06:28:03 +00002325 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002326 colorspace=image->colorspace;
2327 if (IfMagickTrue(IsImageGray(image,exception)))
anthonya322a832013-04-27 06:28:03 +00002328 colorspace=GRAYColorspace; /* FUTURE: this is IMv6 not IMv7 */
anthony2fbb5952012-05-05 12:35:30 +00002329 (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s",
cristy68064d72012-06-13 20:56:14 +00002330 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
anthony2fbb5952012-05-05 12:35:30 +00002331 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
anthonya322a832013-04-27 06:28:03 +00002332 image->alpha_trait == BlendPixelTrait ? "Alpha" : "");
anthony2fbb5952012-05-05 12:35:30 +00002333 break;
2334 }
2335 case 's': /* Image scene number */
2336 {
anthonya322a832013-04-27 06:28:03 +00002337#if 0 /* this seems non-sensical -- simplifing */
anthony2fbb5952012-05-05 12:35:30 +00002338 if (image_info->number_scenes != 0)
2339 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002340 image_info->scene);
anthonya322a832013-04-27 06:28:03 +00002341 else if (image != (Image *)NULL)
anthony2fbb5952012-05-05 12:35:30 +00002342 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002343 image->scene);
anthonya322a832013-04-27 06:28:03 +00002344 else
2345 string="0";
2346#else
2347 WarnNoImageReturn("\"%%%c\"",letter);
2348 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2349 image->scene);
2350#endif
anthony2fbb5952012-05-05 12:35:30 +00002351 break;
2352 }
2353 case 't': /* Base filename without directory or extention */
2354 {
anthonya322a832013-04-27 06:28:03 +00002355 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002356 GetPathComponent(image->magick_filename,BasePath,value);
anthonya322a832013-04-27 06:28:03 +00002357 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002358 break;
2359 }
2360 case 'u': /* Unique filename */
anthonydb2a1d62013-04-29 01:34:58 +00002361 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002362 string=image_info->unique;
anthony2fbb5952012-05-05 12:35:30 +00002363 break;
anthony2fbb5952012-05-05 12:35:30 +00002364 case 'w': /* Image width (current) */
2365 {
anthonya322a832013-04-27 06:28:03 +00002366 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002367 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002368 (image->columns != 0 ? image->columns : image->magick_columns));
anthony2fbb5952012-05-05 12:35:30 +00002369 break;
2370 }
anthonyc69008c2012-05-07 11:45:32 +00002371 case 'x': /* Image horizontal resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002372 {
anthonya322a832013-04-27 06:28:03 +00002373 WarnNoImageReturn("\"%%%c\"",letter);
cristy66a5ca22013-08-01 17:12:57 +00002374 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002375 fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0);
anthony2fbb5952012-05-05 12:35:30 +00002376 break;
2377 }
anthonyc69008c2012-05-07 11:45:32 +00002378 case 'y': /* Image vertical resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002379 {
anthonya322a832013-04-27 06:28:03 +00002380 WarnNoImageReturn("\"%%%c\"",letter);
cristy66a5ca22013-08-01 17:12:57 +00002381 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002382 fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0);
anthony2fbb5952012-05-05 12:35:30 +00002383 break;
2384 }
2385 case 'z': /* Image depth as read in */
2386 {
anthonya322a832013-04-27 06:28:03 +00002387 WarnNoImageReturn("\"%%%c\"",letter);
2388 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002389 (double) image->depth);
anthony2fbb5952012-05-05 12:35:30 +00002390 break;
2391 }
2392 case 'A': /* Image alpha channel */
2393 {
anthonya322a832013-04-27 06:28:03 +00002394 WarnNoImageReturn("\"%%%c\"",letter);
2395 string=CommandOptionToMnemonic(MagickBooleanOptions,
cristyed8d7852013-08-01 22:44:22 +00002396 (ssize_t) image->alpha_trait);
anthony2fbb5952012-05-05 12:35:30 +00002397 break;
2398 }
2399 case 'C': /* Image compression method. */
2400 {
anthonya322a832013-04-27 06:28:03 +00002401 WarnNoImageReturn("\"%%%c\"",letter);
2402 string=CommandOptionToMnemonic(MagickCompressOptions,
cristyed8d7852013-08-01 22:44:22 +00002403 (ssize_t) image->compression);
anthony2fbb5952012-05-05 12:35:30 +00002404 break;
2405 }
2406 case 'D': /* Image dispose method. */
2407 {
anthonya322a832013-04-27 06:28:03 +00002408 WarnNoImageReturn("\"%%%c\"",letter);
2409 string=CommandOptionToMnemonic(MagickDisposeOptions,
cristyed8d7852013-08-01 22:44:22 +00002410 (ssize_t) image->dispose);
anthony2fbb5952012-05-05 12:35:30 +00002411 break;
2412 }
2413 case 'G': /* Image size as geometry = "%wx%h" */
2414 {
anthonya322a832013-04-27 06:28:03 +00002415 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002416 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002417 (double)image->magick_columns,(double) image->magick_rows);
anthony2fbb5952012-05-05 12:35:30 +00002418 break;
2419 }
2420 case 'H': /* layer canvas height */
2421 {
anthonya322a832013-04-27 06:28:03 +00002422 WarnNoImageReturn("\"%%%c\"",letter);
2423 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002424 (double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002425 break;
2426 }
2427 case 'M': /* Magick filename - filename given incl. coder & read mods */
2428 {
anthonya322a832013-04-27 06:28:03 +00002429 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002430 string=image->magick_filename;
anthony2fbb5952012-05-05 12:35:30 +00002431 break;
2432 }
2433 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2434 {
anthonya322a832013-04-27 06:28:03 +00002435 WarnNoImageReturn("\"%%%c\"",letter);
cristy68064d72012-06-13 20:56:14 +00002436 (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long)
2437 image->page.x,(long) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002438 break;
2439 }
2440 case 'P': /* layer canvas page size = "%Wx%H" */
2441 {
anthonya322a832013-04-27 06:28:03 +00002442 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002443 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002444 (double) image->page.width,(double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002445 break;
2446 }
2447 case 'Q': /* image compression quality */
2448 {
anthonya322a832013-04-27 06:28:03 +00002449 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002450 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyd46a07e2013-08-03 23:07:04 +00002451 (image->quality == 0 ? 92 : image->quality));
anthony2fbb5952012-05-05 12:35:30 +00002452 break;
2453 }
anthonya322a832013-04-27 06:28:03 +00002454 case 'S': /* Number of scenes in image list. */
anthony2fbb5952012-05-05 12:35:30 +00002455 {
anthonydb2a1d62013-04-29 01:34:58 +00002456 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthonya322a832013-04-27 06:28:03 +00002457#if 0 /* What is this number? -- it makes no sense - simplifing */
anthony2fbb5952012-05-05 12:35:30 +00002458 if (image_info->number_scenes == 0)
anthonya322a832013-04-27 06:28:03 +00002459 string="2147483647";
2460 else if ( image != (Image *) NULL )
anthony2fbb5952012-05-05 12:35:30 +00002461 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
anthonya322a832013-04-27 06:28:03 +00002462 image_info->scene+image_info->number_scenes);
anthonydb2a1d62013-04-29 01:34:58 +00002463 else
anthonya322a832013-04-27 06:28:03 +00002464 string="0";
2465#else
2466 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyab5fbd82013-04-28 17:08:53 +00002467 (image_info->number_scenes == 0 ? 2147483647 :
2468 image_info->number_scenes));
anthonya322a832013-04-27 06:28:03 +00002469#endif
anthony2fbb5952012-05-05 12:35:30 +00002470 break;
2471 }
2472 case 'T': /* image time delay for animations */
2473 {
anthonya322a832013-04-27 06:28:03 +00002474 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002475 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyed8d7852013-08-01 22:44:22 +00002476 image->delay);
anthony2fbb5952012-05-05 12:35:30 +00002477 break;
2478 }
cristye9f6b9e2013-10-27 22:04:52 +00002479 case 'U': /* Image resolution units. */
2480 {
2481 WarnNoImageReturn("\"%%%c\"",letter);
2482 string=CommandOptionToMnemonic(MagickResolutionOptions,
2483 (ssize_t) image->units);
2484 break;
2485 }
anthony2fbb5952012-05-05 12:35:30 +00002486 case 'W': /* layer canvas width */
2487 {
anthonya322a832013-04-27 06:28:03 +00002488 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002489 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002490 image->page.width);
anthony2fbb5952012-05-05 12:35:30 +00002491 break;
2492 }
2493 case 'X': /* layer canvas X offset */
2494 {
anthonya322a832013-04-27 06:28:03 +00002495 WarnNoImageReturn("\"%%%c\"",letter);
cristy68064d72012-06-13 20:56:14 +00002496 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2497 image->page.x);
anthony2fbb5952012-05-05 12:35:30 +00002498 break;
2499 }
2500 case 'Y': /* layer canvas Y offset */
2501 {
anthonya322a832013-04-27 06:28:03 +00002502 WarnNoImageReturn("\"%%%c\"",letter);
cristy68064d72012-06-13 20:56:14 +00002503 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2504 image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002505 break;
2506 }
anthony6e2e0732012-05-06 12:22:43 +00002507 case 'Z': /* Zero filename ??? */
anthonydb2a1d62013-04-29 01:34:58 +00002508 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002509 string=image_info->zero;
anthony2fbb5952012-05-05 12:35:30 +00002510 break;
anthonya322a832013-04-27 06:28:03 +00002511 case '%': /* percent escaped */
2512 string="%";
2513 break;
2514 case '@': /* Trim bounding box, without actually Trimming! */
anthony2fbb5952012-05-05 12:35:30 +00002515 {
2516 RectangleInfo
2517 page;
2518
anthonya322a832013-04-27 06:28:03 +00002519 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002520 page=GetImageBoundingBox(image,exception);
2521 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002522 (double) page.width,(double) page.height,(double) page.x,(double)
2523 page.y);
anthony2fbb5952012-05-05 12:35:30 +00002524 break;
2525 }
2526 case '#': /* Image signature */
2527 {
anthonya322a832013-04-27 06:28:03 +00002528 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002529 (void) SignatureImage(image,exception);
anthony2cfa1a12012-05-12 05:18:07 +00002530 string=GetImageProperty(image,"signature",exception);
anthony2fbb5952012-05-05 12:35:30 +00002531 break;
2532 }
anthony2fbb5952012-05-05 12:35:30 +00002533 }
cristy68064d72012-06-13 20:56:14 +00002534 if (string != (char *) NULL)
anthonya322a832013-04-27 06:28:03 +00002535 return(string);
2536 if (*value != '\0')
anthonydb2a1d62013-04-29 01:34:58 +00002537 {
2538 /* create a cloned copy of result, that will get cleaned up, eventually */
2539 if (image != (Image *)NULL)
2540 {
2541 (void) SetImageArtifact(image,"get-property",value);
2542 return(GetImageArtifact(image,"get-property"));
2543 }
2544 else
2545 {
2546 (void) SetImageOption(image_info,"get-property",value);
2547 return(GetImageOption(image_info,"get-property"));
2548 }
2549 }
anthony2cfa1a12012-05-12 05:18:07 +00002550 return((char *)NULL);
anthony2fbb5952012-05-05 12:35:30 +00002551}
2552
anthonydb2a1d62013-04-29 01:34:58 +00002553MagickExport const char *GetMagickProperty(ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002554 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002555{
2556 char
anthony003f8992012-05-11 12:50:07 +00002557 value[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00002558
anthony2cfa1a12012-05-12 05:18:07 +00002559 const char
2560 *string;
2561
anthony2fbb5952012-05-05 12:35:30 +00002562 assert(property[0] != '\0');
anthonydb2a1d62013-04-29 01:34:58 +00002563 assert(image != (Image *)NULL || image_info != (ImageInfo *)NULL );
anthonya322a832013-04-27 06:28:03 +00002564
cristy68064d72012-06-13 20:56:14 +00002565 if (property[1] == '\0') /* single letter property request */
2566 return(GetMagickPropertyLetter(image_info,image,*property,exception));
anthonya322a832013-04-27 06:28:03 +00002567
anthonydb2a1d62013-04-29 01:34:58 +00002568 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002569 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonydb2a1d62013-04-29 01:34:58 +00002570 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2571 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
anthonya322a832013-04-27 06:28:03 +00002572
2573 *value='\0'; /* formated string */
cristy68064d72012-06-13 20:56:14 +00002574 string=(char *) NULL; /* constant string reference */
cristyec6897c2010-05-12 00:43:15 +00002575 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002576 {
2577 case 'b':
2578 {
anthony2cfa1a12012-05-12 05:18:07 +00002579 if ((LocaleCompare("base",property) == 0) ||
2580 (LocaleCompare("basename",property) == 0) )
cristy3ed852e2009-09-05 21:47:34 +00002581 {
anthonya322a832013-04-27 06:28:03 +00002582 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002583 GetPathComponent(image->magick_filename,BasePath,value);
anthonya322a832013-04-27 06:28:03 +00002584 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002585 break;
2586 }
dirk2a08b062013-10-19 19:26:02 +00002587 if (LocaleCompare("bit-depth",property) == 0)
2588 {
2589 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2590 GetImageDepth(image, exception));
2591 break;
2592 }
cristy3ed852e2009-09-05 21:47:34 +00002593 break;
2594 }
2595 case 'c':
2596 {
anthony2cfa1a12012-05-12 05:18:07 +00002597 if (LocaleCompare("channels",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002598 {
anthonya322a832013-04-27 06:28:03 +00002599 WarnNoImageReturn("\"%%[%s]\"",property);
anthony7bb7aee2012-05-15 00:09:16 +00002600 /* FUTURE: return actual image channels */
cristyb51dff52011-05-19 16:55:47 +00002601 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002602 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002603 image->colorspace));
2604 LocaleLower(value);
cristy8a46d822012-08-28 23:32:39 +00002605 if( image->alpha_trait == BlendPixelTrait )
cristy3ed852e2009-09-05 21:47:34 +00002606 (void) ConcatenateMagickString(value,"a",MaxTextExtent);
2607 break;
2608 }
anthony2cfa1a12012-05-12 05:18:07 +00002609 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002610 {
anthonya322a832013-04-27 06:28:03 +00002611 WarnNoImageReturn("\"%%[%s]\"",property);
anthony7bb7aee2012-05-15 00:09:16 +00002612 /* FUTURE: return actual colorspace - no 'gray' stuff */
anthony2cfa1a12012-05-12 05:18:07 +00002613 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3f168fe2013-06-10 17:33:36 +00002614 image->colorspace);
cristy3ed852e2009-09-05 21:47:34 +00002615 break;
2616 }
anthony2cfa1a12012-05-12 05:18:07 +00002617 if (LocaleCompare("copyright",property) == 0)
cristy63054742010-09-20 12:42:28 +00002618 {
2619 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
2620 break;
2621 }
cristy3ed852e2009-09-05 21:47:34 +00002622 break;
2623 }
2624 case 'd':
2625 {
anthony2cfa1a12012-05-12 05:18:07 +00002626 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002627 {
anthonya322a832013-04-27 06:28:03 +00002628 WarnNoImageReturn("\"%%[%s]\"",property);
cristyb51dff52011-05-19 16:55:47 +00002629 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002630 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002631 break;
2632 }
anthony2cfa1a12012-05-12 05:18:07 +00002633 if (LocaleCompare("directory",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002634 {
anthonya322a832013-04-27 06:28:03 +00002635 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002636 GetPathComponent(image->magick_filename,HeadPath,value);
anthonya322a832013-04-27 06:28:03 +00002637 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002638 break;
2639 }
2640 break;
2641 }
2642 case 'e':
2643 {
anthony2cfa1a12012-05-12 05:18:07 +00002644 if (LocaleCompare("extension",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002645 {
anthonya322a832013-04-27 06:28:03 +00002646 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002647 GetPathComponent(image->magick_filename,ExtensionPath,value);
anthonya322a832013-04-27 06:28:03 +00002648 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002649 break;
2650 }
2651 break;
2652 }
2653 case 'g':
2654 {
anthony2f7f9ca2012-05-14 06:33:53 +00002655 if (LocaleCompare("gamma",property) == 0)
2656 {
anthonya322a832013-04-27 06:28:03 +00002657 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2f7f9ca2012-05-14 06:33:53 +00002658 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
2659 GetMagickPrecision(),image->gamma);
2660 break;
2661 }
anthonydb2a1d62013-04-29 01:34:58 +00002662 if (LocaleCompare("group",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002663 {
anthonydb2a1d62013-04-29 01:34:58 +00002664 WarnNoImageInfoReturn("\"%%[%s]\"",property);
cristy68064d72012-06-13 20:56:14 +00002665 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
2666 image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002667 break;
2668 }
2669 break;
2670 }
2671 case 'h':
2672 {
anthony2cfa1a12012-05-12 05:18:07 +00002673 if (LocaleCompare("height",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002674 {
anthonya322a832013-04-27 06:28:03 +00002675 WarnNoImageReturn("\"%%[%s]\"",property);
cristyb51dff52011-05-19 16:55:47 +00002676 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002677 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002678 break;
2679 }
2680 break;
2681 }
2682 case 'i':
2683 {
anthony2cfa1a12012-05-12 05:18:07 +00002684 if (LocaleCompare("input",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002685 {
anthonya322a832013-04-27 06:28:03 +00002686 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002687 string=image->filename;
cristy3ed852e2009-09-05 21:47:34 +00002688 break;
2689 }
2690 break;
2691 }
2692 case 'k':
2693 {
anthony2cfa1a12012-05-12 05:18:07 +00002694 if (LocaleCompare("kurtosis",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002695 {
2696 double
2697 kurtosis,
2698 skewness;
2699
anthonya322a832013-04-27 06:28:03 +00002700 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002701 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002702 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002703 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002704 break;
2705 }
2706 break;
2707 }
2708 case 'm':
2709 {
anthony2cfa1a12012-05-12 05:18:07 +00002710 if (LocaleCompare("magick",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002711 {
anthonya322a832013-04-27 06:28:03 +00002712 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002713 string=image->magick;
cristy3ed852e2009-09-05 21:47:34 +00002714 break;
2715 }
anthony2cfa1a12012-05-12 05:18:07 +00002716 if (LocaleCompare("max",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002717 {
2718 double
2719 maximum,
2720 minimum;
2721
anthonya322a832013-04-27 06:28:03 +00002722 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002723 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002724 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002725 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002726 break;
2727 }
anthony2cfa1a12012-05-12 05:18:07 +00002728 if (LocaleCompare("mean",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002729 {
2730 double
2731 mean,
2732 standard_deviation;
2733
anthonya322a832013-04-27 06:28:03 +00002734 WarnNoImageReturn("\"%%[%s]\"",property);
cristy68064d72012-06-13 20:56:14 +00002735 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00002736 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002737 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002738 break;
2739 }
anthony2cfa1a12012-05-12 05:18:07 +00002740 if (LocaleCompare("min",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002741 {
2742 double
2743 maximum,
2744 minimum;
2745
anthonya322a832013-04-27 06:28:03 +00002746 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002747 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002748 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002749 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002750 break;
2751 }
2752 break;
2753 }
cristy3ed852e2009-09-05 21:47:34 +00002754 case 'o':
2755 {
anthony2cfa1a12012-05-12 05:18:07 +00002756 if (LocaleCompare("opaque",property) == 0)
cristyb0094252011-03-26 12:59:45 +00002757 {
anthonya322a832013-04-27 06:28:03 +00002758 WarnNoImageReturn("\"%%[%s]\"",property);
2759 string=CommandOptionToMnemonic(MagickBooleanOptions,
2760 (ssize_t) IsImageOpaque(image,exception));
cristyb0094252011-03-26 12:59:45 +00002761 break;
2762 }
anthony2cfa1a12012-05-12 05:18:07 +00002763 if (LocaleCompare("orientation",property) == 0)
cristy42aa06c2012-04-01 14:05:10 +00002764 {
anthonya322a832013-04-27 06:28:03 +00002765 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002766 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
2767 image->orientation);
cristy42aa06c2012-04-01 14:05:10 +00002768 break;
2769 }
anthonydb2a1d62013-04-29 01:34:58 +00002770 if (LocaleCompare("output",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002771 {
anthonydb2a1d62013-04-29 01:34:58 +00002772 WarnNoImageInfoReturn("\"%%[%s]\"",property);
cristy3ed852e2009-09-05 21:47:34 +00002773 (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
2774 break;
2775 }
2776 break;
2777 }
2778 case 'p':
2779 {
dirk6015d442013-10-19 15:07:40 +00002780#if defined(MAGICKCORE_LCMS_DELEGATE)
2781 if (LocaleCompare("profile:icc",property) == 0 ||
2782 LocaleCompare("profile:icm",property) == 0)
2783 {
cristy1a2fb522013-11-20 14:37:58 +00002784#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
2785#define cmsUInt32Number DWORD
2786#endif
2787
dirk6015d442013-10-19 15:07:40 +00002788 const StringInfo
2789 *profile;
2790
2791 cmsHPROFILE
2792 icc_profile;
2793
2794 profile=GetImageProfile(image,property+8);
2795 if (profile == (StringInfo *) NULL)
2796 break;
2797
2798 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
2799 (cmsUInt32Number) GetStringInfoLength(profile));
2800 if (icc_profile != (cmsHPROFILE *) NULL)
2801 {
2802#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
2803 string=cmsTakeProductName(icc_profile);
2804#else
2805 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
2806 "en","US",value,MaxTextExtent);
2807#endif
2808 (void) cmsCloseProfile(icc_profile);
2809 }
2810 }
2811#endif
2812 if (LocaleCompare("profiles",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002813 {
cristy5954e3c2013-10-18 23:23:44 +00002814 const char
2815 *name;
2816
2817 ResetImageProfileIterator(image);
2818 name=GetNextImageProfile(image);
dirk6015d442013-10-19 15:07:40 +00002819 if (name != (char *) NULL)
2820 {
2821 (void) CopyMagickString(value,name,MaxTextExtent);
2822 name=GetNextImageProfile(image);
2823 while (name != (char *) NULL)
2824 {
2825 ConcatenateMagickString(value,",",MaxTextExtent);
2826 ConcatenateMagickString(value,name,MaxTextExtent);
2827 name=GetNextImageProfile(image);
2828 }
2829 }
cristy3ed852e2009-09-05 21:47:34 +00002830 break;
2831 }
2832 break;
2833 }
cristy946b1032012-04-01 14:19:07 +00002834 case 'r':
2835 {
anthony2cfa1a12012-05-12 05:18:07 +00002836 if (LocaleCompare("resolution.x",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002837 {
anthonya322a832013-04-27 06:28:03 +00002838 WarnNoImageReturn("\"%%[%s]\"",property);
cristy946b1032012-04-01 14:19:07 +00002839 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2840 image->resolution.x);
2841 break;
2842 }
anthony2cfa1a12012-05-12 05:18:07 +00002843 if (LocaleCompare("resolution.y",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002844 {
anthonya322a832013-04-27 06:28:03 +00002845 WarnNoImageReturn("\"%%[%s]\"",property);
cristy946b1032012-04-01 14:19:07 +00002846 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2847 image->resolution.y);
2848 break;
2849 }
2850 break;
2851 }
cristy3ed852e2009-09-05 21:47:34 +00002852 case 's':
2853 {
anthony2cfa1a12012-05-12 05:18:07 +00002854 if (LocaleCompare("scene",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002855 {
anthonydb2a1d62013-04-29 01:34:58 +00002856 WarnNoImageInfoReturn("\"%%[%s]\"",property);
2857 if (image_info->number_scenes != 0)
cristyb51dff52011-05-19 16:55:47 +00002858 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002859 image_info->scene);
anthonya322a832013-04-27 06:28:03 +00002860 else {
2861 WarnNoImageReturn("\"%%[%s]\"",property);
anthony90ebc0d2012-04-28 08:10:02 +00002862 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002863 image->scene);
anthonya322a832013-04-27 06:28:03 +00002864 }
cristy3ed852e2009-09-05 21:47:34 +00002865 break;
2866 }
anthony2cfa1a12012-05-12 05:18:07 +00002867 if (LocaleCompare("scenes",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002868 {
anthonydb2a1d62013-04-29 01:34:58 +00002869 /* FUTURE: equivelent to %n? */
anthonya322a832013-04-27 06:28:03 +00002870 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00002871 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2872 GetImageListLength(image));
2873 break;
2874 }
anthony2cfa1a12012-05-12 05:18:07 +00002875 if (LocaleCompare("size",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002876 {
2877 char
2878 format[MaxTextExtent];
2879
anthonya322a832013-04-27 06:28:03 +00002880 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00002881 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
2882 (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
2883 break;
2884 }
anthony2cfa1a12012-05-12 05:18:07 +00002885 if (LocaleCompare("skewness",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002886 {
2887 double
2888 kurtosis,
2889 skewness;
2890
anthonya322a832013-04-27 06:28:03 +00002891 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002892 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002893 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002894 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002895 break;
2896 }
dirkdc513ec2013-11-07 21:20:34 +00002897 if ((LocaleCompare("standard-deviation",property) == 0) ||
2898 (LocaleCompare("standard_deviation",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002899 {
2900 double
2901 mean,
2902 standard_deviation;
2903
anthonya322a832013-04-27 06:28:03 +00002904 WarnNoImageReturn("\"%%[%s]\"",property);
cristy14fed162012-09-26 10:13:43 +00002905 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00002906 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002907 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00002908 break;
2909 }
2910 break;
2911 }
cristy623aaec2012-06-21 00:49:08 +00002912 case 't':
2913 {
2914 if (LocaleCompare("type",property) == 0)
2915 {
anthonya322a832013-04-27 06:28:03 +00002916 WarnNoImageReturn("\"%%[%s]\"",property);
cristy623aaec2012-06-21 00:49:08 +00002917 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
2918 GetImageType(image,exception));
2919 break;
2920 }
2921 break;
2922 }
cristy3ed852e2009-09-05 21:47:34 +00002923 case 'u':
2924 {
anthonydb2a1d62013-04-29 01:34:58 +00002925 if (LocaleCompare("unique",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002926 {
anthonydb2a1d62013-04-29 01:34:58 +00002927 WarnNoImageInfoReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002928 string=image_info->unique;
cristy3ed852e2009-09-05 21:47:34 +00002929 break;
2930 }
cristy9af7f5b2013-09-23 18:58:46 +00002931 if (LocaleCompare("units",property) == 0)
2932 {
2933 WarnNoImageReturn("\"%%[%s]\"",property);
2934 string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
2935 image->units);
2936 break;
2937 }
2938 if (LocaleCompare("copyright",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002939 break;
2940 }
cristy63054742010-09-20 12:42:28 +00002941 case 'v':
2942 {
anthony2cfa1a12012-05-12 05:18:07 +00002943 if (LocaleCompare("version",property) == 0)
cristy63054742010-09-20 12:42:28 +00002944 {
anthony2cfa1a12012-05-12 05:18:07 +00002945 string=GetMagickVersion((size_t *) NULL);
cristy63054742010-09-20 12:42:28 +00002946 break;
2947 }
2948 break;
2949 }
cristy3ed852e2009-09-05 21:47:34 +00002950 case 'w':
2951 {
anthony2cfa1a12012-05-12 05:18:07 +00002952 if (LocaleCompare("width",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002953 {
anthonya322a832013-04-27 06:28:03 +00002954 WarnNoImageReturn("\"%%[%s]\"",property);
cristyb51dff52011-05-19 16:55:47 +00002955 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002956 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00002957 break;
2958 }
2959 break;
2960 }
anthony2eb9a422012-05-15 02:28:02 +00002961 case 'x': /* FUTURE: Obsolete X resolution */
anthony90ebc0d2012-04-28 08:10:02 +00002962 {
anthony2cfa1a12012-05-12 05:18:07 +00002963 if ((LocaleCompare("xresolution",property) == 0) ||
2964 (LocaleCompare("x-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00002965 {
anthonya322a832013-04-27 06:28:03 +00002966 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00002967 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00002968 image->resolution.x);
anthony90ebc0d2012-04-28 08:10:02 +00002969 break;
2970 }
2971 break;
2972 }
anthony2eb9a422012-05-15 02:28:02 +00002973 case 'y': /* FUTURE: Obsolete Y resolution */
anthony90ebc0d2012-04-28 08:10:02 +00002974 {
anthony2cfa1a12012-05-12 05:18:07 +00002975 if ((LocaleCompare("yresolution",property) == 0) ||
2976 (LocaleCompare("y-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00002977 {
anthonya322a832013-04-27 06:28:03 +00002978 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00002979 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00002980 image->resolution.y);
anthony90ebc0d2012-04-28 08:10:02 +00002981 break;
2982 }
2983 break;
2984 }
cristy3ed852e2009-09-05 21:47:34 +00002985 case 'z':
2986 {
anthonydb2a1d62013-04-29 01:34:58 +00002987 if (LocaleCompare("zero",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002988 {
anthonydb2a1d62013-04-29 01:34:58 +00002989 WarnNoImageInfoReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002990 string=image_info->zero;
cristy3ed852e2009-09-05 21:47:34 +00002991 break;
2992 }
2993 break;
2994 }
2995 }
anthonya322a832013-04-27 06:28:03 +00002996 if (string != (char *) NULL)
2997 return(string);
anthony2cfa1a12012-05-12 05:18:07 +00002998 if (*value != '\0')
anthonydb2a1d62013-04-29 01:34:58 +00002999 {
3000 /* create a cloned copy of result, that will get cleaned up, eventually */
3001 if (image != (Image *)NULL)
3002 {
3003 (void) SetImageArtifact(image,"get-property",value);
3004 return(GetImageArtifact(image,"get-property"));
3005 }
3006 else
3007 {
3008 (void) SetImageOption(image_info,"get-property",value);
3009 return(GetImageOption(image_info,"get-property"));
3010 }
3011 }
anthony2cfa1a12012-05-12 05:18:07 +00003012 return((char *)NULL);
cristy3ed852e2009-09-05 21:47:34 +00003013}
dirk6015d442013-10-19 15:07:40 +00003014#undef WarnNoImageReturn
3015
3016
cristy3ed852e2009-09-05 21:47:34 +00003017/*
3018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3019% %
3020% %
3021% %
3022% G e t N e x t I m a g e P r o p e r t y %
3023% %
3024% %
3025% %
3026%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3027%
cristy97fc9f32012-06-13 21:04:44 +00003028% GetNextImageProperty() gets the next free-form string property name.
cristy3ed852e2009-09-05 21:47:34 +00003029%
3030% The format of the GetNextImageProperty method is:
3031%
3032% char *GetNextImageProperty(const Image *image)
3033%
3034% A description of each parameter follows:
3035%
3036% o image: the image.
3037%
3038*/
3039MagickExport char *GetNextImageProperty(const Image *image)
3040{
3041 assert(image != (Image *) NULL);
3042 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003043 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003044 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3045 image->filename);
3046 if (image->properties == (void *) NULL)
3047 return((char *) NULL);
3048 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
3049}
dirk6015d442013-10-19 15:07:40 +00003050
3051
cristy3ed852e2009-09-05 21:47:34 +00003052/*
3053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3054% %
3055% %
3056% %
3057% I n t e r p r e t I m a g e P r o p e r t i e s %
3058% %
3059% %
3060% %
3061%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3062%
3063% InterpretImageProperties() replaces any embedded formatting characters with
anthony06762232012-04-29 11:45:40 +00003064% the appropriate image property and returns the interpreted text.
3065%
3066% This searches for and replaces
anthony003f8992012-05-11 12:50:07 +00003067% \n \r \% replaced by newline, return, and percent resp.
3068% &lt; &gt; &amp; replaced by '<', '>', '&' resp.
3069% %% replaced by percent
anthony06762232012-04-29 11:45:40 +00003070%
anthonyd2f39e02012-08-20 12:29:28 +00003071% %x %[x] where 'x' is a single letter properity, case sensitive).
3072% %[type:name] where 'type' a is special and known prefix.
anthony003f8992012-05-11 12:50:07 +00003073% %[name] where 'name' is a specifically known attribute, calculated
cristy97fc9f32012-06-13 21:04:44 +00003074% value, or a per-image property string name, or a per-image
anthonyd2f39e02012-08-20 12:29:28 +00003075% 'artifact' (as generated from a global option).
3076% It may contain ':' as long as the prefix is not special.
anthony06762232012-04-29 11:45:40 +00003077%
anthonyd2f39e02012-08-20 12:29:28 +00003078% Single letter % substitutions will only happen if the character before the
3079% percent is NOT a number. But braced substitutions will always be performed.
3080% This prevents the typical usage of percent in a interpreted geometry
3081% argument from being substituted when the percent is a geometry flag.
anthony003f8992012-05-11 12:50:07 +00003082%
3083% If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
3084% used as a search pattern to print multiple lines of "name=value\n" pairs of
anthony643c6132012-11-07 14:50:28 +00003085% the associacted set of properties.
anthony003f8992012-05-11 12:50:07 +00003086%
anthonyd2f39e02012-08-20 12:29:28 +00003087% The returned string must be freed using DestoryString() by the caller.
anthonyf68116b2012-04-14 12:54:41 +00003088%
cristy3ed852e2009-09-05 21:47:34 +00003089% The format of the InterpretImageProperties method is:
3090%
anthonydb2a1d62013-04-29 01:34:58 +00003091% char *InterpretImageProperties(ImageInfo *image_info,
anthony003f8992012-05-11 12:50:07 +00003092% Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003093%
3094% A description of each parameter follows:
3095%
anthonya322a832013-04-27 06:28:03 +00003096% o image_info: the image info. (required)
cristy3ed852e2009-09-05 21:47:34 +00003097%
anthonya322a832013-04-27 06:28:03 +00003098% o image: the image. (optional)
cristy3ed852e2009-09-05 21:47:34 +00003099%
3100% o embed_text: the address of a character string containing the embedded
3101% formatting characters.
3102%
cristy018f07f2011-09-04 21:15:19 +00003103% o exception: return any errors or warnings in this structure.
3104%
cristy3ed852e2009-09-05 21:47:34 +00003105*/
anthony643c6132012-11-07 14:50:28 +00003106
3107/* common inline code to expand the interpreted text string */
3108#define ExtendInterpretText(string_length) do { \
dirk93b02b72013-11-16 16:03:36 +00003109DisableMSCWarning(4127) \
anthony643c6132012-11-07 14:50:28 +00003110 size_t length=(string_length); \
3111 if ((size_t) (q-interpret_text+length+1) >= extent) \
3112 { extent+=length; \
3113 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
3114 extent+MaxTextExtent,sizeof(*interpret_text)); \
3115 if (interpret_text == (char *) NULL) \
3116 return((char *)NULL); \
3117 q=interpret_text+strlen(interpret_text); \
dirk93b02b72013-11-16 16:03:36 +00003118 } } while (0) /* no trailing ; */ \
3119RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003120
3121/* same but append the given string */
3122#define AppendString2Text(string) do { \
dirk93b02b72013-11-16 16:03:36 +00003123DisableMSCWarning(4127) \
anthony643c6132012-11-07 14:50:28 +00003124 size_t length=strlen((string)); \
3125 if ((size_t) (q-interpret_text+length+1) >= extent) \
3126 { extent+=length; \
3127 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
3128 extent+MaxTextExtent,sizeof(*interpret_text)); \
3129 if (interpret_text == (char *) NULL) \
3130 return((char *)NULL); \
3131 q=interpret_text+strlen(interpret_text); \
3132 } \
3133 (void) CopyMagickString(q,(string),extent); \
3134 q+=length; \
dirk93b02b72013-11-16 16:03:36 +00003135 } while (0) /* no trailing ; */ \
3136RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003137
anthonya322a832013-04-27 06:28:03 +00003138/* same but append a 'key' and 'string' pair */
3139#define AppendKeyValue2Text(key,string) do { \
dirk93b02b72013-11-16 16:03:36 +00003140DisableMSCWarning(4127) \
anthonya322a832013-04-27 06:28:03 +00003141 size_t length=strlen(key)+strlen(string)+2; \
anthony643c6132012-11-07 14:50:28 +00003142 if ((size_t) (q-interpret_text+length+1) >= extent) \
3143 { extent+=length; \
3144 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
3145 extent+MaxTextExtent,sizeof(*interpret_text)); \
3146 if (interpret_text == (char *) NULL) \
3147 return((char *)NULL); \
3148 q=interpret_text+strlen(interpret_text); \
3149 } \
anthonya322a832013-04-27 06:28:03 +00003150 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(string)); \
dirk93b02b72013-11-16 16:03:36 +00003151 } while (0) /* no trailing ; */ \
3152RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003153
anthonydb2a1d62013-04-29 01:34:58 +00003154MagickExport char *InterpretImageProperties(ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00003155 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003156{
3157 char
anthony003f8992012-05-11 12:50:07 +00003158 *interpret_text;
cristy3ed852e2009-09-05 21:47:34 +00003159
cristy3ed852e2009-09-05 21:47:34 +00003160 register char
anthony643c6132012-11-07 14:50:28 +00003161 *q; /* current position in interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003162
3163 register const char
anthony643c6132012-11-07 14:50:28 +00003164 *p; /* position in embed_text string being expanded */
cristy3ed852e2009-09-05 21:47:34 +00003165
cristy3ed852e2009-09-05 21:47:34 +00003166 size_t
anthony643c6132012-11-07 14:50:28 +00003167 extent; /* allocated length of interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003168
anthony003f8992012-05-11 12:50:07 +00003169 MagickBooleanType
3170 number;
3171
anthonydb2a1d62013-04-29 01:34:58 +00003172 assert(image == NULL || image->signature == MagickSignature);
3173 assert(image_info == NULL || image_info->signature == MagickSignature);
3174
3175 if (image != (Image *) NULL && IfMagickTrue(image->debug))
3176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3177 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
3178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
anthony2fbb5952012-05-05 12:35:30 +00003179
glennrpa22b2652013-01-05 12:37:21 +00003180 if (embed_text == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003181 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00003182 p=embed_text;
anthony2fbb5952012-05-05 12:35:30 +00003183
anthony964d28e2012-05-17 23:39:46 +00003184 if (*p == '\0')
3185 return(ConstantString(""));
3186
anthony2fbb5952012-05-05 12:35:30 +00003187 /* handle a '@' replace string from file */
anthony003f8992012-05-11 12:50:07 +00003188 if (*p == '@') {
3189 p++;
3190 if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
3191 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003192 OptionError,"UnableToAccessPath","%s",p);
anthony003f8992012-05-11 12:50:07 +00003193 return((char *) NULL);
3194 }
cristy3a5987c2013-11-07 14:18:46 +00003195 return(FileToString(p,~0UL,exception));
anthony003f8992012-05-11 12:50:07 +00003196 }
3197
cristy3ed852e2009-09-05 21:47:34 +00003198 /*
3199 Translate any embedded format characters.
3200 */
anthony003f8992012-05-11 12:50:07 +00003201 interpret_text=AcquireString(embed_text); /* new string with extra space */
anthony643c6132012-11-07 14:50:28 +00003202 extent=MaxTextExtent; /* allocated space in string */
anthony003f8992012-05-11 12:50:07 +00003203 number=MagickFalse; /* is last char a number? */
3204 for (q=interpret_text; *p!='\0'; number=IsMagickTrue(isdigit(*p)),p++)
cristy3ed852e2009-09-05 21:47:34 +00003205 {
3206 *q='\0';
anthony643c6132012-11-07 14:50:28 +00003207 ExtendInterpretText(MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003208 /*
anthonyd2f39e02012-08-20 12:29:28 +00003209 Look for the various escapes, (and handle other specials)
cristy3ed852e2009-09-05 21:47:34 +00003210 */
anthony003f8992012-05-11 12:50:07 +00003211 switch (*p) {
3212 case '\\':
3213 switch (*(p+1)) {
3214 case '\0':
3215 continue;
3216 case 'r': /* convert to RETURN */
3217 *q++='\r';
3218 p++;
3219 continue;
3220 case 'n': /* convert to NEWLINE */
3221 *q++='\n';
3222 p++;
3223 continue;
3224 case '\n': /* EOL removal UNIX,MacOSX */
3225 p++;
3226 continue;
3227 case '\r': /* EOL removal DOS,Windows */
3228 p++;
3229 if (*p == '\n') /* return-newline EOL */
3230 p++;
3231 continue;
3232 default:
3233 p++;
3234 *q++=(*p);
3235 continue;
3236 }
3237 continue; /* never reached! */
3238 case '&':
anthony104f8932012-05-13 01:54:53 +00003239 if (LocaleNCompare("&lt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003240 *q++='<', p+=3;
anthony104f8932012-05-13 01:54:53 +00003241 else if (LocaleNCompare("&gt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003242 *q++='>', p+=3;
anthony104f8932012-05-13 01:54:53 +00003243 else if (LocaleNCompare("&amp;",p,5) == 0)
anthony003f8992012-05-11 12:50:07 +00003244 *q++='&', p+=4;
3245 else
3246 *q++=(*p);
cristy3ed852e2009-09-05 21:47:34 +00003247 continue;
anthony003f8992012-05-11 12:50:07 +00003248 case '%':
3249 break; /* continue to next set of handlers */
3250 default:
3251 *q++=(*p); /* any thing else is 'as normal' */
cristy3ed852e2009-09-05 21:47:34 +00003252 continue;
anthony003f8992012-05-11 12:50:07 +00003253 }
anthony104f8932012-05-13 01:54:53 +00003254 p++; /* advance beyond the percent */
anthony2fbb5952012-05-05 12:35:30 +00003255
anthony003f8992012-05-11 12:50:07 +00003256 /*
anthony2ec9bd92012-05-20 05:43:24 +00003257 Doubled Percent - or percent at end of string
anthony003f8992012-05-11 12:50:07 +00003258 */
anthony2ec9bd92012-05-20 05:43:24 +00003259 if ( *p == '\0' )
3260 p--;
anthony104f8932012-05-13 01:54:53 +00003261 if ( *p == '%' ) {
anthony89075d02012-05-18 03:40:40 +00003262 *q++='%';
anthony104f8932012-05-13 01:54:53 +00003263 continue;
3264 }
anthony003f8992012-05-11 12:50:07 +00003265
3266 /*
anthonyd2f39e02012-08-20 12:29:28 +00003267 Single letter escapes %c
anthony003f8992012-05-11 12:50:07 +00003268 */
anthony104f8932012-05-13 01:54:53 +00003269 if ( *p != '[' ) {
anthony003f8992012-05-11 12:50:07 +00003270 const char
anthonya322a832013-04-27 06:28:03 +00003271 *string;
anthony003f8992012-05-11 12:50:07 +00003272
3273 /* But only if not preceeded by a number! */
3274 if ( IfMagickTrue(number) ) {
anthony104f8932012-05-13 01:54:53 +00003275 *q++='%'; /* do NOT substitute the percent */
3276 p--; /* back up one */
anthony003f8992012-05-11 12:50:07 +00003277 continue;
3278 }
anthonya322a832013-04-27 06:28:03 +00003279 string=GetMagickPropertyLetter(image_info,image,*p, exception);
3280 if (string != (char *) NULL)
cristyf57e5482013-02-17 22:11:27 +00003281 {
anthonya322a832013-04-27 06:28:03 +00003282 AppendString2Text(string);
anthonydb2a1d62013-04-29 01:34:58 +00003283 if (image != (Image *) NULL)
3284 (void)DeleteImageArtifact(image,"get-property");
3285 if (image_info != (ImageInfo *) NULL)
3286 (void)DeleteImageOption(image_info,"get-property");
cristyf57e5482013-02-17 22:11:27 +00003287 continue;
3288 }
3289 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3290 "UnknownImageProperty","\"%%%c\"",*p);
anthony003f8992012-05-11 12:50:07 +00003291 continue;
anthony2fbb5952012-05-05 12:35:30 +00003292 }
anthony2fbb5952012-05-05 12:35:30 +00003293
anthony003f8992012-05-11 12:50:07 +00003294 /*
anthonyd2f39e02012-08-20 12:29:28 +00003295 Braced Percent Escape %[...]
anthony003f8992012-05-11 12:50:07 +00003296 */
3297 {
3298 char
3299 pattern[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00003300
anthony003f8992012-05-11 12:50:07 +00003301 const char
3302 *key,
anthonya322a832013-04-27 06:28:03 +00003303 *string;
cristy3ed852e2009-09-05 21:47:34 +00003304
anthony003f8992012-05-11 12:50:07 +00003305 register ssize_t
3306 len;
cristy3ed852e2009-09-05 21:47:34 +00003307
anthony003f8992012-05-11 12:50:07 +00003308 ssize_t
3309 depth;
3310
anthonya322a832013-04-27 06:28:03 +00003311 /* get the property name framed by the %[...] */
anthony104f8932012-05-13 01:54:53 +00003312 p++; /* advance p to just inside the opening brace */
anthony003f8992012-05-11 12:50:07 +00003313 depth=1;
3314 if ( *p == ']' ) {
3315 (void) ThrowMagickException(exception,GetMagickModule(),
3316 OptionWarning,"UnknownImageProperty","\"%%[]\"");
3317 break;
3318 }
3319 for (len=0; len<(MaxTextExtent-1L) && (*p != '\0');)
3320 {
3321 /* skip escaped braces within braced pattern */
3322 if ( (*p == '\\') && (*(p+1) != '\0') ) {
3323 pattern[len++]=(*p++);
3324 pattern[len++]=(*p++);
3325 continue;
3326 }
3327 if (*p == '[')
3328 depth++;
3329 if (*p == ']')
3330 depth--;
3331 if (depth <= 0)
3332 break;
3333 pattern[len++]=(*p++);
3334 }
3335 pattern[len]='\0';
3336 /* Check for unmatched final ']' for "%[...]" */
3337 if ( depth != 0 ) {
3338 if (len >= 64) { /* truncate string for error message */
3339 pattern[61] = '.';
3340 pattern[62] = '.';
3341 pattern[63] = '.';
3342 pattern[64] = '\0';
3343 }
3344 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003345 OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
3346 interpret_text=DestroyString(interpret_text);
3347 return((char *)NULL);
anthony003f8992012-05-11 12:50:07 +00003348 }
3349
3350 /*
anthony643c6132012-11-07 14:50:28 +00003351 Special Lookup Prefixes %[prefix:...]
anthony003f8992012-05-11 12:50:07 +00003352 */
anthonya322a832013-04-27 06:28:03 +00003353 /* fx - value calculator */
3354 if (LocaleNCompare("fx:",pattern,3) == 0)
3355 {
3356 FxInfo
3357 *fx_info;
3358
3359 double
3360 value;
3361
3362 MagickBooleanType
3363 status;
3364
3365 if (image == (Image *) NULL ) {
3366 (void) ThrowMagickException(exception,GetMagickModule(),
3367 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3368 continue; /* else no image to retrieve artifact */
3369 }
3370 fx_info=AcquireFxInfo(image,pattern+3,exception);
3371 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
3372 &value,exception);
3373 fx_info=DestroyFxInfo(fx_info);
3374 if( IfMagickTrue(status) )
3375 {
3376 char
3377 result[MaxTextExtent];
3378
3379 (void) FormatLocaleString(result,MaxTextExtent,"%.*g",
3380 GetMagickPrecision(),(double) value);
anthonyec26e252013-04-28 12:18:12 +00003381 AppendString2Text(result);
anthonya322a832013-04-27 06:28:03 +00003382 }
anthonyec26e252013-04-28 12:18:12 +00003383 continue;
anthonya322a832013-04-27 06:28:03 +00003384 }
3385 /* pixel - color value calculator */
3386 if (LocaleNCompare("pixel:",pattern,6) == 0)
3387 {
3388 FxInfo
3389 *fx_info;
3390
3391 double
3392 value;
3393
cristyee1bdaa2013-07-16 16:45:03 +00003394 MagickStatusType
anthonya322a832013-04-27 06:28:03 +00003395 status;
3396
3397 PixelInfo
3398 pixel;
3399
3400 if (image == (Image *) NULL ) {
3401 (void) ThrowMagickException(exception,GetMagickModule(),
3402 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3403 continue; /* else no image to retrieve artifact */
3404 }
3405 GetPixelInfo(image,&pixel);
3406 fx_info=AcquireFxInfo(image,pattern+6,exception);
cristyee1bdaa2013-07-16 16:45:03 +00003407 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
3408 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003409 pixel.red=(double) QuantumRange*value;
cristyaae13f62013-08-15 14:41:32 +00003410 status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003411 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003412 pixel.green=(double) QuantumRange*value;
cristyaae13f62013-08-15 14:41:32 +00003413 status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003414 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003415 pixel.blue=(double) QuantumRange*value;
3416 if (image->colorspace == CMYKColorspace)
3417 {
cristyaae13f62013-08-15 14:41:32 +00003418 status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003419 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003420 pixel.black=(double) QuantumRange*value;
3421 }
cristyaae13f62013-08-15 14:41:32 +00003422 status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003423 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003424 pixel.alpha=(double) QuantumRange*value;
3425 fx_info=DestroyFxInfo(fx_info);
3426 if( IfMagickTrue(status) )
3427 {
3428 char
3429 name[MaxTextExtent];
3430
3431 (void) QueryColorname(image,&pixel,SVGCompliance,name,
3432 exception);
3433 AppendString2Text(name);
3434 }
anthonyec26e252013-04-28 12:18:12 +00003435 continue;
anthonya322a832013-04-27 06:28:03 +00003436 }
anthony643c6132012-11-07 14:50:28 +00003437 /* option - direct global option lookup (with globbing) */
3438 if (LocaleNCompare("option:",pattern,7) == 0)
3439 {
anthonya322a832013-04-27 06:28:03 +00003440 if (image_info == (ImageInfo *) NULL ) {
3441 (void) ThrowMagickException(exception,GetMagickModule(),
3442 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3443 continue; /* else no image to retrieve artifact */
3444 }
anthony643c6132012-11-07 14:50:28 +00003445 if( IfMagickTrue(IsGlob(pattern+7)) )
3446 {
3447 ResetImageOptionIterator(image_info);
3448 while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
3449 if( IfMagickTrue(GlobExpression(key,pattern+7,MagickTrue)) )
3450 {
anthonya322a832013-04-27 06:28:03 +00003451 string=GetImageOption(image_info,key);
3452 if (string != (const char *) NULL)
3453 AppendKeyValue2Text(key,string);
3454 /* else - assertion failure? key found but no string value! */
anthony643c6132012-11-07 14:50:28 +00003455 }
3456 continue;
3457 }
anthonya322a832013-04-27 06:28:03 +00003458 string=GetImageOption(image_info,pattern+7);
3459 if (string == (char *) NULL)
3460 goto PropertyLookupFailure; /* no artifact of this specifc name */
3461 AppendString2Text(string);
anthony643c6132012-11-07 14:50:28 +00003462 continue;
3463 }
3464 /* artifact - direct image artifact lookup (with glob) */
anthonyc7994672012-11-17 05:33:27 +00003465 if (LocaleNCompare("artifact:",pattern,9) == 0)
anthony643c6132012-11-07 14:50:28 +00003466 {
anthonya322a832013-04-27 06:28:03 +00003467 if (image == (Image *) NULL ) {
3468 (void) ThrowMagickException(exception,GetMagickModule(),
3469 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
anthonyc7994672012-11-17 05:33:27 +00003470 continue; /* else no image to retrieve artifact */
anthonya322a832013-04-27 06:28:03 +00003471 }
anthony643c6132012-11-07 14:50:28 +00003472 if( IfMagickTrue(IsGlob(pattern+9)) )
3473 {
3474 ResetImageArtifactIterator(image);
3475 while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
3476 if( IfMagickTrue(GlobExpression(key,pattern+9,MagickTrue)) )
3477 {
anthonya322a832013-04-27 06:28:03 +00003478 string=GetImageArtifact(image,key);
3479 if (string != (const char *) NULL)
3480 AppendKeyValue2Text(key,string);
3481 /* else - assertion failure? key found but no string value! */
anthony643c6132012-11-07 14:50:28 +00003482 }
3483 continue;
3484 }
anthonya322a832013-04-27 06:28:03 +00003485 string=GetImageArtifact(image,pattern+9);
3486 if (string == (char *) NULL)
3487 goto PropertyLookupFailure; /* no artifact of this specifc name */
3488 AppendString2Text(string);
anthonyc7994672012-11-17 05:33:27 +00003489 continue;
anthony643c6132012-11-07 14:50:28 +00003490 }
anthonya322a832013-04-27 06:28:03 +00003491 /* property - direct image property lookup (with glob) */
3492 if (LocaleNCompare("property:",pattern,9) == 0)
3493 {
3494 if (image == (Image *) NULL ) {
3495 (void) ThrowMagickException(exception,GetMagickModule(),
3496 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3497 continue; /* else no image to retrieve artifact */
3498 }
3499 if( IfMagickTrue(IsGlob(pattern+9)) )
cristy3ed852e2009-09-05 21:47:34 +00003500 {
anthonya322a832013-04-27 06:28:03 +00003501 ResetImagePropertyIterator(image);
3502 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
3503 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
3504 {
3505 string=GetImageProperty(image,key,exception);
3506 if (string != (const char *) NULL)
3507 AppendKeyValue2Text(key,string);
3508 /* else - assertion failure? */
3509 }
anthony003f8992012-05-11 12:50:07 +00003510 continue;
3511 }
anthonya322a832013-04-27 06:28:03 +00003512 string=GetImageProperty(image,pattern+9,exception);
3513 if (string == (char *) NULL)
3514 goto PropertyLookupFailure; /* no artifact of this specifc name */
3515 AppendString2Text(string);
3516 continue;
3517 }
3518 /* Properties without special prefix.
3519 This handles attributes, properties, and profiles such as %[exif:...]
3520 Note the profile properties may also include a glob expansion pattern.
3521 */
3522 if ( image != (Image *)NULL )
3523 {
3524 string=GetImageProperty(image,pattern,exception);
3525 if (string != (const char *) NULL)
3526 {
3527 AppendString2Text(string);
anthonydb2a1d62013-04-29 01:34:58 +00003528 if (image != (Image *) NULL)
3529 (void)DeleteImageArtifact(image,"get-property");
3530 if (image_info != (ImageInfo *) NULL)
3531 (void)DeleteImageOption(image_info,"get-property");
anthonya322a832013-04-27 06:28:03 +00003532 continue;
3533 }
3534 }
anthony003f8992012-05-11 12:50:07 +00003535 /*
cristy97fc9f32012-06-13 21:04:44 +00003536 Handle property 'glob' patterns
anthony003f8992012-05-11 12:50:07 +00003537 Such as: %[*] %[user:array_??] %[filename:e*]
3538 */
3539 if( IfMagickTrue(IsGlob(pattern)) )
3540 {
anthonya322a832013-04-27 06:28:03 +00003541 if (image == (Image *) NULL)
3542 continue; /* else no image to retrieve proprty - no list */
anthony003f8992012-05-11 12:50:07 +00003543 ResetImagePropertyIterator(image);
anthony643c6132012-11-07 14:50:28 +00003544 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003545 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
cristy3ed852e2009-09-05 21:47:34 +00003546 {
anthonya322a832013-04-27 06:28:03 +00003547 string=GetImageProperty(image,key,exception);
3548 if (string != (const char *) NULL)
3549 AppendKeyValue2Text(key,string);
anthony643c6132012-11-07 14:50:28 +00003550 /* else - assertion failure? */
cristy3ed852e2009-09-05 21:47:34 +00003551 }
anthony003f8992012-05-11 12:50:07 +00003552 continue;
3553 }
3554 /*
3555 Look for a known property or image attribute
3556 Such as %[basename] %[denisty] %[delay]
anthony643c6132012-11-07 14:50:28 +00003557 Also handles a braced single letter: %[b] %[G] %[g]
anthony003f8992012-05-11 12:50:07 +00003558 */
anthonya322a832013-04-27 06:28:03 +00003559 string=GetMagickProperty(image_info,image,pattern,exception);
3560 if (string != (const char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003561 {
anthonya322a832013-04-27 06:28:03 +00003562 AppendString2Text(string);
anthony003f8992012-05-11 12:50:07 +00003563 continue;
3564 }
3565 /*
anthonya322a832013-04-27 06:28:03 +00003566 Look for a per-image Artifact
3567 This includes option lookup (FUTURE: interpreted according to image)
anthony003f8992012-05-11 12:50:07 +00003568 */
anthonya322a832013-04-27 06:28:03 +00003569 if (image != (Image *)NULL)
anthony003f8992012-05-11 12:50:07 +00003570 {
anthonya322a832013-04-27 06:28:03 +00003571 string=GetImageArtifact(image,pattern);
3572 if (string != (char *) NULL)
3573 {
3574 AppendString2Text(string);
3575 continue;
3576 }
anthony003f8992012-05-11 12:50:07 +00003577 }
anthonya322a832013-04-27 06:28:03 +00003578 else
3579 /* no image, so direct 'option' lookup (no delayed percent escapes) */
3580 if (image_info != (ImageInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003581 {
anthonya322a832013-04-27 06:28:03 +00003582 string=GetImageOption(image_info,pattern);
3583 if (string != (char *) NULL)
3584 {
3585 AppendString2Text(string);
3586 continue;
3587 }
cristy3ed852e2009-09-05 21:47:34 +00003588 }
anthonya322a832013-04-27 06:28:03 +00003589PropertyLookupFailure:
anthony003f8992012-05-11 12:50:07 +00003590 /*
3591 Failed to find any match anywhere!
3592 */
3593 if (len >= 64) { /* truncate string for error message */
3594 pattern[61] = '.';
3595 pattern[62] = '.';
3596 pattern[63] = '.';
3597 pattern[64] = '\0';
3598 }
3599 (void) ThrowMagickException(exception,GetMagickModule(),
3600 OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
3601 /* continue */
3602 } /* Braced Percent Escape */
3603
3604 } /* for each char in 'embed_text' */
cristy3ed852e2009-09-05 21:47:34 +00003605 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003606 return(interpret_text);
3607}
dirk6015d442013-10-19 15:07:40 +00003608
3609
cristy3ed852e2009-09-05 21:47:34 +00003610/*
3611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3612% %
3613% %
3614% %
3615% R e m o v e I m a g e P r o p e r t y %
3616% %
3617% %
3618% %
3619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3620%
3621% RemoveImageProperty() removes a property from the image and returns its
3622% value.
3623%
anthonyd2f39e02012-08-20 12:29:28 +00003624% In this case the ConstantString() value returned should be freed by the
3625% caller when finished.
3626%
cristy3ed852e2009-09-05 21:47:34 +00003627% The format of the RemoveImageProperty method is:
3628%
3629% char *RemoveImageProperty(Image *image,const char *property)
3630%
3631% A description of each parameter follows:
3632%
3633% o image: the image.
3634%
3635% o property: the image property.
3636%
3637*/
3638MagickExport char *RemoveImageProperty(Image *image,
3639 const char *property)
3640{
3641 char
3642 *value;
3643
3644 assert(image != (Image *) NULL);
3645 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003646 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003647 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3648 image->filename);
3649 if (image->properties == (void *) NULL)
3650 return((char *) NULL);
3651 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3652 property);
3653 return(value);
3654}
dirk6015d442013-10-19 15:07:40 +00003655
3656
cristy3ed852e2009-09-05 21:47:34 +00003657/*
3658%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3659% %
3660% %
3661% %
3662% 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 %
3663% %
3664% %
3665% %
3666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3667%
3668% ResetImagePropertyIterator() resets the image properties iterator. Use it
3669% in conjunction with GetNextImageProperty() to iterate over all the values
3670% associated with an image property.
3671%
3672% The format of the ResetImagePropertyIterator method is:
3673%
3674% ResetImagePropertyIterator(Image *image)
3675%
3676% A description of each parameter follows:
3677%
3678% o image: the image.
3679%
3680*/
3681MagickExport void ResetImagePropertyIterator(const Image *image)
3682{
3683 assert(image != (Image *) NULL);
3684 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003685 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003686 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3687 image->filename);
3688 if (image->properties == (void *) NULL)
3689 return;
3690 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3691}
dirk6015d442013-10-19 15:07:40 +00003692
3693
cristy3ed852e2009-09-05 21:47:34 +00003694/*
3695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3696% %
3697% %
3698% %
3699% S e t I m a g e P r o p e r t y %
3700% %
3701% %
3702% %
3703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3704%
anthony104f8932012-05-13 01:54:53 +00003705% SetImageProperty() saves the given string value either to specific known
cristy97fc9f32012-06-13 21:04:44 +00003706% attribute or to a freeform property string.
anthony6e2e0732012-05-06 12:22:43 +00003707%
cristy97fc9f32012-06-13 21:04:44 +00003708% Attempting to set a property that is normally calculated will produce
anthony6e2e0732012-05-06 12:22:43 +00003709% an exception.
cristy3ed852e2009-09-05 21:47:34 +00003710%
3711% The format of the SetImageProperty method is:
3712%
3713% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003714% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003715%
3716% A description of each parameter follows:
3717%
3718% o image: the image.
3719%
3720% o property: the image property.
3721%
3722% o values: the image property values.
3723%
cristyd15e6592011-10-15 00:13:06 +00003724% o exception: return any errors or warnings in this structure.
3725%
cristy3ed852e2009-09-05 21:47:34 +00003726*/
3727MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003728 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003729{
3730 MagickBooleanType
3731 status;
3732
3733 MagickStatusType
3734 flags;
3735
3736 assert(image != (Image *) NULL);
3737 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003738 if( IfMagickTrue(image->debug) )
anthony6e2e0732012-05-06 12:22:43 +00003739 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3740
anthony7bb7aee2012-05-15 00:09:16 +00003741 /* Create splay-tree */
cristy3ed852e2009-09-05 21:47:34 +00003742 if (image->properties == (void *) NULL)
3743 image->properties=NewSplayTree(CompareSplayTreeString,
3744 RelinquishMagickMemory,RelinquishMagickMemory);
anthony7bb7aee2012-05-15 00:09:16 +00003745
cristy97fc9f32012-06-13 21:04:44 +00003746 /* Delete property if NULL -- empty string values are valid! */
glennrp9831cc42013-01-08 19:08:31 +00003747 if (value == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003748 return(DeleteImageProperty(image,property));
3749 status=MagickTrue;
anthony6e2e0732012-05-06 12:22:43 +00003750
anthony7bb7aee2012-05-15 00:09:16 +00003751 /* Do not 'set' single letter properties - read only shorthand */
anthony6e2e0732012-05-06 12:22:43 +00003752 if (strlen(property) <= 1)
3753 {
3754 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003755 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony6e2e0732012-05-06 12:22:43 +00003756 return(MagickFalse);
3757 }
anthony7bb7aee2012-05-15 00:09:16 +00003758
anthonya322a832013-04-27 06:28:03 +00003759 /* FUTURE: binary chars or quotes in key should produce a error */
anthony6e2e0732012-05-06 12:22:43 +00003760
anthonya322a832013-04-27 06:28:03 +00003761
3762 /* Set attributes with known names or special prefixes
3763 return result is found, or break to set a free form properity
3764 */
cristy3ed852e2009-09-05 21:47:34 +00003765 switch (*property)
3766 {
anthonya322a832013-04-27 06:28:03 +00003767#if 0 /* Percent escape's sets values with this prefix: for later use
3768 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00003769 case '8':
3770 {
3771 if (LocaleNCompare("8bim:",property,5) == 0)
3772 {
3773 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003774 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003775 return(MagickFalse);
3776 }
3777 break;
3778 }
anthonyc99875a2012-11-14 05:22:17 +00003779#endif
cristy3ed852e2009-09-05 21:47:34 +00003780 case 'B':
3781 case 'b':
3782 {
anthony2f7f9ca2012-05-14 06:33:53 +00003783 if (LocaleCompare("background",property) == 0)
cristyc6c08ab2010-07-24 23:50:09 +00003784 {
cristy9950d572011-10-01 18:22:35 +00003785 (void) QueryColorCompliance(value,AllCompliance,
anthony643c6132012-11-07 14:50:28 +00003786 &image->background_color,exception);
anthonya322a832013-04-27 06:28:03 +00003787 /* check for FUTURE: value exception?? */
anthony643c6132012-11-07 14:50:28 +00003788 /* also add user input to splay tree */
cristyc6c08ab2010-07-24 23:50:09 +00003789 }
anthonya322a832013-04-27 06:28:03 +00003790 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003791 }
3792 case 'C':
3793 case 'c':
3794 {
anthony2f7f9ca2012-05-14 06:33:53 +00003795 if (LocaleCompare("channels",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003796 {
3797 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003798 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003799 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003800 }
anthony2f7f9ca2012-05-14 06:33:53 +00003801 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003802 {
cristybb503372010-05-27 20:51:26 +00003803 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003804 colorspace;
3805
cristy042ee782011-04-22 18:48:30 +00003806 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003807 value);
3808 if (colorspace < 0)
anthonya322a832013-04-27 06:28:03 +00003809 return(MagickFalse); /* FUTURE: value exception?? */
cristy220c4d52013-11-27 19:31:32 +00003810 return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
cristy3ed852e2009-09-05 21:47:34 +00003811 }
anthony2f7f9ca2012-05-14 06:33:53 +00003812 if (LocaleCompare("compose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003813 {
cristybb503372010-05-27 20:51:26 +00003814 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003815 compose;
3816
cristy042ee782011-04-22 18:48:30 +00003817 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003818 if (compose < 0)
anthonya322a832013-04-27 06:28:03 +00003819 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003820 image->compose=(CompositeOperator) compose;
anthony643c6132012-11-07 14:50:28 +00003821 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003822 }
anthony2f7f9ca2012-05-14 06:33:53 +00003823 if (LocaleCompare("compress",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003824 {
cristybb503372010-05-27 20:51:26 +00003825 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003826 compression;
3827
cristy042ee782011-04-22 18:48:30 +00003828 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003829 value);
3830 if (compression < 0)
anthonya322a832013-04-27 06:28:03 +00003831 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003832 image->compression=(CompressionType) compression;
anthony643c6132012-11-07 14:50:28 +00003833 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003834 }
anthony2f7f9ca2012-05-14 06:33:53 +00003835 if (LocaleCompare("copyright",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003836 {
3837 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003838 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003839 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003840 }
anthonya322a832013-04-27 06:28:03 +00003841 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003842 }
3843 case 'D':
3844 case 'd':
3845 {
anthony2f7f9ca2012-05-14 06:33:53 +00003846 if (LocaleCompare("delay",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003847 {
3848 GeometryInfo
3849 geometry_info;
3850
3851 flags=ParseGeometry(value,&geometry_info);
3852 if ((flags & GreaterValue) != 0)
3853 {
cristybb503372010-05-27 20:51:26 +00003854 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3855 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003856 }
3857 else
3858 if ((flags & LessValue) != 0)
3859 {
cristybb503372010-05-27 20:51:26 +00003860 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
anthony643c6132012-11-07 14:50:28 +00003861 image->delay=(ssize_t)
cristyc6c08ab2010-07-24 23:50:09 +00003862 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003863 }
3864 else
cristybb503372010-05-27 20:51:26 +00003865 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003866 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003867 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
anthony643c6132012-11-07 14:50:28 +00003868 return(MagickTrue);
3869 }
3870 if (LocaleCompare("delay_units",property) == 0)
3871 {
3872 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003873 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003874 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00003875 }
anthony2f7f9ca2012-05-14 06:33:53 +00003876 if (LocaleCompare("density",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003877 {
3878 GeometryInfo
3879 geometry_info;
3880
3881 flags=ParseGeometry(value,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +00003882 image->resolution.x=geometry_info.rho;
3883 image->resolution.y=geometry_info.sigma;
cristyfbb56842010-08-02 11:26:33 +00003884 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +00003885 image->resolution.y=image->resolution.x;
anthony643c6132012-11-07 14:50:28 +00003886 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00003887 }
anthony2f7f9ca2012-05-14 06:33:53 +00003888 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003889 {
cristye27293e2009-12-18 02:53:20 +00003890 image->depth=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00003891 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003892 }
anthony2f7f9ca2012-05-14 06:33:53 +00003893 if (LocaleCompare("dispose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003894 {
cristybb503372010-05-27 20:51:26 +00003895 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003896 dispose;
3897
cristy042ee782011-04-22 18:48:30 +00003898 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003899 if (dispose < 0)
anthonya322a832013-04-27 06:28:03 +00003900 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003901 image->dispose=(DisposeType) dispose;
anthony643c6132012-11-07 14:50:28 +00003902 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003903 }
anthonya322a832013-04-27 06:28:03 +00003904 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00003905 }
anthonya322a832013-04-27 06:28:03 +00003906#if 0 /* Percent escape's sets values with this prefix: for later use
3907 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00003908 case 'E':
3909 case 'e':
3910 {
3911 if (LocaleNCompare("exif:",property,5) == 0)
3912 {
3913 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003914 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003915 return(MagickFalse);
3916 }
anthonya322a832013-04-27 06:28:03 +00003917 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00003918 }
3919 case 'F':
3920 case 'f':
3921 {
3922 if (LocaleNCompare("fx:",property,3) == 0)
3923 {
3924 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003925 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003926 return(MagickFalse);
3927 }
anthonya322a832013-04-27 06:28:03 +00003928 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003929 }
anthonyc99875a2012-11-14 05:22:17 +00003930#endif
cristy3ed852e2009-09-05 21:47:34 +00003931 case 'G':
3932 case 'g':
3933 {
anthony2f7f9ca2012-05-14 06:33:53 +00003934 if (LocaleCompare("gamma",property) == 0)
3935 {
3936 image->gamma=StringToDouble(value,(char **) NULL);
anthony643c6132012-11-07 14:50:28 +00003937 return(MagickTrue);
anthony2f7f9ca2012-05-14 06:33:53 +00003938 }
3939 if (LocaleCompare("gravity",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003940 {
cristybb503372010-05-27 20:51:26 +00003941 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003942 gravity;
3943
cristy042ee782011-04-22 18:48:30 +00003944 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003945 if (gravity < 0)
anthonya322a832013-04-27 06:28:03 +00003946 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003947 image->gravity=(GravityType) gravity;
anthony643c6132012-11-07 14:50:28 +00003948 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003949 }
anthonya322a832013-04-27 06:28:03 +00003950 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003951 }
anthony6e2e0732012-05-06 12:22:43 +00003952 case 'H':
3953 case 'h':
anthony643c6132012-11-07 14:50:28 +00003954 {
anthony2f7f9ca2012-05-14 06:33:53 +00003955 if (LocaleCompare("height",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003956 {
3957 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003958 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003959 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003960 }
anthonya322a832013-04-27 06:28:03 +00003961 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00003962 }
cristy3ed852e2009-09-05 21:47:34 +00003963 case 'I':
3964 case 'i':
3965 {
cristy70e9f682013-03-12 22:31:22 +00003966 if (LocaleCompare("intensity",property) == 0)
3967 {
3968 ssize_t
3969 intensity;
3970
3971 intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,
3972 value);
3973 if (intensity < 0)
3974 return(MagickFalse);
3975 image->intensity=(PixelIntensityMethod) intensity;
3976 return(MagickTrue);
3977 }
anthony2f7f9ca2012-05-14 06:33:53 +00003978 if (LocaleCompare("intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003979 {
cristybb503372010-05-27 20:51:26 +00003980 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003981 rendering_intent;
3982
cristy042ee782011-04-22 18:48:30 +00003983 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003984 value);
3985 if (rendering_intent < 0)
anthonya322a832013-04-27 06:28:03 +00003986 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003987 image->rendering_intent=(RenderingIntent) rendering_intent;
anthony643c6132012-11-07 14:50:28 +00003988 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003989 }
anthony2f7f9ca2012-05-14 06:33:53 +00003990 if (LocaleCompare("interpolate",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003991 {
cristybb503372010-05-27 20:51:26 +00003992 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003993 interpolate;
3994
cristy042ee782011-04-22 18:48:30 +00003995 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003996 value);
3997 if (interpolate < 0)
anthonya322a832013-04-27 06:28:03 +00003998 return(MagickFalse); /* FUTURE: value exception?? */
cristy5c4e2582011-09-11 19:21:03 +00003999 image->interpolate=(PixelInterpolateMethod) interpolate;
anthony643c6132012-11-07 14:50:28 +00004000 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004001 }
anthonya322a832013-04-27 06:28:03 +00004002#if 0 /* Percent escape's sets values with this prefix: for later use
4003 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004004 if (LocaleNCompare("iptc:",property,5) == 0)
4005 {
4006 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004007 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004008 return(MagickFalse);
4009 }
anthonyc99875a2012-11-14 05:22:17 +00004010#endif
anthonya322a832013-04-27 06:28:03 +00004011 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004012 }
anthony6e2e0732012-05-06 12:22:43 +00004013 case 'K':
4014 case 'k':
anthony2f7f9ca2012-05-14 06:33:53 +00004015 if (LocaleCompare("kurtosis",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004016 {
4017 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004018 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004019 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004020 }
anthonya322a832013-04-27 06:28:03 +00004021 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004022 case 'L':
4023 case 'l':
4024 {
anthony2f7f9ca2012-05-14 06:33:53 +00004025 if (LocaleCompare("loop",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004026 {
cristye27293e2009-12-18 02:53:20 +00004027 image->iterations=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00004028 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004029 }
anthonya322a832013-04-27 06:28:03 +00004030 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004031 }
anthony6e2e0732012-05-06 12:22:43 +00004032 case 'M':
4033 case 'm':
anthony2f7f9ca2012-05-14 06:33:53 +00004034 if ( (LocaleCompare("magick",property) == 0) ||
4035 (LocaleCompare("max",property) == 0) ||
4036 (LocaleCompare("mean",property) == 0) ||
4037 (LocaleCompare("min",property) == 0) ||
4038 (LocaleCompare("min",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00004039 {
cristy5048d302012-08-07 01:05:16 +00004040 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +00004041 "SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004042 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004043 }
anthonya322a832013-04-27 06:28:03 +00004044 break; /* not an attribute, add as a property */
anthony6e2e0732012-05-06 12:22:43 +00004045 case 'O':
4046 case 'o':
anthony2f7f9ca2012-05-14 06:33:53 +00004047 if (LocaleCompare("opaque",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004048 {
4049 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004050 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004051 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004052 }
anthonya322a832013-04-27 06:28:03 +00004053 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004054 case 'P':
4055 case 'p':
4056 {
anthony2f7f9ca2012-05-14 06:33:53 +00004057 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004058 {
4059 char
4060 *geometry;
4061
4062 geometry=GetPageGeometry(value);
4063 flags=ParseAbsoluteGeometry(geometry,&image->page);
4064 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00004065 return(MagickTrue);
4066 }
anthonya322a832013-04-27 06:28:03 +00004067#if 0 /* Percent escape's sets values with this prefix: for later use
4068 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004069 if (LocaleNCompare("pixel:",property,6) == 0)
4070 {
4071 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004072 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004073 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00004074 }
anthonyc99875a2012-11-14 05:22:17 +00004075#endif
anthony2f7f9ca2012-05-14 06:33:53 +00004076 if (LocaleCompare("profile",property) == 0)
cristy071dd7b2010-04-09 13:04:54 +00004077 {
4078 ImageInfo
4079 *image_info;
4080
4081 StringInfo
4082 *profile;
4083
4084 image_info=AcquireImageInfo();
4085 (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
cristyc6c08ab2010-07-24 23:50:09 +00004086 (void) SetImageInfo(image_info,1,exception);
4087 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00004088 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00004089 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00004090 image_info=DestroyImageInfo(image_info);
anthony643c6132012-11-07 14:50:28 +00004091 return(MagickTrue);
cristy071dd7b2010-04-09 13:04:54 +00004092 }
anthonya322a832013-04-27 06:28:03 +00004093 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004094 }
4095 case 'R':
4096 case 'r':
4097 {
anthony2f7f9ca2012-05-14 06:33:53 +00004098 if (LocaleCompare("rendering-intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004099 {
cristybb503372010-05-27 20:51:26 +00004100 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004101 rendering_intent;
4102
cristy042ee782011-04-22 18:48:30 +00004103 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004104 value);
4105 if (rendering_intent < 0)
anthonya322a832013-04-27 06:28:03 +00004106 return(MagickFalse); /* FUTURE: value exception?? */
anthony643c6132012-11-07 14:50:28 +00004107 image->rendering_intent=(RenderingIntent) rendering_intent;
4108 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004109 }
anthonya322a832013-04-27 06:28:03 +00004110 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004111 }
anthony6e2e0732012-05-06 12:22:43 +00004112 case 'S':
4113 case 's':
anthony2f7f9ca2012-05-14 06:33:53 +00004114 if ( (LocaleCompare("size",property) == 0) ||
4115 (LocaleCompare("skewness",property) == 0) ||
4116 (LocaleCompare("scenes",property) == 0) ||
4117 (LocaleCompare("standard-deviation",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00004118 {
4119 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004120 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004121 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004122 }
anthonya322a832013-04-27 06:28:03 +00004123 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004124 case 'T':
4125 case 't':
4126 {
anthony2f7f9ca2012-05-14 06:33:53 +00004127 if (LocaleCompare("tile-offset",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004128 {
4129 char
4130 *geometry;
4131
4132 geometry=GetPageGeometry(value);
4133 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
4134 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00004135 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004136 }
anthonya322a832013-04-27 06:28:03 +00004137 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004138 }
cristyfbb56842010-08-02 11:26:33 +00004139 case 'U':
4140 case 'u':
4141 {
anthony2f7f9ca2012-05-14 06:33:53 +00004142 if (LocaleCompare("units",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00004143 {
4144 ssize_t
4145 units;
4146
cristy042ee782011-04-22 18:48:30 +00004147 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00004148 if (units < 0)
anthonya322a832013-04-27 06:28:03 +00004149 return(MagickFalse); /* FUTURE: value exception?? */
cristyfbb56842010-08-02 11:26:33 +00004150 image->units=(ResolutionType) units;
anthony643c6132012-11-07 14:50:28 +00004151 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00004152 }
anthonya322a832013-04-27 06:28:03 +00004153 break; /* not an attribute, add as a property */
cristyfbb56842010-08-02 11:26:33 +00004154 }
anthony6e2e0732012-05-06 12:22:43 +00004155 case 'V':
4156 case 'v':
anthony643c6132012-11-07 14:50:28 +00004157 {
anthony2f7f9ca2012-05-14 06:33:53 +00004158 if (LocaleCompare("version",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004159 {
4160 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004161 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004162 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004163 }
anthonya322a832013-04-27 06:28:03 +00004164 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004165 }
anthony6e2e0732012-05-06 12:22:43 +00004166 case 'W':
4167 case 'w':
anthony643c6132012-11-07 14:50:28 +00004168 {
anthony2f7f9ca2012-05-14 06:33:53 +00004169 if (LocaleCompare("width",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004170 {
4171 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004172 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004173 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004174 }
anthonya322a832013-04-27 06:28:03 +00004175 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004176 }
anthonya322a832013-04-27 06:28:03 +00004177#if 0 /* Percent escape's sets values with this prefix: for later use
4178 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004179 case 'X':
4180 case 'x':
cristy3ed852e2009-09-05 21:47:34 +00004181 {
anthony643c6132012-11-07 14:50:28 +00004182 if (LocaleNCompare("xmp:",property,4) == 0)
4183 {
4184 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004185 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004186 return(MagickFalse);
4187 }
anthonya322a832013-04-27 06:28:03 +00004188 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004189 }
anthonyc99875a2012-11-14 05:22:17 +00004190#endif
cristy3ed852e2009-09-05 21:47:34 +00004191 }
anthonya322a832013-04-27 06:28:03 +00004192 /* Default: not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004193 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
4194 ConstantString(property),ConstantString(value));
4195 /* FUTURE: error if status is bad? */
cristy3ed852e2009-09-05 21:47:34 +00004196 return(status);
4197}