blob: 50c78bb69fc76d06fa1e94717417b5eb3dc30472 [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% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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
cristy3ed852e2009-09-05 21:47:34 +000040/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
anthony2fbb5952012-05-05 12:35:30 +000044#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/attribute.h"
46#include "MagickCore/cache.h"
cristyafcb0492013-10-15 01:11:56 +000047#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000048#include "MagickCore/color.h"
cristyd228c032012-06-03 15:01:15 +000049#include "MagickCore/color-private.h"
cristy3d9f5ba2012-06-26 13:37:31 +000050#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000051#include "MagickCore/compare.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/draw.h"
54#include "MagickCore/effect.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/fx-private.h"
59#include "MagickCore/gem.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/histogram.h"
62#include "MagickCore/image.h"
cristy4c08aed2011-07-01 19:47:50 +000063#include "MagickCore/layer.h"
cristy7832dc22011-09-05 01:21:53 +000064#include "MagickCore/locale-private.h"
cristy4c08aed2011-07-01 19:47:50 +000065#include "MagickCore/list.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/montage.h"
70#include "MagickCore/option.h"
71#include "MagickCore/profile.h"
72#include "MagickCore/property.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/resource_.h"
75#include "MagickCore/splay-tree.h"
cristy7832dc22011-09-05 01:21:53 +000076#include "MagickCore/signature.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/statistic.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/token.h"
cristy7832dc22011-09-05 01:21:53 +000081#include "MagickCore/token-private.h"
cristy4c08aed2011-07-01 19:47:50 +000082#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000083#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000084#include "MagickCore/version.h"
85#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000086#include "MagickCore/xml-tree-private.h"
dirk6015d442013-10-19 15:07:40 +000087#if defined(MAGICKCORE_LCMS_DELEGATE)
cristyd6ac47f2015-06-06 13:46:55 +000088#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
89#include <lcms/lcms2.h>
90#elif defined(MAGICKCORE_HAVE_LCMS2_H)
cristyb054e072015-06-05 20:35:43 +000091#include "lcms2.h"
cristyd6ac47f2015-06-06 13:46:55 +000092#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
93#include <lcms/lcms.h>
94#else
95#include "lcms.h"
dirk6015d442013-10-19 15:07:40 +000096#endif
97#endif
cristyf516f2f2014-01-07 01:29:28 +000098
cristy3ed852e2009-09-05 21:47:34 +000099/*
cristyd57d5632014-01-23 13:28:07 +0000100 Define declarations.
101*/
102#if defined(MAGICKCORE_LCMS_DELEGATE)
103#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
cristyfc57e8c2014-01-29 15:50:18 +0000104#define cmsUInt32Number DWORD
cristyd57d5632014-01-23 13:28:07 +0000105#endif
106#endif
107
108/*
cristy3ed852e2009-09-05 21:47:34 +0000109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110% %
111% %
112% %
113% C l o n e I m a g e P r o p e r t i e s %
114% %
115% %
116% %
117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118%
anthonyd2f39e02012-08-20 12:29:28 +0000119% CloneImageProperties() clones all the image properties to another image.
cristy3ed852e2009-09-05 21:47:34 +0000120%
121% The format of the CloneImageProperties method is:
122%
123% MagickBooleanType CloneImageProperties(Image *image,
124% const Image *clone_image)
125%
126% A description of each parameter follows:
127%
128% o image: the image.
129%
130% o clone_image: the clone image.
131%
132*/
133MagickExport MagickBooleanType CloneImageProperties(Image *image,
134 const Image *clone_image)
135{
136 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000137 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +0000138 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000139 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
140 assert(clone_image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000141 assert(clone_image->signature == MagickCoreSignature);
anthony2fbb5952012-05-05 12:35:30 +0000142 if( IfMagickTrue(clone_image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
144 clone_image->filename);
cristy151b66d2015-04-15 10:50:31 +0000145 (void) CopyMagickString(image->filename,clone_image->filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000146 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
cristy151b66d2015-04-15 10:50:31 +0000147 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000148 image->compression=clone_image->compression;
149 image->quality=clone_image->quality;
150 image->depth=clone_image->depth;
151 image->background_color=clone_image->background_color;
152 image->border_color=clone_image->border_color;
153 image->matte_color=clone_image->matte_color;
154 image->transparent_color=clone_image->transparent_color;
155 image->gamma=clone_image->gamma;
156 image->chromaticity=clone_image->chromaticity;
157 image->rendering_intent=clone_image->rendering_intent;
158 image->black_point_compensation=clone_image->black_point_compensation;
159 image->units=clone_image->units;
160 image->montage=(char *) NULL;
161 image->directory=(char *) NULL;
162 (void) CloneString(&image->geometry,clone_image->geometry);
163 image->offset=clone_image->offset;
cristy2a11bef2011-10-28 18:33:11 +0000164 image->resolution.x=clone_image->resolution.x;
165 image->resolution.y=clone_image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +0000166 image->page=clone_image->page;
167 image->tile_offset=clone_image->tile_offset;
168 image->extract_info=clone_image->extract_info;
cristy3ed852e2009-09-05 21:47:34 +0000169 image->filter=clone_image->filter;
cristy3ed852e2009-09-05 21:47:34 +0000170 image->fuzz=clone_image->fuzz;
cristyff848fa2013-06-10 23:34:40 +0000171 image->intensity=clone_image->intensity;
cristy3ed852e2009-09-05 21:47:34 +0000172 image->interlace=clone_image->interlace;
173 image->interpolate=clone_image->interpolate;
174 image->endian=clone_image->endian;
175 image->gravity=clone_image->gravity;
176 image->compose=clone_image->compose;
cristy3ed852e2009-09-05 21:47:34 +0000177 image->orientation=clone_image->orientation;
cristy11695122014-06-09 12:29:50 +0000178 image->scene=clone_image->scene;
cristy3ed852e2009-09-05 21:47:34 +0000179 image->dispose=clone_image->dispose;
180 image->delay=clone_image->delay;
181 image->ticks_per_second=clone_image->ticks_per_second;
182 image->iterations=clone_image->iterations;
183 image->total_colors=clone_image->total_colors;
184 image->taint=clone_image->taint;
185 image->progress_monitor=clone_image->progress_monitor;
186 image->client_data=clone_image->client_data;
187 image->start_loop=clone_image->start_loop;
188 image->error=clone_image->error;
189 image->signature=clone_image->signature;
190 if (clone_image->properties != (void *) NULL)
191 {
192 if (image->properties != (void *) NULL)
193 DestroyImageProperties(image);
194 image->properties=CloneSplayTree((SplayTreeInfo *)
195 clone_image->properties,(void *(*)(void *)) ConstantString,
196 (void *(*)(void *)) ConstantString);
197 }
198 return(MagickTrue);
199}
cristyf516f2f2014-01-07 01:29:28 +0000200
cristy3ed852e2009-09-05 21:47:34 +0000201/*
202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203% %
204% %
205% %
206% D e f i n e I m a g e P r o p e r t y %
207% %
208% %
209% %
210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211%
anthonyd2f39e02012-08-20 12:29:28 +0000212% DefineImageProperty() associates an assignment string of the form
anthonya322a832013-04-27 06:28:03 +0000213% "key=value" with an artifact or options. It is equivelent to
214% SetImageProperty()
cristy3ed852e2009-09-05 21:47:34 +0000215%
216% The format of the DefineImageProperty method is:
217%
cristy31197622013-03-06 02:07:54 +0000218% MagickBooleanType DefineImageProperty(Image *image,const char *property,
219% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000220%
221% A description of each parameter follows:
222%
223% o image: the image.
224%
225% o property: the image property.
226%
cristyd15e6592011-10-15 00:13:06 +0000227% o exception: return any errors or warnings in this structure.
228%
cristy3ed852e2009-09-05 21:47:34 +0000229*/
230MagickExport MagickBooleanType DefineImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000231 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000232{
233 char
cristy151b66d2015-04-15 10:50:31 +0000234 key[MagickPathExtent],
235 value[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000236
237 register char
238 *p;
239
240 assert(image != (Image *) NULL);
241 assert(property != (const char *) NULL);
cristy151b66d2015-04-15 10:50:31 +0000242 (void) CopyMagickString(key,property,MagickPathExtent-1);
cristy3ed852e2009-09-05 21:47:34 +0000243 for (p=key; *p != '\0'; p++)
244 if (*p == '=')
245 break;
246 *value='\0';
247 if (*p == '=')
cristy151b66d2015-04-15 10:50:31 +0000248 (void) CopyMagickString(value,p+1,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000249 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000250 return(SetImageProperty(image,key,value,exception));
cristy3ed852e2009-09-05 21:47:34 +0000251}
cristyf516f2f2014-01-07 01:29:28 +0000252
cristy3ed852e2009-09-05 21:47:34 +0000253/*
254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255% %
256% %
257% %
258% D e l e t e I m a g e P r o p e r t y %
259% %
260% %
261% %
262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263%
264% DeleteImageProperty() deletes an image property.
265%
266% The format of the DeleteImageProperty method is:
267%
268% MagickBooleanType DeleteImageProperty(Image *image,const char *property)
269%
270% A description of each parameter follows:
271%
272% o image: the image.
273%
274% o property: the image property.
275%
276*/
277MagickExport MagickBooleanType DeleteImageProperty(Image *image,
278 const char *property)
279{
280 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000281 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +0000282 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000283 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
284 image->filename);
285 if (image->properties == (void *) NULL)
286 return(MagickFalse);
287 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
288}
cristyf516f2f2014-01-07 01:29:28 +0000289
cristy3ed852e2009-09-05 21:47:34 +0000290/*
291%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
292% %
293% %
294% %
295% D e s t r o y I m a g e P r o p e r t i e s %
296% %
297% %
298% %
299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300%
anthony643c6132012-11-07 14:50:28 +0000301% DestroyImageProperties() destroys all properties and associated memory
anthonyd2f39e02012-08-20 12:29:28 +0000302% attached to the given image.
cristy3ed852e2009-09-05 21:47:34 +0000303%
304% The format of the DestroyDefines method is:
305%
306% void DestroyImageProperties(Image *image)
307%
308% A description of each parameter follows:
309%
310% o image: the image.
311%
312*/
313MagickExport void DestroyImageProperties(Image *image)
314{
315 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000316 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +0000317 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000318 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
319 image->filename);
320 if (image->properties != (void *) NULL)
321 image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
322 image->properties);
323}
cristyf516f2f2014-01-07 01:29:28 +0000324
cristy3ed852e2009-09-05 21:47:34 +0000325/*
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327% %
328% %
329% %
330% F o r m a t I m a g e P r o p e r t y %
331% %
332% %
333% %
334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335%
336% FormatImageProperty() permits formatted property/value pairs to be saved as
glennrp2cc891a2010-12-24 13:44:32 +0000337% an image property.
cristy3ed852e2009-09-05 21:47:34 +0000338%
339% The format of the FormatImageProperty method is:
340%
341% MagickBooleanType FormatImageProperty(Image *image,const char *property,
342% const char *format,...)
343%
344% A description of each parameter follows.
345%
346% o image: The image.
347%
348% o property: The attribute property.
349%
350% o format: A string describing the format to use to write the remaining
351% arguments.
352%
353*/
cristydb584ae2011-05-20 14:50:53 +0000354MagickExport MagickBooleanType FormatImageProperty(Image *image,
355 const char *property,const char *format,...)
cristy3ed852e2009-09-05 21:47:34 +0000356{
357 char
cristy151b66d2015-04-15 10:50:31 +0000358 value[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000359
cristyc82a27b2011-10-21 01:07:16 +0000360 ExceptionInfo
361 *exception;
362
363 MagickBooleanType
364 status;
365
cristy20ec7592011-05-29 01:28:05 +0000366 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000367 n;
368
cristy3ed852e2009-09-05 21:47:34 +0000369 va_list
370 operands;
371
372 va_start(operands,format);
cristy151b66d2015-04-15 10:50:31 +0000373 n=FormatLocaleStringList(value,MagickPathExtent,format,operands);
cristydb584ae2011-05-20 14:50:53 +0000374 (void) n;
cristy3ed852e2009-09-05 21:47:34 +0000375 va_end(operands);
cristyc82a27b2011-10-21 01:07:16 +0000376 exception=AcquireExceptionInfo();
377 status=SetImageProperty(image,property,value,exception);
378 exception=DestroyExceptionInfo(exception);
379 return(status);
cristy3ed852e2009-09-05 21:47:34 +0000380}
cristyf516f2f2014-01-07 01:29:28 +0000381
cristy3ed852e2009-09-05 21:47:34 +0000382/*
383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
384% %
385% %
386% %
387% G e t I m a g e P r o p e r t y %
388% %
389% %
390% %
391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392%
393% GetImageProperty() gets a value associated with an image property.
394%
anthonya322a832013-04-27 06:28:03 +0000395% This includes, profile prefixes, such as "exif:", "iptc:" and "8bim:"
396% It does not handle non-prifile prefixes, such as "fx:", "option:", or
397% "artifact:".
398%
399% The returned string is stored as a properity of the same name for faster
400% lookup later. It should NOT be freed by the caller.
anthonyd2f39e02012-08-20 12:29:28 +0000401%
cristy3ed852e2009-09-05 21:47:34 +0000402% The format of the GetImageProperty method is:
403%
cristyd15e6592011-10-15 00:13:06 +0000404% const char *GetImageProperty(const Image *image,const char *key,
405% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000406%
407% A description of each parameter follows:
408%
409% o image: the image.
410%
411% o key: the key.
412%
cristyd15e6592011-10-15 00:13:06 +0000413% o exception: return any errors or warnings in this structure.
414%
cristy3ed852e2009-09-05 21:47:34 +0000415*/
416
417static char
cristybb503372010-05-27 20:51:26 +0000418 *TracePSClippath(const unsigned char *,size_t,const size_t,
419 const size_t),
420 *TraceSVGClippath(const unsigned char *,size_t,const size_t,
421 const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000422
cristyd15e6592011-10-15 00:13:06 +0000423static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
424 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000425{
426 char
427 *attribute,
428 *message;
429
430 const StringInfo
431 *profile;
432
cristycee97112010-05-28 00:44:52 +0000433 long
cristy3ed852e2009-09-05 21:47:34 +0000434 count,
435 dataset,
436 record;
437
cristybb503372010-05-27 20:51:26 +0000438 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000439 i;
440
441 size_t
442 length;
443
444 profile=GetImageProfile(image,"iptc");
445 if (profile == (StringInfo *) NULL)
446 profile=GetImageProfile(image,"8bim");
447 if (profile == (StringInfo *) NULL)
448 return(MagickFalse);
449 count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
450 if (count != 2)
451 return(MagickFalse);
452 attribute=(char *) NULL;
cristybb503372010-05-27 20:51:26 +0000453 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
cristy3ed852e2009-09-05 21:47:34 +0000454 {
455 length=1;
cristybb503372010-05-27 20:51:26 +0000456 if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
cristy3ed852e2009-09-05 21:47:34 +0000457 continue;
458 length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
459 length|=GetStringInfoDatum(profile)[i+4];
cristycee97112010-05-28 00:44:52 +0000460 if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
461 ((long) GetStringInfoDatum(profile)[i+2] == record))
cristy3ed852e2009-09-05 21:47:34 +0000462 {
463 message=(char *) NULL;
464 if (~length >= 1)
465 message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
466 if (message != (char *) NULL)
467 {
468 (void) CopyMagickString(message,(char *) GetStringInfoDatum(
469 profile)+i+5,length+1);
470 (void) ConcatenateString(&attribute,message);
471 (void) ConcatenateString(&attribute,";");
472 message=DestroyString(message);
473 }
474 }
475 i+=5;
476 }
477 if ((attribute == (char *) NULL) || (*attribute == ';'))
478 {
479 if (attribute != (char *) NULL)
480 attribute=DestroyString(attribute);
481 return(MagickFalse);
482 }
483 attribute[strlen(attribute)-1]='\0';
cristyd15e6592011-10-15 00:13:06 +0000484 (void) SetImageProperty((Image *) image,key,(const char *) attribute,
485 exception);
cristy3ed852e2009-09-05 21:47:34 +0000486 attribute=DestroyString(attribute);
487 return(MagickTrue);
488}
489
cristy3ed852e2009-09-05 21:47:34 +0000490static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
491{
492 int
493 c;
494
495 if (*length < 1)
496 return(EOF);
497 c=(int) (*(*p)++);
498 (*length)--;
499 return(c);
500}
501
cristybb503372010-05-27 20:51:26 +0000502static inline size_t ReadPropertyMSBLong(const unsigned char **p,
cristy3ed852e2009-09-05 21:47:34 +0000503 size_t *length)
504{
505 int
506 c;
507
cristybb503372010-05-27 20:51:26 +0000508 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000509 i;
510
511 unsigned char
512 buffer[4];
513
cristybb503372010-05-27 20:51:26 +0000514 size_t
cristy3ed852e2009-09-05 21:47:34 +0000515 value;
516
517 if (*length < 4)
518 return(~0UL);
519 for (i=0; i < 4; i++)
520 {
521 c=(int) (*(*p)++);
522 (*length)--;
523 buffer[i]=(unsigned char) c;
524 }
cristybb503372010-05-27 20:51:26 +0000525 value=(size_t) (buffer[0] << 24);
cristy3ed852e2009-09-05 21:47:34 +0000526 value|=buffer[1] << 16;
527 value|=buffer[2] << 8;
528 value|=buffer[3];
529 return(value & 0xffffffff);
530}
531
532static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
533 size_t *length)
534{
535 int
536 c;
537
cristybb503372010-05-27 20:51:26 +0000538 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000539 i;
540
541 unsigned char
542 buffer[2];
543
544 unsigned short
545 value;
546
547 if (*length < 2)
dirk93b02b72013-11-16 16:03:36 +0000548 return((unsigned short) ~0);
cristy3ed852e2009-09-05 21:47:34 +0000549 for (i=0; i < 2; i++)
550 {
551 c=(int) (*(*p)++);
552 (*length)--;
553 buffer[i]=(unsigned char) c;
554 }
555 value=(unsigned short) (buffer[0] << 8);
556 value|=buffer[1];
557 return((unsigned short) (value & 0xffff));
558}
559
cristyd15e6592011-10-15 00:13:06 +0000560static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
561 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000562{
563 char
564 *attribute,
cristy151b66d2015-04-15 10:50:31 +0000565 format[MagickPathExtent],
566 name[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +0000567 *resource;
568
569 const StringInfo
570 *profile;
571
572 const unsigned char
573 *info;
574
cristycee97112010-05-28 00:44:52 +0000575 long
cristy3ed852e2009-09-05 21:47:34 +0000576 start,
cristycee97112010-05-28 00:44:52 +0000577 stop;
cristy3ed852e2009-09-05 21:47:34 +0000578
579 MagickBooleanType
580 status;
581
cristybb503372010-05-27 20:51:26 +0000582 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000583 i;
584
585 ssize_t
cristycee97112010-05-28 00:44:52 +0000586 count,
587 id,
588 sub_number;
cristy3ed852e2009-09-05 21:47:34 +0000589
590 size_t
591 length;
592
593 /*
glennrp2cc891a2010-12-24 13:44:32 +0000594 There are no newlines in path names, so it's safe as terminator.
cristy3ed852e2009-09-05 21:47:34 +0000595 */
596 profile=GetImageProfile(image,"8bim");
597 if (profile == (StringInfo *) NULL)
598 return(MagickFalse);
599 count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
600 format);
601 if ((count != 2) && (count != 3) && (count != 4))
602 return(MagickFalse);
603 if (count < 4)
cristy151b66d2015-04-15 10:50:31 +0000604 (void) CopyMagickString(format,"SVG",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000605 if (count < 3)
606 *name='\0';
607 sub_number=1;
608 if (*name == '#')
cristyad740052010-07-03 01:38:03 +0000609 sub_number=(ssize_t) StringToLong(&name[1]);
cristy3ed852e2009-09-05 21:47:34 +0000610 sub_number=MagickMax(sub_number,1L);
611 resource=(char *) NULL;
612 status=MagickFalse;
613 length=GetStringInfoLength(profile);
614 info=GetStringInfoDatum(profile);
anthony2fbb5952012-05-05 12:35:30 +0000615 while ((length > 0) && IfMagickFalse(status))
cristy3ed852e2009-09-05 21:47:34 +0000616 {
617 if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
618 continue;
619 if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
620 continue;
621 if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
622 continue;
623 if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
624 continue;
cristy4a8b0172012-02-03 16:39:53 +0000625 id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
cristycee97112010-05-28 00:44:52 +0000626 if (id < (ssize_t) start)
cristy3ed852e2009-09-05 21:47:34 +0000627 continue;
cristycee97112010-05-28 00:44:52 +0000628 if (id > (ssize_t) stop)
cristy3ed852e2009-09-05 21:47:34 +0000629 continue;
630 if (resource != (char *) NULL)
631 resource=DestroyString(resource);
632 count=(ssize_t) ReadPropertyByte(&info,&length);
633 if ((count != 0) && ((size_t) count <= length))
634 {
635 resource=(char *) NULL;
cristy151b66d2015-04-15 10:50:31 +0000636 if (~((size_t) count) >= (MagickPathExtent-1))
637 resource=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000638 sizeof(*resource));
639 if (resource != (char *) NULL)
640 {
cristybb503372010-05-27 20:51:26 +0000641 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000642 resource[i]=(char) ReadPropertyByte(&info,&length);
643 resource[count]='\0';
644 }
645 }
646 if ((count & 0x01) == 0)
647 (void) ReadPropertyByte(&info,&length);
cristy55a91cd2010-12-01 00:57:40 +0000648 count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
cristy3ed852e2009-09-05 21:47:34 +0000649 if ((*name != '\0') && (*name != '#'))
650 if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
651 {
652 /*
653 No name match, scroll forward and try next.
654 */
655 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000656 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000657 continue;
658 }
659 if ((*name == '#') && (sub_number != 1))
660 {
661 /*
662 No numbered match, scroll forward and try next.
663 */
664 sub_number--;
665 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000666 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000667 continue;
668 }
669 /*
670 We have the resource of interest.
671 */
672 attribute=(char *) NULL;
cristy151b66d2015-04-15 10:50:31 +0000673 if (~((size_t) count) >= (MagickPathExtent-1))
674 attribute=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000675 sizeof(*attribute));
676 if (attribute != (char *) NULL)
677 {
678 (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
679 attribute[count]='\0';
680 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000681 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000682 if ((id <= 1999) || (id >= 2999))
683 (void) SetImageProperty((Image *) image,key,(const char *)
cristyd15e6592011-10-15 00:13:06 +0000684 attribute,exception);
cristy3ed852e2009-09-05 21:47:34 +0000685 else
686 {
687 char
688 *path;
689
690 if (LocaleCompare(format,"svg") == 0)
691 path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
692 image->columns,image->rows);
693 else
694 path=TracePSClippath((unsigned char *) attribute,(size_t) count,
695 image->columns,image->rows);
cristyd15e6592011-10-15 00:13:06 +0000696 (void) SetImageProperty((Image *) image,key,(const char *) path,
697 exception);
cristy3ed852e2009-09-05 21:47:34 +0000698 path=DestroyString(path);
699 }
700 attribute=DestroyString(attribute);
701 status=MagickTrue;
702 }
703 }
704 if (resource != (char *) NULL)
705 resource=DestroyString(resource);
706 return(status);
707}
708
709static inline unsigned short ReadPropertyShort(const EndianType endian,
710 const unsigned char *buffer)
711{
712 unsigned short
713 value;
714
cristy1715f792012-11-22 16:56:12 +0000715 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +0000716 {
cristy1715f792012-11-22 16:56:12 +0000717 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
cristy3ed852e2009-09-05 21:47:34 +0000718 return((unsigned short) (value & 0xffff));
719 }
cristy1715f792012-11-22 16:56:12 +0000720 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
721 ((unsigned char *) buffer)[1]);
cristy3ed852e2009-09-05 21:47:34 +0000722 return((unsigned short) (value & 0xffff));
723}
724
cristybb503372010-05-27 20:51:26 +0000725static inline size_t ReadPropertyLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +0000726 const unsigned char *buffer)
727{
cristybb503372010-05-27 20:51:26 +0000728 size_t
cristy3ed852e2009-09-05 21:47:34 +0000729 value;
730
cristy1715f792012-11-22 16:56:12 +0000731 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +0000732 {
cristy1715f792012-11-22 16:56:12 +0000733 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
734 (buffer[1] << 8 ) | (buffer[0]));
cristybb503372010-05-27 20:51:26 +0000735 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000736 }
cristy1715f792012-11-22 16:56:12 +0000737 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
738 (buffer[2] << 8) | buffer[3]);
cristybb503372010-05-27 20:51:26 +0000739 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000740}
741
742static MagickBooleanType GetEXIFProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +0000743 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000744{
745#define MaxDirectoryStack 16
746#define EXIF_DELIMITER "\n"
747#define EXIF_NUM_FORMATS 12
748#define EXIF_FMT_BYTE 1
749#define EXIF_FMT_STRING 2
750#define EXIF_FMT_USHORT 3
751#define EXIF_FMT_ULONG 4
752#define EXIF_FMT_URATIONAL 5
753#define EXIF_FMT_SBYTE 6
754#define EXIF_FMT_UNDEFINED 7
755#define EXIF_FMT_SSHORT 8
756#define EXIF_FMT_SLONG 9
757#define EXIF_FMT_SRATIONAL 10
758#define EXIF_FMT_SINGLE 11
759#define EXIF_FMT_DOUBLE 12
760#define TAG_EXIF_OFFSET 0x8769
761#define TAG_GPS_OFFSET 0x8825
762#define TAG_INTEROP_OFFSET 0xa005
763
cristy96ea4862011-11-22 00:45:47 +0000764#define EXIFMultipleValues(size,format,arg) \
cristy3ed852e2009-09-05 21:47:34 +0000765{ \
cristybb503372010-05-27 20:51:26 +0000766 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000767 component; \
768 \
769 size_t \
770 length; \
771 \
772 unsigned char \
773 *p1; \
774 \
775 length=0; \
776 p1=p; \
777 for (component=0; component < components; component++) \
778 { \
cristy151b66d2015-04-15 10:50:31 +0000779 length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \
cristy3ed852e2009-09-05 21:47:34 +0000780 format", ",arg); \
cristy151b66d2015-04-15 10:50:31 +0000781 if (length >= (MagickPathExtent-1)) \
782 length=MagickPathExtent-1; \
cristy3ed852e2009-09-05 21:47:34 +0000783 p1+=size; \
784 } \
785 if (length > 1) \
786 buffer[length-2]='\0'; \
787 value=AcquireString(buffer); \
788}
789
cristy96ea4862011-11-22 00:45:47 +0000790#define EXIFMultipleFractions(size,format,arg1,arg2) \
cristy3ed852e2009-09-05 21:47:34 +0000791{ \
cristybb503372010-05-27 20:51:26 +0000792 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000793 component; \
794 \
795 size_t \
796 length; \
797 \
798 unsigned char \
799 *p1; \
800 \
801 length=0; \
802 p1=p; \
803 for (component=0; component < components; component++) \
804 { \
cristy151b66d2015-04-15 10:50:31 +0000805 length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \
cristyd66c1082012-03-28 18:17:37 +0000806 format", ",(arg1),(arg2)); \
cristy151b66d2015-04-15 10:50:31 +0000807 if (length >= (MagickPathExtent-1)) \
808 length=MagickPathExtent-1; \
cristy3ed852e2009-09-05 21:47:34 +0000809 p1+=size; \
810 } \
811 if (length > 1) \
812 buffer[length-2]='\0'; \
813 value=AcquireString(buffer); \
814}
815
816 typedef struct _DirectoryInfo
817 {
818 const unsigned char
819 *directory;
820
cristybb503372010-05-27 20:51:26 +0000821 size_t
cristy3d8d1f12012-02-29 01:59:28 +0000822 entry;
823
824 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000825 offset;
826 } DirectoryInfo;
827
828 typedef struct _TagInfo
829 {
cristybb503372010-05-27 20:51:26 +0000830 size_t
cristy3ed852e2009-09-05 21:47:34 +0000831 tag;
832
833 const char
834 *description;
835 } TagInfo;
836
837 static TagInfo
838 EXIFTag[] =
839 {
840 { 0x001, "exif:InteroperabilityIndex" },
841 { 0x002, "exif:InteroperabilityVersion" },
842 { 0x100, "exif:ImageWidth" },
843 { 0x101, "exif:ImageLength" },
844 { 0x102, "exif:BitsPerSample" },
845 { 0x103, "exif:Compression" },
846 { 0x106, "exif:PhotometricInterpretation" },
847 { 0x10a, "exif:FillOrder" },
848 { 0x10d, "exif:DocumentName" },
849 { 0x10e, "exif:ImageDescription" },
850 { 0x10f, "exif:Make" },
851 { 0x110, "exif:Model" },
852 { 0x111, "exif:StripOffsets" },
853 { 0x112, "exif:Orientation" },
854 { 0x115, "exif:SamplesPerPixel" },
855 { 0x116, "exif:RowsPerStrip" },
856 { 0x117, "exif:StripByteCounts" },
857 { 0x11a, "exif:XResolution" },
858 { 0x11b, "exif:YResolution" },
859 { 0x11c, "exif:PlanarConfiguration" },
860 { 0x11d, "exif:PageName" },
861 { 0x11e, "exif:XPosition" },
862 { 0x11f, "exif:YPosition" },
863 { 0x118, "exif:MinSampleValue" },
864 { 0x119, "exif:MaxSampleValue" },
865 { 0x120, "exif:FreeOffsets" },
866 { 0x121, "exif:FreeByteCounts" },
867 { 0x122, "exif:GrayResponseUnit" },
868 { 0x123, "exif:GrayResponseCurve" },
869 { 0x124, "exif:T4Options" },
870 { 0x125, "exif:T6Options" },
871 { 0x128, "exif:ResolutionUnit" },
872 { 0x12d, "exif:TransferFunction" },
873 { 0x131, "exif:Software" },
874 { 0x132, "exif:DateTime" },
875 { 0x13b, "exif:Artist" },
876 { 0x13e, "exif:WhitePoint" },
877 { 0x13f, "exif:PrimaryChromaticities" },
878 { 0x140, "exif:ColorMap" },
879 { 0x141, "exif:HalfToneHints" },
880 { 0x142, "exif:TileWidth" },
881 { 0x143, "exif:TileLength" },
882 { 0x144, "exif:TileOffsets" },
883 { 0x145, "exif:TileByteCounts" },
884 { 0x14a, "exif:SubIFD" },
885 { 0x14c, "exif:InkSet" },
886 { 0x14d, "exif:InkNames" },
887 { 0x14e, "exif:NumberOfInks" },
888 { 0x150, "exif:DotRange" },
889 { 0x151, "exif:TargetPrinter" },
890 { 0x152, "exif:ExtraSample" },
891 { 0x153, "exif:SampleFormat" },
892 { 0x154, "exif:SMinSampleValue" },
893 { 0x155, "exif:SMaxSampleValue" },
894 { 0x156, "exif:TransferRange" },
895 { 0x157, "exif:ClipPath" },
896 { 0x158, "exif:XClipPathUnits" },
897 { 0x159, "exif:YClipPathUnits" },
898 { 0x15a, "exif:Indexed" },
899 { 0x15b, "exif:JPEGTables" },
900 { 0x15f, "exif:OPIProxy" },
901 { 0x200, "exif:JPEGProc" },
902 { 0x201, "exif:JPEGInterchangeFormat" },
903 { 0x202, "exif:JPEGInterchangeFormatLength" },
904 { 0x203, "exif:JPEGRestartInterval" },
905 { 0x205, "exif:JPEGLosslessPredictors" },
906 { 0x206, "exif:JPEGPointTransforms" },
907 { 0x207, "exif:JPEGQTables" },
908 { 0x208, "exif:JPEGDCTables" },
909 { 0x209, "exif:JPEGACTables" },
910 { 0x211, "exif:YCbCrCoefficients" },
911 { 0x212, "exif:YCbCrSubSampling" },
912 { 0x213, "exif:YCbCrPositioning" },
913 { 0x214, "exif:ReferenceBlackWhite" },
914 { 0x2bc, "exif:ExtensibleMetadataPlatform" },
915 { 0x301, "exif:Gamma" },
916 { 0x302, "exif:ICCProfileDescriptor" },
917 { 0x303, "exif:SRGBRenderingIntent" },
918 { 0x320, "exif:ImageTitle" },
919 { 0x5001, "exif:ResolutionXUnit" },
920 { 0x5002, "exif:ResolutionYUnit" },
921 { 0x5003, "exif:ResolutionXLengthUnit" },
922 { 0x5004, "exif:ResolutionYLengthUnit" },
923 { 0x5005, "exif:PrintFlags" },
924 { 0x5006, "exif:PrintFlagsVersion" },
925 { 0x5007, "exif:PrintFlagsCrop" },
926 { 0x5008, "exif:PrintFlagsBleedWidth" },
927 { 0x5009, "exif:PrintFlagsBleedWidthScale" },
928 { 0x500A, "exif:HalftoneLPI" },
929 { 0x500B, "exif:HalftoneLPIUnit" },
930 { 0x500C, "exif:HalftoneDegree" },
931 { 0x500D, "exif:HalftoneShape" },
932 { 0x500E, "exif:HalftoneMisc" },
933 { 0x500F, "exif:HalftoneScreen" },
934 { 0x5010, "exif:JPEGQuality" },
935 { 0x5011, "exif:GridSize" },
936 { 0x5012, "exif:ThumbnailFormat" },
937 { 0x5013, "exif:ThumbnailWidth" },
938 { 0x5014, "exif:ThumbnailHeight" },
939 { 0x5015, "exif:ThumbnailColorDepth" },
940 { 0x5016, "exif:ThumbnailPlanes" },
941 { 0x5017, "exif:ThumbnailRawBytes" },
942 { 0x5018, "exif:ThumbnailSize" },
943 { 0x5019, "exif:ThumbnailCompressedSize" },
944 { 0x501a, "exif:ColorTransferFunction" },
945 { 0x501b, "exif:ThumbnailData" },
946 { 0x5020, "exif:ThumbnailImageWidth" },
947 { 0x5021, "exif:ThumbnailImageHeight" },
948 { 0x5022, "exif:ThumbnailBitsPerSample" },
949 { 0x5023, "exif:ThumbnailCompression" },
950 { 0x5024, "exif:ThumbnailPhotometricInterp" },
951 { 0x5025, "exif:ThumbnailImageDescription" },
952 { 0x5026, "exif:ThumbnailEquipMake" },
953 { 0x5027, "exif:ThumbnailEquipModel" },
954 { 0x5028, "exif:ThumbnailStripOffsets" },
955 { 0x5029, "exif:ThumbnailOrientation" },
956 { 0x502a, "exif:ThumbnailSamplesPerPixel" },
957 { 0x502b, "exif:ThumbnailRowsPerStrip" },
958 { 0x502c, "exif:ThumbnailStripBytesCount" },
959 { 0x502d, "exif:ThumbnailResolutionX" },
960 { 0x502e, "exif:ThumbnailResolutionY" },
961 { 0x502f, "exif:ThumbnailPlanarConfig" },
962 { 0x5030, "exif:ThumbnailResolutionUnit" },
963 { 0x5031, "exif:ThumbnailTransferFunction" },
964 { 0x5032, "exif:ThumbnailSoftwareUsed" },
965 { 0x5033, "exif:ThumbnailDateTime" },
966 { 0x5034, "exif:ThumbnailArtist" },
967 { 0x5035, "exif:ThumbnailWhitePoint" },
968 { 0x5036, "exif:ThumbnailPrimaryChromaticities" },
969 { 0x5037, "exif:ThumbnailYCbCrCoefficients" },
970 { 0x5038, "exif:ThumbnailYCbCrSubsampling" },
971 { 0x5039, "exif:ThumbnailYCbCrPositioning" },
972 { 0x503A, "exif:ThumbnailRefBlackWhite" },
973 { 0x503B, "exif:ThumbnailCopyRight" },
974 { 0x5090, "exif:LuminanceTable" },
975 { 0x5091, "exif:ChrominanceTable" },
976 { 0x5100, "exif:FrameDelay" },
977 { 0x5101, "exif:LoopCount" },
978 { 0x5110, "exif:PixelUnit" },
979 { 0x5111, "exif:PixelPerUnitX" },
980 { 0x5112, "exif:PixelPerUnitY" },
981 { 0x5113, "exif:PaletteHistogram" },
982 { 0x1000, "exif:RelatedImageFileFormat" },
983 { 0x1001, "exif:RelatedImageLength" },
984 { 0x1002, "exif:RelatedImageWidth" },
985 { 0x800d, "exif:ImageID" },
986 { 0x80e3, "exif:Matteing" },
987 { 0x80e4, "exif:DataType" },
988 { 0x80e5, "exif:ImageDepth" },
989 { 0x80e6, "exif:TileDepth" },
990 { 0x828d, "exif:CFARepeatPatternDim" },
991 { 0x828e, "exif:CFAPattern2" },
992 { 0x828f, "exif:BatteryLevel" },
993 { 0x8298, "exif:Copyright" },
994 { 0x829a, "exif:ExposureTime" },
995 { 0x829d, "exif:FNumber" },
996 { 0x83bb, "exif:IPTC/NAA" },
997 { 0x84e3, "exif:IT8RasterPadding" },
998 { 0x84e5, "exif:IT8ColorTable" },
999 { 0x8649, "exif:ImageResourceInformation" },
1000 { 0x8769, "exif:ExifOffset" },
1001 { 0x8773, "exif:InterColorProfile" },
1002 { 0x8822, "exif:ExposureProgram" },
1003 { 0x8824, "exif:SpectralSensitivity" },
1004 { 0x8825, "exif:GPSInfo" },
1005 { 0x8827, "exif:ISOSpeedRatings" },
1006 { 0x8828, "exif:OECF" },
1007 { 0x8829, "exif:Interlace" },
1008 { 0x882a, "exif:TimeZoneOffset" },
1009 { 0x882b, "exif:SelfTimerMode" },
1010 { 0x9000, "exif:ExifVersion" },
1011 { 0x9003, "exif:DateTimeOriginal" },
1012 { 0x9004, "exif:DateTimeDigitized" },
1013 { 0x9101, "exif:ComponentsConfiguration" },
1014 { 0x9102, "exif:CompressedBitsPerPixel" },
1015 { 0x9201, "exif:ShutterSpeedValue" },
1016 { 0x9202, "exif:ApertureValue" },
1017 { 0x9203, "exif:BrightnessValue" },
1018 { 0x9204, "exif:ExposureBiasValue" },
1019 { 0x9205, "exif:MaxApertureValue" },
1020 { 0x9206, "exif:SubjectDistance" },
1021 { 0x9207, "exif:MeteringMode" },
1022 { 0x9208, "exif:LightSource" },
1023 { 0x9209, "exif:Flash" },
1024 { 0x920a, "exif:FocalLength" },
1025 { 0x920b, "exif:FlashEnergy" },
1026 { 0x920c, "exif:SpatialFrequencyResponse" },
1027 { 0x920d, "exif:Noise" },
1028 { 0x9211, "exif:ImageNumber" },
1029 { 0x9212, "exif:SecurityClassification" },
1030 { 0x9213, "exif:ImageHistory" },
1031 { 0x9214, "exif:SubjectArea" },
1032 { 0x9215, "exif:ExposureIndex" },
1033 { 0x9216, "exif:TIFF-EPStandardID" },
1034 { 0x927c, "exif:MakerNote" },
1035 { 0x9C9b, "exif:WinXP-Title" },
1036 { 0x9C9c, "exif:WinXP-Comments" },
1037 { 0x9C9d, "exif:WinXP-Author" },
1038 { 0x9C9e, "exif:WinXP-Keywords" },
1039 { 0x9C9f, "exif:WinXP-Subject" },
1040 { 0x9286, "exif:UserComment" },
1041 { 0x9290, "exif:SubSecTime" },
1042 { 0x9291, "exif:SubSecTimeOriginal" },
1043 { 0x9292, "exif:SubSecTimeDigitized" },
1044 { 0xa000, "exif:FlashPixVersion" },
1045 { 0xa001, "exif:ColorSpace" },
1046 { 0xa002, "exif:ExifImageWidth" },
1047 { 0xa003, "exif:ExifImageLength" },
1048 { 0xa004, "exif:RelatedSoundFile" },
1049 { 0xa005, "exif:InteroperabilityOffset" },
1050 { 0xa20b, "exif:FlashEnergy" },
1051 { 0xa20c, "exif:SpatialFrequencyResponse" },
1052 { 0xa20d, "exif:Noise" },
1053 { 0xa20e, "exif:FocalPlaneXResolution" },
1054 { 0xa20f, "exif:FocalPlaneYResolution" },
1055 { 0xa210, "exif:FocalPlaneResolutionUnit" },
1056 { 0xa214, "exif:SubjectLocation" },
1057 { 0xa215, "exif:ExposureIndex" },
1058 { 0xa216, "exif:TIFF/EPStandardID" },
1059 { 0xa217, "exif:SensingMethod" },
1060 { 0xa300, "exif:FileSource" },
1061 { 0xa301, "exif:SceneType" },
1062 { 0xa302, "exif:CFAPattern" },
1063 { 0xa401, "exif:CustomRendered" },
1064 { 0xa402, "exif:ExposureMode" },
1065 { 0xa403, "exif:WhiteBalance" },
1066 { 0xa404, "exif:DigitalZoomRatio" },
1067 { 0xa405, "exif:FocalLengthIn35mmFilm" },
1068 { 0xa406, "exif:SceneCaptureType" },
1069 { 0xa407, "exif:GainControl" },
1070 { 0xa408, "exif:Contrast" },
1071 { 0xa409, "exif:Saturation" },
1072 { 0xa40a, "exif:Sharpness" },
1073 { 0xa40b, "exif:DeviceSettingDescription" },
1074 { 0xa40c, "exif:SubjectDistanceRange" },
1075 { 0xa420, "exif:ImageUniqueID" },
1076 { 0xc4a5, "exif:PrintImageMatching" },
cristyb3fea0e2009-11-28 01:46:20 +00001077 { 0xa500, "exif:Gamma" },
1078 { 0xc640, "exif:CR2Slice" },
cristy3ed852e2009-09-05 21:47:34 +00001079 { 0x10000, "exif:GPSVersionID" },
1080 { 0x10001, "exif:GPSLatitudeRef" },
1081 { 0x10002, "exif:GPSLatitude" },
1082 { 0x10003, "exif:GPSLongitudeRef" },
1083 { 0x10004, "exif:GPSLongitude" },
1084 { 0x10005, "exif:GPSAltitudeRef" },
1085 { 0x10006, "exif:GPSAltitude" },
1086 { 0x10007, "exif:GPSTimeStamp" },
1087 { 0x10008, "exif:GPSSatellites" },
1088 { 0x10009, "exif:GPSStatus" },
1089 { 0x1000a, "exif:GPSMeasureMode" },
1090 { 0x1000b, "exif:GPSDop" },
1091 { 0x1000c, "exif:GPSSpeedRef" },
1092 { 0x1000d, "exif:GPSSpeed" },
1093 { 0x1000e, "exif:GPSTrackRef" },
1094 { 0x1000f, "exif:GPSTrack" },
1095 { 0x10010, "exif:GPSImgDirectionRef" },
1096 { 0x10011, "exif:GPSImgDirection" },
1097 { 0x10012, "exif:GPSMapDatum" },
1098 { 0x10013, "exif:GPSDestLatitudeRef" },
1099 { 0x10014, "exif:GPSDestLatitude" },
1100 { 0x10015, "exif:GPSDestLongitudeRef" },
1101 { 0x10016, "exif:GPSDestLongitude" },
1102 { 0x10017, "exif:GPSDestBearingRef" },
1103 { 0x10018, "exif:GPSDestBearing" },
1104 { 0x10019, "exif:GPSDestDistanceRef" },
1105 { 0x1001a, "exif:GPSDestDistance" },
1106 { 0x1001b, "exif:GPSProcessingMethod" },
1107 { 0x1001c, "exif:GPSAreaInformation" },
1108 { 0x1001d, "exif:GPSDateStamp" },
1109 { 0x1001e, "exif:GPSDifferential" },
cristy6e617e62012-09-29 14:12:23 +00001110 { 0x00000, (const char *) NULL }
cristy3ed852e2009-09-05 21:47:34 +00001111 };
1112
1113 const StringInfo
1114 *profile;
1115
1116 const unsigned char
1117 *directory,
1118 *exif;
1119
1120 DirectoryInfo
1121 directory_stack[MaxDirectoryStack];
1122
1123 EndianType
1124 endian;
1125
cristy929ea322011-02-21 15:21:35 +00001126 MagickBooleanType
1127 status;
cristy3ed852e2009-09-05 21:47:34 +00001128
cristybb503372010-05-27 20:51:26 +00001129 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001130 i;
1131
1132 size_t
cristy3ed852e2009-09-05 21:47:34 +00001133 entry,
cristy929ea322011-02-21 15:21:35 +00001134 length,
cristy3ed852e2009-09-05 21:47:34 +00001135 number_entries,
cristy3ed852e2009-09-05 21:47:34 +00001136 tag;
1137
cristy142ab012012-02-02 02:26:18 +00001138 SplayTreeInfo
1139 *exif_resources;
1140
cristy929ea322011-02-21 15:21:35 +00001141 ssize_t
1142 all,
1143 id,
1144 level,
1145 offset,
cristy3d8d1f12012-02-29 01:59:28 +00001146 tag_offset,
cristy929ea322011-02-21 15:21:35 +00001147 tag_value;
1148
1149 static int
1150 tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1151
cristy3ed852e2009-09-05 21:47:34 +00001152 /*
1153 If EXIF data exists, then try to parse the request for a tag.
1154 */
1155 profile=GetImageProfile(image,"exif");
cristyd5fc5312015-02-19 12:28:37 +00001156 if (profile == (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001157 return(MagickFalse);
1158 if ((property == (const char *) NULL) || (*property == '\0'))
1159 return(MagickFalse);
1160 while (isspace((int) ((unsigned char) *property)) != 0)
1161 property++;
cristyd5fc5312015-02-19 12:28:37 +00001162 if (strlen(property) <= 5)
1163 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001164 all=0;
1165 tag=(~0UL);
1166 switch (*(property+5))
1167 {
1168 case '*':
1169 {
1170 /*
1171 Caller has asked for all the tags in the EXIF data.
1172 */
1173 tag=0;
1174 all=1; /* return the data in description=value format */
1175 break;
1176 }
1177 case '!':
1178 {
1179 tag=0;
1180 all=2; /* return the data in tagid=value format */
1181 break;
1182 }
1183 case '#':
1184 case '@':
1185 {
1186 int
1187 c;
1188
1189 size_t
1190 n;
1191
1192 /*
1193 Check for a hex based tag specification first.
1194 */
1195 tag=(*(property+5) == '@') ? 1UL : 0UL;
1196 property+=6;
1197 n=strlen(property);
1198 if (n != 4)
1199 return(MagickFalse);
1200 /*
1201 Parse tag specification as a hex number.
1202 */
1203 n/=4;
1204 do
1205 {
cristybb503372010-05-27 20:51:26 +00001206 for (i=(ssize_t) n-1L; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +00001207 {
1208 c=(*property++);
1209 tag<<=4;
1210 if ((c >= '0') && (c <= '9'))
1211 tag|=(c-'0');
1212 else
1213 if ((c >= 'A') && (c <= 'F'))
1214 tag|=(c-('A'-10));
1215 else
1216 if ((c >= 'a') && (c <= 'f'))
1217 tag|=(c-('a'-10));
1218 else
1219 return(MagickFalse);
1220 }
1221 } while (*property != '\0');
1222 break;
1223 }
1224 default:
1225 {
1226 /*
1227 Try to match the text with a tag name instead.
1228 */
1229 for (i=0; ; i++)
1230 {
1231 if (EXIFTag[i].tag == 0)
1232 break;
1233 if (LocaleCompare(EXIFTag[i].description,property) == 0)
1234 {
cristybb503372010-05-27 20:51:26 +00001235 tag=(size_t) EXIFTag[i].tag;
cristy3ed852e2009-09-05 21:47:34 +00001236 break;
1237 }
1238 }
1239 break;
1240 }
1241 }
1242 if (tag == (~0UL))
1243 return(MagickFalse);
1244 length=GetStringInfoLength(profile);
1245 exif=GetStringInfoDatum(profile);
1246 while (length != 0)
1247 {
1248 if (ReadPropertyByte(&exif,&length) != 0x45)
1249 continue;
1250 if (ReadPropertyByte(&exif,&length) != 0x78)
1251 continue;
1252 if (ReadPropertyByte(&exif,&length) != 0x69)
1253 continue;
1254 if (ReadPropertyByte(&exif,&length) != 0x66)
1255 continue;
1256 if (ReadPropertyByte(&exif,&length) != 0x00)
1257 continue;
1258 if (ReadPropertyByte(&exif,&length) != 0x00)
1259 continue;
1260 break;
1261 }
1262 if (length < 16)
1263 return(MagickFalse);
cristyf0696672012-02-01 23:43:06 +00001264 id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
cristy3ed852e2009-09-05 21:47:34 +00001265 endian=LSBEndian;
1266 if (id == 0x4949)
1267 endian=LSBEndian;
1268 else
1269 if (id == 0x4D4D)
1270 endian=MSBEndian;
1271 else
1272 return(MagickFalse);
1273 if (ReadPropertyShort(endian,exif+2) != 0x002a)
1274 return(MagickFalse);
1275 /*
1276 This the offset to the first IFD.
1277 */
cristy55a91cd2010-12-01 00:57:40 +00001278 offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
cristy64de1572012-02-29 02:44:11 +00001279 if ((offset < 0) || (size_t) offset >= length)
cristy3ed852e2009-09-05 21:47:34 +00001280 return(MagickFalse);
1281 /*
1282 Set the pointer to the first IFD and follow it were it leads.
1283 */
cristy929ea322011-02-21 15:21:35 +00001284 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001285 directory=exif+offset;
1286 level=0;
1287 entry=0;
1288 tag_offset=0;
cristy142ab012012-02-02 02:26:18 +00001289 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1290 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1291 do
cristy3ed852e2009-09-05 21:47:34 +00001292 {
1293 /*
1294 If there is anything on the stack then pop it off.
1295 */
1296 if (level > 0)
1297 {
1298 level--;
1299 directory=directory_stack[level].directory;
1300 entry=directory_stack[level].entry;
1301 tag_offset=directory_stack[level].offset;
1302 }
cristyd5fc5312015-02-19 12:28:37 +00001303 if ((directory < exif) || (directory > (exif+length-2)))
1304 break;
cristy3ed852e2009-09-05 21:47:34 +00001305 /*
1306 Determine how many entries there are in the current IFD.
1307 */
cristy4a8b0172012-02-03 16:39:53 +00001308 number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
cristy3ed852e2009-09-05 21:47:34 +00001309 for ( ; entry < number_entries; entry++)
1310 {
cristy3ed852e2009-09-05 21:47:34 +00001311 register unsigned char
1312 *p,
1313 *q;
1314
1315 size_t
anthony90ebc0d2012-04-28 08:10:02 +00001316 format;
cristy3ed852e2009-09-05 21:47:34 +00001317
cristy9d314ff2011-03-09 01:30:28 +00001318 ssize_t
anthony90ebc0d2012-04-28 08:10:02 +00001319 number_bytes,
cristy9d314ff2011-03-09 01:30:28 +00001320 components;
cristy3ed852e2009-09-05 21:47:34 +00001321
cristyf0696672012-02-01 23:43:06 +00001322 q=(unsigned char *) (directory+(12*entry)+2);
cristy142ab012012-02-02 02:26:18 +00001323 if (GetValueFromSplayTree(exif_resources,q) == q)
1324 break;
1325 (void) AddValueToSplayTree(exif_resources,q,q);
cristyf0696672012-02-01 23:43:06 +00001326 tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
1327 format=(size_t) ((int) ReadPropertyShort(endian,q+2));
cristy3ed852e2009-09-05 21:47:34 +00001328 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1329 break;
cristy55a91cd2010-12-01 00:57:40 +00001330 components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00001331 number_bytes=(size_t) components*tag_bytes[format];
cristy5f06bc12012-04-03 12:47:36 +00001332 if (number_bytes < components)
1333 break; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001334 if (number_bytes <= 4)
1335 p=q+8;
1336 else
1337 {
1338 ssize_t
1339 offset;
1340
1341 /*
1342 The directory entry contains an offset.
1343 */
cristy55a91cd2010-12-01 00:57:40 +00001344 offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
cristydca8d582014-10-29 15:53:19 +00001345 if ((offset < 0) || (size_t) offset >= length)
1346 continue;
anthony90ebc0d2012-04-28 08:10:02 +00001347 if ((ssize_t) (offset+number_bytes) < offset)
1348 continue; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001349 if ((size_t) (offset+number_bytes) > length)
1350 continue;
1351 p=(unsigned char *) (exif+offset);
1352 }
cristy90e72a02012-09-29 01:08:57 +00001353 if ((all != 0) || (tag == (size_t) tag_value))
cristy3ed852e2009-09-05 21:47:34 +00001354 {
1355 char
cristy151b66d2015-04-15 10:50:31 +00001356 buffer[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00001357 *value;
1358
cristy7ad8a132012-03-28 22:42:29 +00001359 value=(char *) NULL;
1360 *buffer='\0';
cristy3ed852e2009-09-05 21:47:34 +00001361 switch (format)
1362 {
1363 case EXIF_FMT_BYTE:
1364 case EXIF_FMT_UNDEFINED:
1365 {
cristy13adad02011-08-16 19:22:15 +00001366 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001367 break;
1368 }
1369 case EXIF_FMT_SBYTE:
1370 {
cristye8c25f92010-06-03 00:53:06 +00001371 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001372 break;
1373 }
1374 case EXIF_FMT_SSHORT:
1375 {
1376 EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
1377 break;
1378 }
1379 case EXIF_FMT_USHORT:
1380 {
1381 EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
1382 break;
1383 }
1384 case EXIF_FMT_ULONG:
1385 {
cristye8c25f92010-06-03 00:53:06 +00001386 EXIFMultipleValues(4,"%.20g",(double)
cristy4a8b0172012-02-03 16:39:53 +00001387 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001388 break;
1389 }
1390 case EXIF_FMT_SLONG:
1391 {
cristye8c25f92010-06-03 00:53:06 +00001392 EXIFMultipleValues(4,"%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001393 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001394 break;
1395 }
1396 case EXIF_FMT_URATIONAL:
1397 {
cristye8c25f92010-06-03 00:53:06 +00001398 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristyf0696672012-02-01 23:43:06 +00001399 ((int) ReadPropertyLong(endian,p1)),(double)
1400 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001401 break;
1402 }
1403 case EXIF_FMT_SRATIONAL:
1404 {
cristye8c25f92010-06-03 00:53:06 +00001405 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001406 ((int) ReadPropertyLong(endian,p1)),(double)
1407 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001408 break;
1409 }
1410 case EXIF_FMT_SINGLE:
1411 {
1412 EXIFMultipleValues(4,"%f",(double) *(float *) p1);
1413 break;
1414 }
1415 case EXIF_FMT_DOUBLE:
1416 {
1417 EXIFMultipleValues(8,"%f",*(double *) p1);
1418 break;
1419 }
1420 default:
1421 case EXIF_FMT_STRING:
1422 {
1423 value=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +00001424 if (~((size_t) number_bytes) >= 1)
cristy3ed852e2009-09-05 21:47:34 +00001425 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1426 sizeof(*value));
1427 if (value != (char *) NULL)
1428 {
cristybb503372010-05-27 20:51:26 +00001429 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001430 i;
1431
cristybb503372010-05-27 20:51:26 +00001432 for (i=0; i < (ssize_t) number_bytes; i++)
cristy3ed852e2009-09-05 21:47:34 +00001433 {
1434 value[i]='.';
1435 if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1436 value[i]=(char) p[i];
1437 }
1438 value[i]='\0';
1439 }
1440 break;
1441 }
1442 }
1443 if (value != (char *) NULL)
1444 {
1445 char
cristy6e617e62012-09-29 14:12:23 +00001446 *key;
cristy3ed852e2009-09-05 21:47:34 +00001447
1448 register const char
1449 *p;
1450
cristy6e617e62012-09-29 14:12:23 +00001451 key=AcquireString(property);
cristy3ed852e2009-09-05 21:47:34 +00001452 switch (all)
1453 {
1454 case 1:
1455 {
1456 const char
1457 *description;
1458
cristybb503372010-05-27 20:51:26 +00001459 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001460 i;
1461
1462 description="unknown";
1463 for (i=0; ; i++)
1464 {
1465 if (EXIFTag[i].tag == 0)
1466 break;
cristybb503372010-05-27 20:51:26 +00001467 if ((ssize_t) EXIFTag[i].tag == tag_value)
cristy3ed852e2009-09-05 21:47:34 +00001468 {
1469 description=EXIFTag[i].description;
1470 break;
1471 }
1472 }
cristy151b66d2015-04-15 10:50:31 +00001473 (void) FormatLocaleString(key,MagickPathExtent,"%s",description);
dirke1c4ce42014-12-30 19:47:47 +00001474 if (level == 2)
1475 (void) SubstituteString(&key,"exif:","exif:thumbnail:");
cristy3ed852e2009-09-05 21:47:34 +00001476 break;
1477 }
1478 case 2:
1479 {
1480 if (tag_value < 0x10000)
cristy151b66d2015-04-15 10:50:31 +00001481 (void) FormatLocaleString(key,MagickPathExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001482 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001483 else
1484 if (tag_value < 0x20000)
cristy151b66d2015-04-15 10:50:31 +00001485 (void) FormatLocaleString(key,MagickPathExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001486 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001487 else
cristy151b66d2015-04-15 10:50:31 +00001488 (void) FormatLocaleString(key,MagickPathExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001489 break;
1490 }
dirke1c4ce42014-12-30 19:47:47 +00001491 default:
1492 {
1493 if (level == 2)
1494 (void) SubstituteString(&key,"exif:","exif:thumbnail:");
1495 }
cristy3ed852e2009-09-05 21:47:34 +00001496 }
1497 p=(const char *) NULL;
1498 if (image->properties != (void *) NULL)
1499 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1500 image->properties,key);
1501 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001502 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001503 value=DestroyString(value);
cristy6e617e62012-09-29 14:12:23 +00001504 key=DestroyString(key);
cristy929ea322011-02-21 15:21:35 +00001505 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001506 }
1507 }
1508 if ((tag_value == TAG_EXIF_OFFSET) ||
cristy4a8b0172012-02-03 16:39:53 +00001509 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
cristy3ed852e2009-09-05 21:47:34 +00001510 {
cristy3d8d1f12012-02-29 01:59:28 +00001511 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001512 offset;
1513
cristy3d8d1f12012-02-29 01:59:28 +00001514 offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
1515 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00001516 {
cristy3d8d1f12012-02-29 01:59:28 +00001517 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001518 tag_offset1;
1519
cristy3d8d1f12012-02-29 01:59:28 +00001520 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1521 0);
cristy3ed852e2009-09-05 21:47:34 +00001522 directory_stack[level].directory=directory;
1523 entry++;
1524 directory_stack[level].entry=entry;
1525 directory_stack[level].offset=tag_offset;
1526 level++;
1527 directory_stack[level].directory=exif+offset;
1528 directory_stack[level].offset=tag_offset1;
1529 directory_stack[level].entry=0;
1530 level++;
1531 if ((directory+2+(12*number_entries)) > (exif+length))
1532 break;
cristy3d8d1f12012-02-29 01:59:28 +00001533 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
cristy4a8b0172012-02-03 16:39:53 +00001534 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00001535 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00001536 (level < (MaxDirectoryStack-2)))
1537 {
1538 directory_stack[level].directory=exif+offset;
1539 directory_stack[level].entry=0;
1540 directory_stack[level].offset=tag_offset1;
1541 level++;
1542 }
1543 }
1544 break;
1545 }
1546 }
cristy142ab012012-02-02 02:26:18 +00001547 } while (level > 0);
1548 exif_resources=DestroySplayTree(exif_resources);
cristy929ea322011-02-21 15:21:35 +00001549 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001550}
1551
cristy920a0e72014-01-24 14:20:14 +00001552static MagickBooleanType GetICCProperty(const Image *image,const char *property,
cristy55166322014-01-23 01:53:24 +00001553 ExceptionInfo *exception)
1554{
1555 const StringInfo
1556 *profile;
1557
cristy920a0e72014-01-24 14:20:14 +00001558 magick_unreferenced(property);
cristy65c88e92014-01-24 14:15:27 +00001559
cristy55166322014-01-23 01:53:24 +00001560 profile=GetImageProfile(image,"icc");
1561 if (profile == (StringInfo *) NULL)
1562 profile=GetImageProfile(image,"icm");
1563 if (profile == (StringInfo *) NULL)
1564 return(MagickFalse);
cristy89da59a2014-02-02 19:54:09 +00001565 if (GetStringInfoLength(profile) < 128)
1566 return(MagickFalse); /* minimum ICC profile length */
cristy55166322014-01-23 01:53:24 +00001567#if defined(MAGICKCORE_LCMS_DELEGATE)
1568 {
1569 cmsHPROFILE
1570 icc_profile;
1571
1572 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
1573 (cmsUInt32Number) GetStringInfoLength(profile));
1574 if (icc_profile != (cmsHPROFILE *) NULL)
1575 {
1576#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
1577 const char
1578 *name;
1579
1580 name=cmsTakeProductName(icc_profile);
1581 if (name != (const char *) NULL)
1582 (void) SetImageProperty((Image *) image,"icc:name",name,exception);
1583#else
1584 char
cristy151b66d2015-04-15 10:50:31 +00001585 info[MagickPathExtent];
cristy55166322014-01-23 01:53:24 +00001586
1587 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
cristy151b66d2015-04-15 10:50:31 +00001588 "en","US",info,MagickPathExtent);
cristy55166322014-01-23 01:53:24 +00001589 (void) SetImageProperty((Image *) image,"icc:description",info,
1590 exception);
1591 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,
cristy151b66d2015-04-15 10:50:31 +00001592 "en","US",info,MagickPathExtent);
cristy55166322014-01-23 01:53:24 +00001593 (void) SetImageProperty((Image *) image,"icc:manufacturer",info,
1594 exception);
1595 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en",
cristy151b66d2015-04-15 10:50:31 +00001596 "US",info,MagickPathExtent);
cristy55166322014-01-23 01:53:24 +00001597 (void) SetImageProperty((Image *) image,"icc:model",info,exception);
1598 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,
cristy151b66d2015-04-15 10:50:31 +00001599 "en","US",info,MagickPathExtent);
cristy55166322014-01-23 01:53:24 +00001600 (void) SetImageProperty((Image *) image,"icc:copyright",info,exception);
1601#endif
1602 (void) cmsCloseProfile(icc_profile);
1603 }
1604 }
1605#endif
1606 return(MagickTrue);
1607}
1608
dirk90d68962014-05-23 11:11:17 +00001609static MagickBooleanType SkipXMPValue(const char *value)
1610{
1611 if (value == (const char*) NULL)
1612 return(MagickTrue);
cristye9054192014-09-24 11:53:14 +00001613 while (*value != '\0')
dirk90d68962014-05-23 11:11:17 +00001614 {
cristye9054192014-09-24 11:53:14 +00001615 if (isspace((int) ((unsigned char) *value)) == 0)
dirk90d68962014-05-23 11:11:17 +00001616 return(MagickFalse);
1617 value++;
1618 }
dirk90d68962014-05-23 11:11:17 +00001619 return(MagickTrue);
1620}
1621
cristy13adad02011-08-16 19:22:15 +00001622static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001623{
1624 char
1625 *xmp_profile;
1626
dirk90d68962014-05-23 11:11:17 +00001627 const char
1628 *content;
1629
cristy3ed852e2009-09-05 21:47:34 +00001630 const StringInfo
1631 *profile;
1632
1633 ExceptionInfo
1634 *exception;
1635
1636 MagickBooleanType
1637 status;
1638
1639 register const char
1640 *p;
1641
1642 XMLTreeInfo
1643 *child,
1644 *description,
1645 *node,
1646 *rdf,
1647 *xmp;
1648
1649 profile=GetImageProfile(image,"xmp");
1650 if (profile == (StringInfo *) NULL)
1651 return(MagickFalse);
1652 if ((property == (const char *) NULL) || (*property == '\0'))
1653 return(MagickFalse);
1654 xmp_profile=StringInfoToString(profile);
1655 if (xmp_profile == (char *) NULL)
1656 return(MagickFalse);
1657 for (p=xmp_profile; *p != '\0'; p++)
1658 if ((*p == '<') && (*(p+1) == 'x'))
1659 break;
1660 exception=AcquireExceptionInfo();
1661 xmp=NewXMLTree((char *) p,exception);
1662 xmp_profile=DestroyString(xmp_profile);
1663 exception=DestroyExceptionInfo(exception);
1664 if (xmp == (XMLTreeInfo *) NULL)
1665 return(MagickFalse);
1666 status=MagickFalse;
1667 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1668 if (rdf != (XMLTreeInfo *) NULL)
1669 {
1670 if (image->properties == (void *) NULL)
1671 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1672 RelinquishMagickMemory,RelinquishMagickMemory);
1673 description=GetXMLTreeChild(rdf,"rdf:Description");
1674 while (description != (XMLTreeInfo *) NULL)
1675 {
1676 node=GetXMLTreeChild(description,(const char *) NULL);
1677 while (node != (XMLTreeInfo *) NULL)
1678 {
1679 child=GetXMLTreeChild(node,(const char *) NULL);
dirk90d68962014-05-23 11:11:17 +00001680 content=GetXMLTreeContent(node);
cristy9745ca82014-05-23 20:10:13 +00001681 if ((child == (XMLTreeInfo *) NULL) &&
1682 (SkipXMPValue(content) == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001683 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
cristy9745ca82014-05-23 20:10:13 +00001684 ConstantString(GetXMLTreeTag(node)),ConstantString(content));
cristy3ed852e2009-09-05 21:47:34 +00001685 while (child != (XMLTreeInfo *) NULL)
1686 {
dirk90d68962014-05-23 11:11:17 +00001687 content=GetXMLTreeContent(child);
1688 if (SkipXMPValue(content) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001689 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
cristy9745ca82014-05-23 20:10:13 +00001690 ConstantString(GetXMLTreeTag(child)),ConstantString(content));
cristy3ed852e2009-09-05 21:47:34 +00001691 child=GetXMLTreeSibling(child);
1692 }
1693 node=GetXMLTreeSibling(node);
1694 }
1695 description=GetNextXMLTreeTag(description);
1696 }
1697 }
1698 xmp=DestroyXMLTree(xmp);
1699 return(status);
1700}
1701
1702static char *TracePSClippath(const unsigned char *blob,size_t length,
cristyf5d0a1a2012-02-03 12:33:10 +00001703 const size_t magick_unused(columns),const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001704{
1705 char
1706 *path,
1707 *message;
1708
cristy3ed852e2009-09-05 21:47:34 +00001709 MagickBooleanType
1710 in_subpath;
1711
1712 PointInfo
1713 first[3],
1714 last[3],
1715 point[3];
1716
cristybb503372010-05-27 20:51:26 +00001717 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001718 i,
1719 x;
1720
cristy9d314ff2011-03-09 01:30:28 +00001721 ssize_t
1722 knot_count,
1723 selector,
1724 y;
1725
cristy3ed852e2009-09-05 21:47:34 +00001726 path=AcquireString((char *) NULL);
1727 if (path == (char *) NULL)
1728 return((char *) NULL);
1729 message=AcquireString((char *) NULL);
cristy151b66d2015-04-15 10:50:31 +00001730 (void) FormatLocaleString(message,MagickPathExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001731 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001732 (void) FormatLocaleString(message,MagickPathExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001733 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001734 (void) FormatLocaleString(message,MagickPathExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001735 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001736 (void) FormatLocaleString(message,MagickPathExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001737 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001738 (void) FormatLocaleString(message,MagickPathExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001739 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001740 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001741 " /v {currentpoint 6 2 roll curveto} bind def\n");
1742 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001743 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001744 " /y {2 copy curveto} bind def\n");
1745 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001746 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001747 " /z {closepath} bind def\n");
1748 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001749 (void) FormatLocaleString(message,MagickPathExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001750 (void) ConcatenateString(&path,message);
1751 /*
1752 The clipping path format is defined in "Adobe Photoshop File
1753 Formats Specification" version 6.0 downloadable from adobe.com.
1754 */
1755 (void) ResetMagickMemory(point,0,sizeof(point));
1756 (void) ResetMagickMemory(first,0,sizeof(first));
1757 (void) ResetMagickMemory(last,0,sizeof(last));
1758 knot_count=0;
1759 in_subpath=MagickFalse;
1760 while (length > 0)
1761 {
cristy4a8b0172012-02-03 16:39:53 +00001762 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001763 switch (selector)
1764 {
1765 case 0:
1766 case 3:
1767 {
1768 if (knot_count != 0)
1769 {
1770 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001771 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001772 break;
1773 }
1774 /*
1775 Expected subpath length record.
1776 */
cristy4a8b0172012-02-03 16:39:53 +00001777 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001778 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001779 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001780 break;
1781 }
1782 case 1:
1783 case 2:
1784 case 4:
1785 case 5:
1786 {
1787 if (knot_count == 0)
1788 {
1789 /*
1790 Unexpected subpath knot
1791 */
1792 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001793 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001794 break;
1795 }
1796 /*
1797 Add sub-path knot
1798 */
1799 for (i=0; i < 3; i++)
1800 {
cristy13b9a2e2010-11-10 14:03:51 +00001801 size_t
cristy97433202009-10-27 02:05:08 +00001802 xx,
1803 yy;
1804
cristy4a8b0172012-02-03 16:39:53 +00001805 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1806 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001807 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001808 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001809 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001810 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001811 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001812 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001813 point[i].x=(double) x/4096/4096;
1814 point[i].y=1.0-(double) y/4096/4096;
1815 }
anthony2fbb5952012-05-05 12:35:30 +00001816 if( IfMagickFalse(in_subpath) )
cristy3ed852e2009-09-05 21:47:34 +00001817 {
cristy151b66d2015-04-15 10:50:31 +00001818 (void) FormatLocaleString(message,MagickPathExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001819 point[1].x,point[1].y);
1820 for (i=0; i < 3; i++)
1821 {
1822 first[i]=point[i];
1823 last[i]=point[i];
1824 }
1825 }
1826 else
1827 {
1828 /*
1829 Handle special cases when Bezier curves are used to describe
1830 corners and straight lines.
1831 */
1832 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1833 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristy151b66d2015-04-15 10:50:31 +00001834 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001835 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001836 else
1837 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristy151b66d2015-04-15 10:50:31 +00001838 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001839 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001840 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001841 else
1842 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristy151b66d2015-04-15 10:50:31 +00001843 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001844 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001845 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001846 else
cristy151b66d2015-04-15 10:50:31 +00001847 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001848 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001849 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001850 for (i=0; i < 3; i++)
1851 last[i]=point[i];
1852 }
1853 (void) ConcatenateString(&path,message);
1854 in_subpath=MagickTrue;
1855 knot_count--;
1856 /*
1857 Close the subpath if there are no more knots.
1858 */
1859 if (knot_count == 0)
1860 {
1861 /*
1862 Same special handling as above except we compare to the
1863 first point in the path and close the path.
1864 */
1865 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1866 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristy151b66d2015-04-15 10:50:31 +00001867 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001868 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001869 else
1870 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristy151b66d2015-04-15 10:50:31 +00001871 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001872 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001873 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001874 else
1875 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristy151b66d2015-04-15 10:50:31 +00001876 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001877 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001878 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001879 else
cristy151b66d2015-04-15 10:50:31 +00001880 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001881 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001882 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001883 (void) ConcatenateString(&path,message);
1884 in_subpath=MagickFalse;
1885 }
1886 break;
1887 }
1888 case 6:
1889 case 7:
1890 case 8:
1891 default:
1892 {
1893 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001894 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001895 break;
1896 }
1897 }
1898 }
1899 /*
1900 Returns an empty PS path if the path has no knots.
1901 */
cristy151b66d2015-04-15 10:50:31 +00001902 (void) FormatLocaleString(message,MagickPathExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001903 (void) ConcatenateString(&path,message);
cristy151b66d2015-04-15 10:50:31 +00001904 (void) FormatLocaleString(message,MagickPathExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001905 (void) ConcatenateString(&path,message);
1906 message=DestroyString(message);
1907 return(path);
1908}
1909
1910static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001911 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001912{
1913 char
1914 *path,
1915 *message;
1916
cristy3ed852e2009-09-05 21:47:34 +00001917 MagickBooleanType
1918 in_subpath;
1919
1920 PointInfo
1921 first[3],
1922 last[3],
1923 point[3];
1924
cristybb503372010-05-27 20:51:26 +00001925 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001926 i;
1927
cristy9d314ff2011-03-09 01:30:28 +00001928 ssize_t
1929 knot_count,
1930 selector,
1931 x,
1932 y;
1933
cristy3ed852e2009-09-05 21:47:34 +00001934 path=AcquireString((char *) NULL);
1935 if (path == (char *) NULL)
1936 return((char *) NULL);
1937 message=AcquireString((char *) NULL);
cristy151b66d2015-04-15 10:50:31 +00001938 (void) FormatLocaleString(message,MagickPathExtent,
dirkeba89a32014-07-19 09:26:27 +00001939 ("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
1940 "<svg xmlns=\"http://www.w3.org/2000/svg\""
1941 " width=\"%.20g\" height=\"%.20g\">\n"
1942 "<g>\n"
1943 "<path fill-rule=\"evenodd\" style=\"fill:#00000000;stroke:#00000000;"
1944 "stroke-width:0;stroke-antialiasing:false\" d=\"\n"),
1945 (double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001946 (void) ConcatenateString(&path,message);
1947 (void) ResetMagickMemory(point,0,sizeof(point));
1948 (void) ResetMagickMemory(first,0,sizeof(first));
1949 (void) ResetMagickMemory(last,0,sizeof(last));
1950 knot_count=0;
1951 in_subpath=MagickFalse;
1952 while (length != 0)
1953 {
cristy4a8b0172012-02-03 16:39:53 +00001954 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001955 switch (selector)
1956 {
1957 case 0:
1958 case 3:
1959 {
1960 if (knot_count != 0)
1961 {
1962 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001963 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001964 break;
1965 }
1966 /*
1967 Expected subpath length record.
1968 */
cristy4a8b0172012-02-03 16:39:53 +00001969 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001970 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001971 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001972 break;
1973 }
1974 case 1:
1975 case 2:
1976 case 4:
1977 case 5:
1978 {
1979 if (knot_count == 0)
1980 {
1981 /*
1982 Unexpected subpath knot.
1983 */
1984 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001985 length-=MagickMin(24,(ssize_t) length);
cristy97433202009-10-27 02:05:08 +00001986 break;
1987 }
1988 /*
1989 Add sub-path knot
1990 */
1991 for (i=0; i < 3; i++)
1992 {
dirk68bd1b52014-08-28 18:58:23 +00001993 unsigned int
cristy97433202009-10-27 02:05:08 +00001994 xx,
1995 yy;
1996
dirk68bd1b52014-08-28 18:58:23 +00001997 yy=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length));
1998 xx=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001999 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00002000 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00002001 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00002002 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00002003 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00002004 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00002005 point[i].x=(double) x*columns/4096/4096;
2006 point[i].y=(double) y*rows/4096/4096;
2007 }
cristyf516f2f2014-01-07 01:29:28 +00002008 if (in_subpath == MagickFalse)
cristy97433202009-10-27 02:05:08 +00002009 {
cristy151b66d2015-04-15 10:50:31 +00002010 (void) FormatLocaleString(message,MagickPathExtent,"M %g %g\n",
cristy97433202009-10-27 02:05:08 +00002011 point[1].x,point[1].y);
2012 for (i=0; i < 3; i++)
2013 {
2014 first[i]=point[i];
2015 last[i]=point[i];
2016 }
cristy3ed852e2009-09-05 21:47:34 +00002017 }
2018 else
2019 {
cristy9eb18472013-11-11 14:55:48 +00002020 /*
2021 Handle special cases when Bezier curves are used to describe
2022 corners and straight lines.
2023 */
cristy97433202009-10-27 02:05:08 +00002024 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2025 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristy151b66d2015-04-15 10:50:31 +00002026 (void) FormatLocaleString(message,MagickPathExtent,
cristyf516f2f2014-01-07 01:29:28 +00002027 "L %g %g\n",point[1].x,point[1].y);
cristy9eb18472013-11-11 14:55:48 +00002028 else
cristy151b66d2015-04-15 10:50:31 +00002029 (void) FormatLocaleString(message,MagickPathExtent,
dirkd8c32862014-09-18 22:01:17 +00002030 "C %g %g %g %g %g %g\n",last[2].x,
2031 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00002032 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00002033 last[i]=point[i];
2034 }
2035 (void) ConcatenateString(&path,message);
2036 in_subpath=MagickTrue;
2037 knot_count--;
2038 /*
2039 Close the subpath if there are no more knots.
2040 */
2041 if (knot_count == 0)
2042 {
cristy9eb18472013-11-11 14:55:48 +00002043 /*
2044 Same special handling as above except we compare to the
2045 first point in the path and close the path.
2046 */
cristy97433202009-10-27 02:05:08 +00002047 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2048 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristy151b66d2015-04-15 10:50:31 +00002049 (void) FormatLocaleString(message,MagickPathExtent,
cristyf516f2f2014-01-07 01:29:28 +00002050 "L %g %g Z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00002051 else
cristy151b66d2015-04-15 10:50:31 +00002052 (void) FormatLocaleString(message,MagickPathExtent,
dirkd8c32862014-09-18 22:01:17 +00002053 "C %g %g %g %g %g %g Z\n",last[2].x,
2054 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy9eb18472013-11-11 14:55:48 +00002055 (void) ConcatenateString(&path,message);
cristy97433202009-10-27 02:05:08 +00002056 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002057 }
cristy97433202009-10-27 02:05:08 +00002058 break;
cristy3ed852e2009-09-05 21:47:34 +00002059 }
2060 case 6:
2061 case 7:
2062 case 8:
2063 default:
2064 {
2065 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00002066 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00002067 break;
2068 }
2069 }
2070 }
2071 /*
2072 Return an empty SVG image if the path does not have knots.
2073 */
dirkeba89a32014-07-19 09:26:27 +00002074 (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00002075 message=DestroyString(message);
2076 return(path);
2077}
2078
2079MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00002080 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002081{
cristy3ed852e2009-09-05 21:47:34 +00002082 register const char
2083 *p;
2084
2085 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002086 assert(image->signature == MagickCoreSignature);
cristy8c8d3f52014-02-14 02:22:14 +00002087 if (IfMagickTrue(image->debug))
cristy3ed852e2009-09-05 21:47:34 +00002088 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2089 p=(const char *) NULL;
cristy27f7af22010-06-21 12:23:21 +00002090 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002091 {
cristy5eb9fc82011-02-21 15:05:41 +00002092 if (property == (const char *) NULL)
2093 {
2094 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
2095 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
2096 image->properties);
2097 return(p);
2098 }
anthonya322a832013-04-27 06:28:03 +00002099 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2100 image->properties,property);
2101 if (p != (const char *) NULL)
2102 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002103 }
cristy5eb9fc82011-02-21 15:05:41 +00002104 if ((property == (const char *) NULL) ||
2105 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002106 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002107 switch (*property)
2108 {
2109 case '8':
2110 {
2111 if (LocaleNCompare("8bim:",property,5) == 0)
2112 {
cristy8c8d3f52014-02-14 02:22:14 +00002113 (void) Get8BIMProperty(image,property,exception);
2114 break;
cristy3ed852e2009-09-05 21:47:34 +00002115 }
2116 break;
2117 }
2118 case 'E':
2119 case 'e':
2120 {
2121 if (LocaleNCompare("exif:",property,5) == 0)
2122 {
cristy8c8d3f52014-02-14 02:22:14 +00002123 (void) GetEXIFProperty(image,property,exception);
2124 break;
cristy3ed852e2009-09-05 21:47:34 +00002125 }
2126 break;
2127 }
cristy3ed852e2009-09-05 21:47:34 +00002128 case 'I':
2129 case 'i':
2130 {
cristy55166322014-01-23 01:53:24 +00002131 if ((LocaleNCompare("icc:",property,4) == 0) ||
2132 (LocaleNCompare("icm:",property,4) == 0))
2133 {
cristy8c8d3f52014-02-14 02:22:14 +00002134 (void) GetICCProperty(image,property,exception);
2135 break;
cristy55166322014-01-23 01:53:24 +00002136 }
cristy3ed852e2009-09-05 21:47:34 +00002137 if (LocaleNCompare("iptc:",property,5) == 0)
2138 {
cristy8c8d3f52014-02-14 02:22:14 +00002139 (void) GetIPTCProperty(image,property,exception);
2140 break;
cristy3ed852e2009-09-05 21:47:34 +00002141 }
2142 break;
2143 }
cristy3ed852e2009-09-05 21:47:34 +00002144 case 'X':
2145 case 'x':
2146 {
2147 if (LocaleNCompare("xmp:",property,4) == 0)
2148 {
cristy8c8d3f52014-02-14 02:22:14 +00002149 (void) GetXMPProperty(image,property);
2150 break;
cristy3ed852e2009-09-05 21:47:34 +00002151 }
2152 break;
2153 }
cristy5eb9fc82011-02-21 15:05:41 +00002154 default:
2155 break;
cristy3ed852e2009-09-05 21:47:34 +00002156 }
cristy8c8d3f52014-02-14 02:22:14 +00002157 if (image->properties != (void *) NULL)
2158 {
2159 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2160 image->properties,property);
2161 return(p);
2162 }
2163 return((const char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002164}
cristyf516f2f2014-01-07 01:29:28 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166/*
2167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2168% %
2169% %
2170% %
2171+ G e t M a g i c k P r o p e r t y %
2172% %
2173% %
2174% %
2175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2176%
anthony06762232012-04-29 11:45:40 +00002177% GetMagickProperty() gets attributes or calculated values that is associated
anthonya322a832013-04-27 06:28:03 +00002178% with a fixed known property name, or single letter property. It may be
anthonydb2a1d62013-04-29 01:34:58 +00002179% called if no image is defined (IMv7), in which case only global image_info
cristy934c77d2014-12-14 20:14:16 +00002180% values are available:
2181%
2182% \n newline
2183% \r carriage return
2184% < less-than character.
2185% > greater-than character.
2186% & ampersand character.
2187% %% a percent sign
2188% %b file size of image read in
2189% %c comment meta-data property
2190% %d directory component of path
2191% %e filename extension or suffix
2192% %f filename (including suffix)
2193% %g layer canvas page geometry (equivalent to "%Wx%H%X%Y")
2194% %h current image height in pixels
2195% %i image filename (note: becomes output filename for "info:")
2196% %k CALCULATED: number of unique colors
2197% %l label meta-data property
2198% %m image file format (file magic)
2199% %n number of images in current image sequence
2200% %o output filename (used for delegates)
2201% %p index of image in current image list
2202% %q quantum depth (compile-time constant)
2203% %r image class and colorspace
2204% %s scene number (from input unless re-assigned)
2205% %t filename without directory or extension (suffix)
2206% %u unique temporary filename (used for delegates)
2207% %w current width in pixels
2208% %x x resolution (density)
2209% %y y resolution (density)
2210% %z image depth (as read in unless modified, image save depth)
2211% %A image transparency channel enabled (true/false)
2212% %C image compression type
2213% %D image GIF dispose method
2214% %G original image size (%wx%h; before any resizes)
2215% %H page (canvas) height
2216% %M Magick filename (original file exactly as given, including read mods)
2217% %O page (canvas) offset ( = %X%Y )
2218% %P page (canvas) size ( = %Wx%H )
2219% %Q image compression quality ( 0 = default )
2220% %S ?? scenes ??
2221% %T image time delay (in centi-seconds)
2222% %U image resolution units
2223% %W page (canvas) width
2224% %X page (canvas) x offset (including sign)
2225% %Y page (canvas) y offset (including sign)
2226% %Z unique filename (used for delegates)
2227% %@ CALCULATED: trim bounding box (without actually trimming)
2228% %# CALCULATED: 'signature' hash of image values
anthony06762232012-04-29 11:45:40 +00002229%
anthonydb2a1d62013-04-29 01:34:58 +00002230% This routine only handles specifically known properties. It does not
2231% handle special prefixed properties, profiles, or expressions. Nor does
2232% it return any free-form property strings.
anthony06762232012-04-29 11:45:40 +00002233%
anthonydb2a1d62013-04-29 01:34:58 +00002234% The returned string is stored in a structure somewhere, and should not be
2235% directly freed. If the string was generated (common) the string will be
2236% stored as as either as artifact or option 'get-property'. These may be
2237% deleted (cleaned up) when no longer required, but neither artifact or
2238% option is guranteed to exist.
cristy3ed852e2009-09-05 21:47:34 +00002239%
2240% The format of the GetMagickProperty method is:
2241%
anthonydb2a1d62013-04-29 01:34:58 +00002242% const char *GetMagickProperty(ImageInfo *image_info,Image *image,
cristy97fc9f32012-06-13 21:04:44 +00002243% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002244%
2245% A description of each parameter follows:
2246%
anthonydb2a1d62013-04-29 01:34:58 +00002247% o image_info: the image info (optional)
cristy3ed852e2009-09-05 21:47:34 +00002248%
anthonydb2a1d62013-04-29 01:34:58 +00002249% o image: the image (optional)
cristy3ed852e2009-09-05 21:47:34 +00002250%
2251% o key: the key.
2252%
cristyd15e6592011-10-15 00:13:06 +00002253% o exception: return any errors or warnings in this structure.
2254%
cristy3ed852e2009-09-05 21:47:34 +00002255*/
anthonya322a832013-04-27 06:28:03 +00002256#define WarnNoImageReturn(format,arg) \
2257 if (image == (Image *) NULL ) { \
2258 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2259 "NoImageForProperty",format,arg); \
cristyf432c632014-12-07 15:11:28 +00002260 return((const char *) NULL); \
anthonya322a832013-04-27 06:28:03 +00002261 }
anthonydb2a1d62013-04-29 01:34:58 +00002262#define WarnNoImageInfoReturn(format,arg) \
cristy4d246fc2014-01-15 22:33:44 +00002263 if (image_info == (ImageInfo *) NULL ) { \
anthonydb2a1d62013-04-29 01:34:58 +00002264 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2265 "NoImageInfoForProperty",format,arg); \
cristyf432c632014-12-07 15:11:28 +00002266 return((const char *) NULL); \
anthonydb2a1d62013-04-29 01:34:58 +00002267 }
anthonya322a832013-04-27 06:28:03 +00002268
anthonydb2a1d62013-04-29 01:34:58 +00002269static const char *GetMagickPropertyLetter(ImageInfo *image_info,
anthony2fbb5952012-05-05 12:35:30 +00002270 Image *image,const char letter,ExceptionInfo *exception)
2271{
2272 char
cristy151b66d2015-04-15 10:50:31 +00002273 value[MagickPathExtent]; /* formated string to store as a returned artifact */
anthony2fbb5952012-05-05 12:35:30 +00002274
anthony2cfa1a12012-05-12 05:18:07 +00002275 const char
anthonya322a832013-04-27 06:28:03 +00002276 *string; /* return a string already stored somewher */
anthony2cfa1a12012-05-12 05:18:07 +00002277
2278 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002279 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonydb2a1d62013-04-29 01:34:58 +00002280 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2281 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
2282
anthonya322a832013-04-27 06:28:03 +00002283 *value='\0'; /* formatted string */
cristy68064d72012-06-13 20:56:14 +00002284 string=(char *) NULL; /* constant string reference */
anthonya322a832013-04-27 06:28:03 +00002285
2286 /* Get properities that are directly defined by images */
anthony2fbb5952012-05-05 12:35:30 +00002287 switch (letter)
2288 {
anthony2fbb5952012-05-05 12:35:30 +00002289 case 'b': /* image size read in - in bytes */
2290 {
anthonya322a832013-04-27 06:28:03 +00002291 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002292 (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
cristyd4618c02015-04-14 23:54:43 +00002293 value);
anthony2fbb5952012-05-05 12:35:30 +00002294 break;
2295 }
anthony7bb7aee2012-05-15 00:09:16 +00002296 case 'c': /* image comment property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002297 {
anthonya322a832013-04-27 06:28:03 +00002298 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002299 string=GetImageProperty(image,"comment",exception);
anthonya322a832013-04-27 06:28:03 +00002300 if ( string == (const char *) NULL )
anthony7bb7aee2012-05-15 00:09:16 +00002301 string="";
anthony2fbb5952012-05-05 12:35:30 +00002302 break;
2303 }
2304 case 'd': /* Directory component of filename */
2305 {
anthonya322a832013-04-27 06:28:03 +00002306 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002307 GetPathComponent(image->magick_filename,HeadPath,value);
anthonya322a832013-04-27 06:28:03 +00002308 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002309 break;
2310 }
2311 case 'e': /* Filename extension (suffix) of image file */
2312 {
anthonya322a832013-04-27 06:28:03 +00002313 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002314 GetPathComponent(image->magick_filename,ExtensionPath,value);
anthonya322a832013-04-27 06:28:03 +00002315 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002316 break;
2317 }
2318 case 'f': /* Filename without directory component */
2319 {
anthonya322a832013-04-27 06:28:03 +00002320 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002321 GetPathComponent(image->magick_filename,TailPath,value);
anthonya322a832013-04-27 06:28:03 +00002322 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002323 break;
2324 }
2325 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */
2326 {
anthonya322a832013-04-27 06:28:03 +00002327 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002328 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002329 (double) image->page.width,(double) image->page.height,
2330 (double) image->page.x,(double) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002331 break;
2332 }
2333 case 'h': /* Image height (current) */
2334 {
anthonya322a832013-04-27 06:28:03 +00002335 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002336 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristyec6934f2012-08-14 18:38:40 +00002337 (image->rows != 0 ? image->rows : image->magick_rows));
anthony2fbb5952012-05-05 12:35:30 +00002338 break;
2339 }
anthonya322a832013-04-27 06:28:03 +00002340 case 'i': /* Filename last used for an image (read or write) */
anthony2fbb5952012-05-05 12:35:30 +00002341 {
anthonya322a832013-04-27 06:28:03 +00002342 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002343 string=image->filename;
anthony2fbb5952012-05-05 12:35:30 +00002344 break;
2345 }
2346 case 'k': /* Number of unique colors */
2347 {
cristyec6934f2012-08-14 18:38:40 +00002348 /*
2349 FUTURE: ensure this does not generate the formatted comment!
2350 */
anthonya322a832013-04-27 06:28:03 +00002351 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002352 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002353 GetNumberColors(image,(FILE *) NULL,exception));
anthony2fbb5952012-05-05 12:35:30 +00002354 break;
2355 }
cristy97fc9f32012-06-13 21:04:44 +00002356 case 'l': /* Image label property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002357 {
anthonya322a832013-04-27 06:28:03 +00002358 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002359 string=GetImageProperty(image,"label",exception);
anthonya322a832013-04-27 06:28:03 +00002360 if ( string == (const char *) NULL)
anthony7bb7aee2012-05-15 00:09:16 +00002361 string="";
anthony2fbb5952012-05-05 12:35:30 +00002362 break;
2363 }
2364 case 'm': /* Image format (file magick) */
2365 {
anthonya322a832013-04-27 06:28:03 +00002366 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002367 string=image->magick;
anthony2fbb5952012-05-05 12:35:30 +00002368 break;
2369 }
2370 case 'n': /* Number of images in the list. */
2371 {
anthonya322a832013-04-27 06:28:03 +00002372 if ( image != (Image *) NULL )
cristy151b66d2015-04-15 10:50:31 +00002373 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
anthonya322a832013-04-27 06:28:03 +00002374 GetImageListLength(image));
2375 else
2376 string="0"; /* no images or scenes */
anthony2fbb5952012-05-05 12:35:30 +00002377 break;
2378 }
2379 case 'o': /* Output Filename - for delegate use only */
anthonydb2a1d62013-04-29 01:34:58 +00002380 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002381 string=image_info->filename;
anthony2fbb5952012-05-05 12:35:30 +00002382 break;
anthonya322a832013-04-27 06:28:03 +00002383 case 'p': /* Image index in current image list */
anthony2fbb5952012-05-05 12:35:30 +00002384 {
anthonya322a832013-04-27 06:28:03 +00002385 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002386 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002387 GetImageIndexInList(image));
anthony2fbb5952012-05-05 12:35:30 +00002388 break;
2389 }
2390 case 'q': /* Quantum depth of image in memory */
2391 {
anthonya322a832013-04-27 06:28:03 +00002392 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002393 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
anthony2fbb5952012-05-05 12:35:30 +00002394 MAGICKCORE_QUANTUM_DEPTH);
2395 break;
2396 }
anthonya322a832013-04-27 06:28:03 +00002397 case 'r': /* Image storage class, colorspace, and alpha enabled. */
anthony2fbb5952012-05-05 12:35:30 +00002398 {
2399 ColorspaceType
2400 colorspace;
2401
anthonya322a832013-04-27 06:28:03 +00002402 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002403 colorspace=image->colorspace;
dirkf1d85482015-04-06 00:36:00 +00002404 if (IfMagickTrue(SetImageGray(image,exception)))
anthonya322a832013-04-27 06:28:03 +00002405 colorspace=GRAYColorspace; /* FUTURE: this is IMv6 not IMv7 */
cristy151b66d2015-04-15 10:50:31 +00002406 (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s",
cristy68064d72012-06-13 20:56:14 +00002407 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
anthony2fbb5952012-05-05 12:35:30 +00002408 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
cristy17f11b02014-12-20 19:37:04 +00002409 image->alpha_trait != UndefinedPixelTrait ? "Alpha" : "");
anthony2fbb5952012-05-05 12:35:30 +00002410 break;
2411 }
2412 case 's': /* Image scene number */
2413 {
anthonya322a832013-04-27 06:28:03 +00002414#if 0 /* this seems non-sensical -- simplifing */
anthony2fbb5952012-05-05 12:35:30 +00002415 if (image_info->number_scenes != 0)
cristy151b66d2015-04-15 10:50:31 +00002416 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002417 image_info->scene);
cristyf432c632014-12-07 15:11:28 +00002418 else if (image != (Image *) NULL)
cristy151b66d2015-04-15 10:50:31 +00002419 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002420 image->scene);
anthonya322a832013-04-27 06:28:03 +00002421 else
2422 string="0";
2423#else
2424 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002425 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
anthonya322a832013-04-27 06:28:03 +00002426 image->scene);
2427#endif
anthony2fbb5952012-05-05 12:35:30 +00002428 break;
2429 }
2430 case 't': /* Base filename without directory or extention */
2431 {
anthonya322a832013-04-27 06:28:03 +00002432 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002433 GetPathComponent(image->magick_filename,BasePath,value);
anthonya322a832013-04-27 06:28:03 +00002434 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002435 break;
2436 }
2437 case 'u': /* Unique filename */
anthonydb2a1d62013-04-29 01:34:58 +00002438 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002439 string=image_info->unique;
anthony2fbb5952012-05-05 12:35:30 +00002440 break;
anthony2fbb5952012-05-05 12:35:30 +00002441 case 'w': /* Image width (current) */
2442 {
anthonya322a832013-04-27 06:28:03 +00002443 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002444 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002445 (image->columns != 0 ? image->columns : image->magick_columns));
anthony2fbb5952012-05-05 12:35:30 +00002446 break;
2447 }
anthonyc69008c2012-05-07 11:45:32 +00002448 case 'x': /* Image horizontal resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002449 {
anthonya322a832013-04-27 06:28:03 +00002450 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002451 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002452 fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0);
anthony2fbb5952012-05-05 12:35:30 +00002453 break;
2454 }
anthonyc69008c2012-05-07 11:45:32 +00002455 case 'y': /* Image vertical resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002456 {
anthonya322a832013-04-27 06:28:03 +00002457 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002458 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002459 fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0);
anthony2fbb5952012-05-05 12:35:30 +00002460 break;
2461 }
2462 case 'z': /* Image depth as read in */
2463 {
anthonya322a832013-04-27 06:28:03 +00002464 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002465 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002466 (double) image->depth);
anthony2fbb5952012-05-05 12:35:30 +00002467 break;
2468 }
2469 case 'A': /* Image alpha channel */
2470 {
anthonya322a832013-04-27 06:28:03 +00002471 WarnNoImageReturn("\"%%%c\"",letter);
2472 string=CommandOptionToMnemonic(MagickBooleanOptions,
cristyed8d7852013-08-01 22:44:22 +00002473 (ssize_t) image->alpha_trait);
anthony2fbb5952012-05-05 12:35:30 +00002474 break;
2475 }
2476 case 'C': /* Image compression method. */
2477 {
anthonya322a832013-04-27 06:28:03 +00002478 WarnNoImageReturn("\"%%%c\"",letter);
2479 string=CommandOptionToMnemonic(MagickCompressOptions,
cristyed8d7852013-08-01 22:44:22 +00002480 (ssize_t) image->compression);
anthony2fbb5952012-05-05 12:35:30 +00002481 break;
2482 }
2483 case 'D': /* Image dispose method. */
2484 {
anthonya322a832013-04-27 06:28:03 +00002485 WarnNoImageReturn("\"%%%c\"",letter);
2486 string=CommandOptionToMnemonic(MagickDisposeOptions,
cristyed8d7852013-08-01 22:44:22 +00002487 (ssize_t) image->dispose);
anthony2fbb5952012-05-05 12:35:30 +00002488 break;
2489 }
2490 case 'G': /* Image size as geometry = "%wx%h" */
2491 {
anthonya322a832013-04-27 06:28:03 +00002492 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002493 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002494 (double)image->magick_columns,(double) image->magick_rows);
anthony2fbb5952012-05-05 12:35:30 +00002495 break;
2496 }
2497 case 'H': /* layer canvas height */
2498 {
anthonya322a832013-04-27 06:28:03 +00002499 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002500 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002501 (double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002502 break;
2503 }
2504 case 'M': /* Magick filename - filename given incl. coder & read mods */
2505 {
anthonya322a832013-04-27 06:28:03 +00002506 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002507 string=image->magick_filename;
anthony2fbb5952012-05-05 12:35:30 +00002508 break;
2509 }
2510 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2511 {
anthonya322a832013-04-27 06:28:03 +00002512 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002513 (void) FormatLocaleString(value,MagickPathExtent,"%+ld%+ld",(long)
cristy68064d72012-06-13 20:56:14 +00002514 image->page.x,(long) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002515 break;
2516 }
2517 case 'P': /* layer canvas page size = "%Wx%H" */
2518 {
anthonya322a832013-04-27 06:28:03 +00002519 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002520 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002521 (double) image->page.width,(double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002522 break;
2523 }
2524 case 'Q': /* image compression quality */
2525 {
anthonya322a832013-04-27 06:28:03 +00002526 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002527 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristyd46a07e2013-08-03 23:07:04 +00002528 (image->quality == 0 ? 92 : image->quality));
anthony2fbb5952012-05-05 12:35:30 +00002529 break;
2530 }
anthonya322a832013-04-27 06:28:03 +00002531 case 'S': /* Number of scenes in image list. */
anthony2fbb5952012-05-05 12:35:30 +00002532 {
anthonydb2a1d62013-04-29 01:34:58 +00002533 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthonya322a832013-04-27 06:28:03 +00002534#if 0 /* What is this number? -- it makes no sense - simplifing */
anthony2fbb5952012-05-05 12:35:30 +00002535 if (image_info->number_scenes == 0)
anthonya322a832013-04-27 06:28:03 +00002536 string="2147483647";
2537 else if ( image != (Image *) NULL )
cristy151b66d2015-04-15 10:50:31 +00002538 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
anthonya322a832013-04-27 06:28:03 +00002539 image_info->scene+image_info->number_scenes);
anthonydb2a1d62013-04-29 01:34:58 +00002540 else
anthonya322a832013-04-27 06:28:03 +00002541 string="0";
2542#else
cristy151b66d2015-04-15 10:50:31 +00002543 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristyab5fbd82013-04-28 17:08:53 +00002544 (image_info->number_scenes == 0 ? 2147483647 :
2545 image_info->number_scenes));
anthonya322a832013-04-27 06:28:03 +00002546#endif
anthony2fbb5952012-05-05 12:35:30 +00002547 break;
2548 }
2549 case 'T': /* image time delay for animations */
2550 {
anthonya322a832013-04-27 06:28:03 +00002551 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002552 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristyed8d7852013-08-01 22:44:22 +00002553 image->delay);
anthony2fbb5952012-05-05 12:35:30 +00002554 break;
2555 }
cristye9f6b9e2013-10-27 22:04:52 +00002556 case 'U': /* Image resolution units. */
2557 {
2558 WarnNoImageReturn("\"%%%c\"",letter);
2559 string=CommandOptionToMnemonic(MagickResolutionOptions,
2560 (ssize_t) image->units);
2561 break;
2562 }
anthony2fbb5952012-05-05 12:35:30 +00002563 case 'W': /* layer canvas width */
2564 {
anthonya322a832013-04-27 06:28:03 +00002565 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002566 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002567 image->page.width);
anthony2fbb5952012-05-05 12:35:30 +00002568 break;
2569 }
2570 case 'X': /* layer canvas X offset */
2571 {
anthonya322a832013-04-27 06:28:03 +00002572 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002573 (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002574 image->page.x);
anthony2fbb5952012-05-05 12:35:30 +00002575 break;
2576 }
2577 case 'Y': /* layer canvas Y offset */
2578 {
anthonya322a832013-04-27 06:28:03 +00002579 WarnNoImageReturn("\"%%%c\"",letter);
cristy151b66d2015-04-15 10:50:31 +00002580 (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002581 image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002582 break;
2583 }
anthony6e2e0732012-05-06 12:22:43 +00002584 case 'Z': /* Zero filename ??? */
anthonydb2a1d62013-04-29 01:34:58 +00002585 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002586 string=image_info->zero;
anthony2fbb5952012-05-05 12:35:30 +00002587 break;
anthonya322a832013-04-27 06:28:03 +00002588 case '%': /* percent escaped */
2589 string="%";
2590 break;
2591 case '@': /* Trim bounding box, without actually Trimming! */
anthony2fbb5952012-05-05 12:35:30 +00002592 {
2593 RectangleInfo
2594 page;
2595
anthonya322a832013-04-27 06:28:03 +00002596 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002597 page=GetImageBoundingBox(image,exception);
cristy151b66d2015-04-15 10:50:31 +00002598 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002599 (double) page.width,(double) page.height,(double) page.x,(double)
2600 page.y);
anthony2fbb5952012-05-05 12:35:30 +00002601 break;
2602 }
cristye5b517a2014-02-14 14:19:29 +00002603 case '#':
anthony2fbb5952012-05-05 12:35:30 +00002604 {
cristye5b517a2014-02-14 14:19:29 +00002605 /*
2606 Image signature.
2607 */
anthonya322a832013-04-27 06:28:03 +00002608 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002609 (void) SignatureImage(image,exception);
anthony2cfa1a12012-05-12 05:18:07 +00002610 string=GetImageProperty(image,"signature",exception);
anthony2fbb5952012-05-05 12:35:30 +00002611 break;
2612 }
anthony2fbb5952012-05-05 12:35:30 +00002613 }
cristy68064d72012-06-13 20:56:14 +00002614 if (string != (char *) NULL)
anthonya322a832013-04-27 06:28:03 +00002615 return(string);
2616 if (*value != '\0')
cristye5b517a2014-02-14 14:19:29 +00002617 {
2618 /*
2619 Create a cloned copy of result.
2620 */
cristyf432c632014-12-07 15:11:28 +00002621 if (image != (Image *) NULL)
cristye5b517a2014-02-14 14:19:29 +00002622 {
2623 (void) SetImageArtifact(image,"get-property",value);
2624 return(GetImageArtifact(image,"get-property"));
2625 }
2626 else
2627 {
2628 (void) SetImageOption(image_info,"get-property",value);
2629 return(GetImageOption(image_info,"get-property"));
2630 }
2631 }
cristyf432c632014-12-07 15:11:28 +00002632 return((char *) NULL);
anthony2fbb5952012-05-05 12:35:30 +00002633}
2634
anthonydb2a1d62013-04-29 01:34:58 +00002635MagickExport const char *GetMagickProperty(ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002636 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002637{
2638 char
cristy151b66d2015-04-15 10:50:31 +00002639 value[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002640
anthony2cfa1a12012-05-12 05:18:07 +00002641 const char
2642 *string;
2643
anthony2fbb5952012-05-05 12:35:30 +00002644 assert(property[0] != '\0');
cristyf432c632014-12-07 15:11:28 +00002645 assert(image != (Image *) NULL || image_info != (ImageInfo *) NULL );
anthonya322a832013-04-27 06:28:03 +00002646
cristy68064d72012-06-13 20:56:14 +00002647 if (property[1] == '\0') /* single letter property request */
2648 return(GetMagickPropertyLetter(image_info,image,*property,exception));
anthonya322a832013-04-27 06:28:03 +00002649
anthonydb2a1d62013-04-29 01:34:58 +00002650 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002651 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonydb2a1d62013-04-29 01:34:58 +00002652 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
anthonya322a832013-04-27 06:28:03 +00002654
2655 *value='\0'; /* formated string */
cristy68064d72012-06-13 20:56:14 +00002656 string=(char *) NULL; /* constant string reference */
cristyec6897c2010-05-12 00:43:15 +00002657 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002658 {
2659 case 'b':
2660 {
cristy10d55412015-02-13 21:52:54 +00002661 if (LocaleCompare("basename",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002662 {
anthonya322a832013-04-27 06:28:03 +00002663 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002664 GetPathComponent(image->magick_filename,BasePath,value);
anthonya322a832013-04-27 06:28:03 +00002665 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002666 break;
2667 }
dirk2a08b062013-10-19 19:26:02 +00002668 if (LocaleCompare("bit-depth",property) == 0)
2669 {
cristy151b66d2015-04-15 10:50:31 +00002670 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
dirk2a08b062013-10-19 19:26:02 +00002671 GetImageDepth(image, exception));
2672 break;
2673 }
cristy3ed852e2009-09-05 21:47:34 +00002674 break;
2675 }
2676 case 'c':
2677 {
anthony2cfa1a12012-05-12 05:18:07 +00002678 if (LocaleCompare("channels",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002679 {
anthonya322a832013-04-27 06:28:03 +00002680 WarnNoImageReturn("\"%%[%s]\"",property);
anthony7bb7aee2012-05-15 00:09:16 +00002681 /* FUTURE: return actual image channels */
cristy151b66d2015-04-15 10:50:31 +00002682 (void) FormatLocaleString(value,MagickPathExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002683 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002684 image->colorspace));
2685 LocaleLower(value);
cristy17f11b02014-12-20 19:37:04 +00002686 if( image->alpha_trait != UndefinedPixelTrait )
cristy151b66d2015-04-15 10:50:31 +00002687 (void) ConcatenateMagickString(value,"a",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002688 break;
2689 }
anthony2cfa1a12012-05-12 05:18:07 +00002690 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002691 {
anthonya322a832013-04-27 06:28:03 +00002692 WarnNoImageReturn("\"%%[%s]\"",property);
anthony7bb7aee2012-05-15 00:09:16 +00002693 /* FUTURE: return actual colorspace - no 'gray' stuff */
anthony2cfa1a12012-05-12 05:18:07 +00002694 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3f168fe2013-06-10 17:33:36 +00002695 image->colorspace);
cristy3ed852e2009-09-05 21:47:34 +00002696 break;
2697 }
anthony2cfa1a12012-05-12 05:18:07 +00002698 if (LocaleCompare("copyright",property) == 0)
cristy63054742010-09-20 12:42:28 +00002699 {
cristy151b66d2015-04-15 10:50:31 +00002700 (void) CopyMagickString(value,GetMagickCopyright(),MagickPathExtent);
cristy63054742010-09-20 12:42:28 +00002701 break;
2702 }
cristy3ed852e2009-09-05 21:47:34 +00002703 break;
2704 }
2705 case 'd':
2706 {
anthony2cfa1a12012-05-12 05:18:07 +00002707 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002708 {
anthonya322a832013-04-27 06:28:03 +00002709 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002710 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002711 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002712 break;
2713 }
anthony2cfa1a12012-05-12 05:18:07 +00002714 if (LocaleCompare("directory",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002715 {
anthonya322a832013-04-27 06:28:03 +00002716 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002717 GetPathComponent(image->magick_filename,HeadPath,value);
anthonya322a832013-04-27 06:28:03 +00002718 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002719 break;
2720 }
2721 break;
2722 }
2723 case 'e':
2724 {
cristy50908ac2014-11-09 13:49:47 +00002725 if (LocaleCompare("entropy",property) == 0)
2726 {
2727 double
2728 entropy;
2729
2730 WarnNoImageReturn("\"%%[%s]\"",property);
2731 (void) GetImageEntropy(image,&entropy,exception);
cristy151b66d2015-04-15 10:50:31 +00002732 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristy50908ac2014-11-09 13:49:47 +00002733 GetMagickPrecision(),entropy);
2734 break;
2735 }
anthony2cfa1a12012-05-12 05:18:07 +00002736 if (LocaleCompare("extension",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002737 {
anthonya322a832013-04-27 06:28:03 +00002738 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002739 GetPathComponent(image->magick_filename,ExtensionPath,value);
anthonya322a832013-04-27 06:28:03 +00002740 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002741 break;
2742 }
2743 break;
2744 }
2745 case 'g':
2746 {
anthony2f7f9ca2012-05-14 06:33:53 +00002747 if (LocaleCompare("gamma",property) == 0)
2748 {
anthonya322a832013-04-27 06:28:03 +00002749 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002750 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
anthony2f7f9ca2012-05-14 06:33:53 +00002751 GetMagickPrecision(),image->gamma);
2752 break;
2753 }
anthonydb2a1d62013-04-29 01:34:58 +00002754 if (LocaleCompare("group",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002755 {
anthonydb2a1d62013-04-29 01:34:58 +00002756 WarnNoImageInfoReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002757 (void) FormatLocaleString(value,MagickPathExtent,"0x%lx",(unsigned long)
cristy68064d72012-06-13 20:56:14 +00002758 image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002759 break;
2760 }
2761 break;
2762 }
2763 case 'h':
2764 {
anthony2cfa1a12012-05-12 05:18:07 +00002765 if (LocaleCompare("height",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002766 {
anthonya322a832013-04-27 06:28:03 +00002767 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002768 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002769 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002770 break;
2771 }
2772 break;
2773 }
2774 case 'i':
2775 {
anthony2cfa1a12012-05-12 05:18:07 +00002776 if (LocaleCompare("input",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002777 {
anthonya322a832013-04-27 06:28:03 +00002778 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002779 string=image->filename;
cristy3ed852e2009-09-05 21:47:34 +00002780 break;
2781 }
2782 break;
2783 }
2784 case 'k':
2785 {
anthony2cfa1a12012-05-12 05:18:07 +00002786 if (LocaleCompare("kurtosis",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002787 {
2788 double
2789 kurtosis,
2790 skewness;
2791
anthonya322a832013-04-27 06:28:03 +00002792 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002793 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristy151b66d2015-04-15 10:50:31 +00002794 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002795 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002796 break;
2797 }
2798 break;
2799 }
2800 case 'm':
2801 {
anthony2cfa1a12012-05-12 05:18:07 +00002802 if (LocaleCompare("magick",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002803 {
anthonya322a832013-04-27 06:28:03 +00002804 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002805 string=image->magick;
cristy3ed852e2009-09-05 21:47:34 +00002806 break;
2807 }
cristy8a304ba2015-02-14 01:25:26 +00002808 if ((LocaleCompare("maxima",property) == 0) ||
2809 (LocaleCompare("max",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002810 {
2811 double
2812 maximum,
2813 minimum;
2814
anthonya322a832013-04-27 06:28:03 +00002815 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002816 (void) GetImageRange(image,&minimum,&maximum,exception);
cristy151b66d2015-04-15 10:50:31 +00002817 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002818 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002819 break;
2820 }
anthony2cfa1a12012-05-12 05:18:07 +00002821 if (LocaleCompare("mean",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002822 {
2823 double
2824 mean,
2825 standard_deviation;
2826
anthonya322a832013-04-27 06:28:03 +00002827 WarnNoImageReturn("\"%%[%s]\"",property);
cristy68064d72012-06-13 20:56:14 +00002828 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristy151b66d2015-04-15 10:50:31 +00002829 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002830 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002831 break;
2832 }
cristy8a304ba2015-02-14 01:25:26 +00002833 if ((LocaleCompare("minima",property) == 0) ||
2834 (LocaleCompare("min",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002835 {
2836 double
2837 maximum,
2838 minimum;
2839
anthonya322a832013-04-27 06:28:03 +00002840 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002841 (void) GetImageRange(image,&minimum,&maximum,exception);
cristy151b66d2015-04-15 10:50:31 +00002842 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002843 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002844 break;
2845 }
2846 break;
2847 }
cristy3ed852e2009-09-05 21:47:34 +00002848 case 'o':
2849 {
anthony2cfa1a12012-05-12 05:18:07 +00002850 if (LocaleCompare("opaque",property) == 0)
cristyb0094252011-03-26 12:59:45 +00002851 {
anthonya322a832013-04-27 06:28:03 +00002852 WarnNoImageReturn("\"%%[%s]\"",property);
cristy1b7a35c2015-02-13 18:49:03 +00002853 string=CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t)
2854 IsImageOpaque(image,exception));
cristyb0094252011-03-26 12:59:45 +00002855 break;
2856 }
anthony2cfa1a12012-05-12 05:18:07 +00002857 if (LocaleCompare("orientation",property) == 0)
cristy42aa06c2012-04-01 14:05:10 +00002858 {
anthonya322a832013-04-27 06:28:03 +00002859 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002860 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
2861 image->orientation);
cristy42aa06c2012-04-01 14:05:10 +00002862 break;
2863 }
anthonydb2a1d62013-04-29 01:34:58 +00002864 if (LocaleCompare("output",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002865 {
anthonydb2a1d62013-04-29 01:34:58 +00002866 WarnNoImageInfoReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002867 (void) CopyMagickString(value,image_info->filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002868 break;
2869 }
2870 break;
2871 }
2872 case 'p':
2873 {
dirk6015d442013-10-19 15:07:40 +00002874#if defined(MAGICKCORE_LCMS_DELEGATE)
2875 if (LocaleCompare("profile:icc",property) == 0 ||
2876 LocaleCompare("profile:icm",property) == 0)
2877 {
cristy1a2fb522013-11-20 14:37:58 +00002878#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
2879#define cmsUInt32Number DWORD
2880#endif
2881
dirk6015d442013-10-19 15:07:40 +00002882 const StringInfo
2883 *profile;
2884
2885 cmsHPROFILE
2886 icc_profile;
2887
2888 profile=GetImageProfile(image,property+8);
2889 if (profile == (StringInfo *) NULL)
2890 break;
dirk6015d442013-10-19 15:07:40 +00002891 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
2892 (cmsUInt32Number) GetStringInfoLength(profile));
2893 if (icc_profile != (cmsHPROFILE *) NULL)
2894 {
2895#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
2896 string=cmsTakeProductName(icc_profile);
2897#else
2898 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
cristy151b66d2015-04-15 10:50:31 +00002899 "en","US",value,MagickPathExtent);
dirk6015d442013-10-19 15:07:40 +00002900#endif
2901 (void) cmsCloseProfile(icc_profile);
2902 }
2903 }
2904#endif
2905 if (LocaleCompare("profiles",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002906 {
cristy5954e3c2013-10-18 23:23:44 +00002907 const char
2908 *name;
2909
2910 ResetImageProfileIterator(image);
2911 name=GetNextImageProfile(image);
dirk6015d442013-10-19 15:07:40 +00002912 if (name != (char *) NULL)
2913 {
cristy151b66d2015-04-15 10:50:31 +00002914 (void) CopyMagickString(value,name,MagickPathExtent);
dirk6015d442013-10-19 15:07:40 +00002915 name=GetNextImageProfile(image);
2916 while (name != (char *) NULL)
2917 {
cristy151b66d2015-04-15 10:50:31 +00002918 ConcatenateMagickString(value,",",MagickPathExtent);
2919 ConcatenateMagickString(value,name,MagickPathExtent);
dirk6015d442013-10-19 15:07:40 +00002920 name=GetNextImageProfile(image);
2921 }
2922 }
cristy3ed852e2009-09-05 21:47:34 +00002923 break;
2924 }
2925 break;
2926 }
cristy946b1032012-04-01 14:19:07 +00002927 case 'r':
2928 {
anthony2cfa1a12012-05-12 05:18:07 +00002929 if (LocaleCompare("resolution.x",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002930 {
anthonya322a832013-04-27 06:28:03 +00002931 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002932 (void) FormatLocaleString(value,MagickPathExtent,"%g",
cristy946b1032012-04-01 14:19:07 +00002933 image->resolution.x);
2934 break;
2935 }
anthony2cfa1a12012-05-12 05:18:07 +00002936 if (LocaleCompare("resolution.y",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002937 {
anthonya322a832013-04-27 06:28:03 +00002938 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002939 (void) FormatLocaleString(value,MagickPathExtent,"%g",
cristy946b1032012-04-01 14:19:07 +00002940 image->resolution.y);
2941 break;
2942 }
2943 break;
2944 }
cristy3ed852e2009-09-05 21:47:34 +00002945 case 's':
2946 {
anthony2cfa1a12012-05-12 05:18:07 +00002947 if (LocaleCompare("scene",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002948 {
anthonydb2a1d62013-04-29 01:34:58 +00002949 WarnNoImageInfoReturn("\"%%[%s]\"",property);
2950 if (image_info->number_scenes != 0)
cristy151b66d2015-04-15 10:50:31 +00002951 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002952 image_info->scene);
anthonya322a832013-04-27 06:28:03 +00002953 else {
2954 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002955 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002956 image->scene);
anthonya322a832013-04-27 06:28:03 +00002957 }
cristy3ed852e2009-09-05 21:47:34 +00002958 break;
2959 }
anthony2cfa1a12012-05-12 05:18:07 +00002960 if (LocaleCompare("scenes",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002961 {
anthonydb2a1d62013-04-29 01:34:58 +00002962 /* FUTURE: equivelent to %n? */
anthonya322a832013-04-27 06:28:03 +00002963 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00002964 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
anthonyc69008c2012-05-07 11:45:32 +00002965 GetImageListLength(image));
2966 break;
2967 }
anthony2cfa1a12012-05-12 05:18:07 +00002968 if (LocaleCompare("size",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002969 {
anthonya322a832013-04-27 06:28:03 +00002970 WarnNoImageReturn("\"%%[%s]\"",property);
cristyd4618c02015-04-14 23:54:43 +00002971 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
cristy151b66d2015-04-15 10:50:31 +00002972 MagickPathExtent,value);
anthonyc69008c2012-05-07 11:45:32 +00002973 break;
2974 }
anthony2cfa1a12012-05-12 05:18:07 +00002975 if (LocaleCompare("skewness",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002976 {
2977 double
2978 kurtosis,
2979 skewness;
2980
anthonya322a832013-04-27 06:28:03 +00002981 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002982 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristy151b66d2015-04-15 10:50:31 +00002983 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002984 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002985 break;
2986 }
cristy10d55412015-02-13 21:52:54 +00002987 if (LocaleCompare("standard-deviation",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002988 {
2989 double
2990 mean,
2991 standard_deviation;
2992
anthonya322a832013-04-27 06:28:03 +00002993 WarnNoImageReturn("\"%%[%s]\"",property);
cristy14fed162012-09-26 10:13:43 +00002994 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristy151b66d2015-04-15 10:50:31 +00002995 (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002996 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00002997 break;
2998 }
2999 break;
3000 }
cristy623aaec2012-06-21 00:49:08 +00003001 case 't':
3002 {
3003 if (LocaleCompare("type",property) == 0)
3004 {
anthonya322a832013-04-27 06:28:03 +00003005 WarnNoImageReturn("\"%%[%s]\"",property);
cristy623aaec2012-06-21 00:49:08 +00003006 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
dirkab4f0bb2015-07-25 11:46:32 +00003007 IdentifyImageType(image,exception));
cristy623aaec2012-06-21 00:49:08 +00003008 break;
3009 }
3010 break;
3011 }
cristy3ed852e2009-09-05 21:47:34 +00003012 case 'u':
3013 {
anthonydb2a1d62013-04-29 01:34:58 +00003014 if (LocaleCompare("unique",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003015 {
anthonydb2a1d62013-04-29 01:34:58 +00003016 WarnNoImageInfoReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00003017 string=image_info->unique;
cristy3ed852e2009-09-05 21:47:34 +00003018 break;
3019 }
cristy9af7f5b2013-09-23 18:58:46 +00003020 if (LocaleCompare("units",property) == 0)
3021 {
3022 WarnNoImageReturn("\"%%[%s]\"",property);
3023 string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
3024 image->units);
3025 break;
3026 }
3027 if (LocaleCompare("copyright",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003028 break;
3029 }
cristy63054742010-09-20 12:42:28 +00003030 case 'v':
3031 {
anthony2cfa1a12012-05-12 05:18:07 +00003032 if (LocaleCompare("version",property) == 0)
cristy63054742010-09-20 12:42:28 +00003033 {
anthony2cfa1a12012-05-12 05:18:07 +00003034 string=GetMagickVersion((size_t *) NULL);
cristy63054742010-09-20 12:42:28 +00003035 break;
3036 }
3037 break;
3038 }
cristy3ed852e2009-09-05 21:47:34 +00003039 case 'w':
3040 {
anthony2cfa1a12012-05-12 05:18:07 +00003041 if (LocaleCompare("width",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003042 {
anthonya322a832013-04-27 06:28:03 +00003043 WarnNoImageReturn("\"%%[%s]\"",property);
cristy151b66d2015-04-15 10:50:31 +00003044 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003045 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00003046 break;
3047 }
3048 break;
3049 }
cristy3ed852e2009-09-05 21:47:34 +00003050 case 'z':
3051 {
anthonydb2a1d62013-04-29 01:34:58 +00003052 if (LocaleCompare("zero",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003053 {
anthonydb2a1d62013-04-29 01:34:58 +00003054 WarnNoImageInfoReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00003055 string=image_info->zero;
cristy3ed852e2009-09-05 21:47:34 +00003056 break;
3057 }
3058 break;
3059 }
3060 }
anthonya322a832013-04-27 06:28:03 +00003061 if (string != (char *) NULL)
3062 return(string);
anthony2cfa1a12012-05-12 05:18:07 +00003063 if (*value != '\0')
anthonydb2a1d62013-04-29 01:34:58 +00003064 {
3065 /* create a cloned copy of result, that will get cleaned up, eventually */
cristyf432c632014-12-07 15:11:28 +00003066 if (image != (Image *) NULL)
anthonydb2a1d62013-04-29 01:34:58 +00003067 {
3068 (void) SetImageArtifact(image,"get-property",value);
3069 return(GetImageArtifact(image,"get-property"));
3070 }
3071 else
3072 {
3073 (void) SetImageOption(image_info,"get-property",value);
3074 return(GetImageOption(image_info,"get-property"));
3075 }
3076 }
cristyf432c632014-12-07 15:11:28 +00003077 return((char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003078}
dirk6015d442013-10-19 15:07:40 +00003079#undef WarnNoImageReturn
cristyf516f2f2014-01-07 01:29:28 +00003080
cristy3ed852e2009-09-05 21:47:34 +00003081/*
3082%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3083% %
3084% %
3085% %
3086% G e t N e x t I m a g e P r o p e r t y %
3087% %
3088% %
3089% %
3090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3091%
cristy97fc9f32012-06-13 21:04:44 +00003092% GetNextImageProperty() gets the next free-form string property name.
cristy3ed852e2009-09-05 21:47:34 +00003093%
3094% The format of the GetNextImageProperty method is:
3095%
3096% char *GetNextImageProperty(const Image *image)
3097%
3098% A description of each parameter follows:
3099%
3100% o image: the image.
3101%
3102*/
dirk0d819b62015-07-15 11:34:05 +00003103MagickExport const char *GetNextImageProperty(const Image *image)
cristy3ed852e2009-09-05 21:47:34 +00003104{
3105 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003106 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +00003107 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003108 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3109 image->filename);
3110 if (image->properties == (void *) NULL)
dirk0d819b62015-07-15 11:34:05 +00003111 return((const char *) NULL);
3112 return((const char *) GetNextKeyInSplayTree(
3113 (SplayTreeInfo *) image->properties));
cristy3ed852e2009-09-05 21:47:34 +00003114}
cristycef1ea02014-01-07 00:39:10 +00003115
cristy3ed852e2009-09-05 21:47:34 +00003116/*
3117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3118% %
3119% %
3120% %
3121% I n t e r p r e t I m a g e P r o p e r t i e s %
3122% %
3123% %
3124% %
3125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3126%
3127% InterpretImageProperties() replaces any embedded formatting characters with
anthony06762232012-04-29 11:45:40 +00003128% the appropriate image property and returns the interpreted text.
3129%
3130% This searches for and replaces
anthony003f8992012-05-11 12:50:07 +00003131% \n \r \% replaced by newline, return, and percent resp.
3132% &lt; &gt; &amp; replaced by '<', '>', '&' resp.
3133% %% replaced by percent
anthony06762232012-04-29 11:45:40 +00003134%
anthonyd2f39e02012-08-20 12:29:28 +00003135% %x %[x] where 'x' is a single letter properity, case sensitive).
3136% %[type:name] where 'type' a is special and known prefix.
anthony003f8992012-05-11 12:50:07 +00003137% %[name] where 'name' is a specifically known attribute, calculated
cristy97fc9f32012-06-13 21:04:44 +00003138% value, or a per-image property string name, or a per-image
anthonyd2f39e02012-08-20 12:29:28 +00003139% 'artifact' (as generated from a global option).
3140% It may contain ':' as long as the prefix is not special.
anthony06762232012-04-29 11:45:40 +00003141%
anthonyd2f39e02012-08-20 12:29:28 +00003142% Single letter % substitutions will only happen if the character before the
3143% percent is NOT a number. But braced substitutions will always be performed.
3144% This prevents the typical usage of percent in a interpreted geometry
3145% argument from being substituted when the percent is a geometry flag.
anthony003f8992012-05-11 12:50:07 +00003146%
3147% If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
3148% used as a search pattern to print multiple lines of "name=value\n" pairs of
anthony643c6132012-11-07 14:50:28 +00003149% the associacted set of properties.
anthony003f8992012-05-11 12:50:07 +00003150%
anthonyd2f39e02012-08-20 12:29:28 +00003151% The returned string must be freed using DestoryString() by the caller.
anthonyf68116b2012-04-14 12:54:41 +00003152%
cristy3ed852e2009-09-05 21:47:34 +00003153% The format of the InterpretImageProperties method is:
3154%
anthonydb2a1d62013-04-29 01:34:58 +00003155% char *InterpretImageProperties(ImageInfo *image_info,
anthony003f8992012-05-11 12:50:07 +00003156% Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003157%
3158% A description of each parameter follows:
3159%
anthonya322a832013-04-27 06:28:03 +00003160% o image_info: the image info. (required)
cristy3ed852e2009-09-05 21:47:34 +00003161%
anthonya322a832013-04-27 06:28:03 +00003162% o image: the image. (optional)
cristy3ed852e2009-09-05 21:47:34 +00003163%
3164% o embed_text: the address of a character string containing the embedded
3165% formatting characters.
3166%
cristy018f07f2011-09-04 21:15:19 +00003167% o exception: return any errors or warnings in this structure.
3168%
cristy3ed852e2009-09-05 21:47:34 +00003169*/
anthony643c6132012-11-07 14:50:28 +00003170
3171/* common inline code to expand the interpreted text string */
3172#define ExtendInterpretText(string_length) do { \
dirk93b02b72013-11-16 16:03:36 +00003173DisableMSCWarning(4127) \
anthony643c6132012-11-07 14:50:28 +00003174 size_t length=(string_length); \
3175 if ((size_t) (q-interpret_text+length+1) >= extent) \
3176 { extent+=length; \
3177 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
cristy151b66d2015-04-15 10:50:31 +00003178 extent+MagickPathExtent,sizeof(*interpret_text)); \
anthony643c6132012-11-07 14:50:28 +00003179 if (interpret_text == (char *) NULL) \
cristyf432c632014-12-07 15:11:28 +00003180 return((char *) NULL); \
anthony643c6132012-11-07 14:50:28 +00003181 q=interpret_text+strlen(interpret_text); \
dirk93b02b72013-11-16 16:03:36 +00003182 } } while (0) /* no trailing ; */ \
3183RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003184
3185/* same but append the given string */
3186#define AppendString2Text(string) do { \
dirk93b02b72013-11-16 16:03:36 +00003187DisableMSCWarning(4127) \
anthony643c6132012-11-07 14:50:28 +00003188 size_t length=strlen((string)); \
3189 if ((size_t) (q-interpret_text+length+1) >= extent) \
3190 { extent+=length; \
3191 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
cristy151b66d2015-04-15 10:50:31 +00003192 extent+MagickPathExtent,sizeof(*interpret_text)); \
anthony643c6132012-11-07 14:50:28 +00003193 if (interpret_text == (char *) NULL) \
cristyf432c632014-12-07 15:11:28 +00003194 return((char *) NULL); \
anthony643c6132012-11-07 14:50:28 +00003195 q=interpret_text+strlen(interpret_text); \
3196 } \
3197 (void) CopyMagickString(q,(string),extent); \
3198 q+=length; \
dirk93b02b72013-11-16 16:03:36 +00003199 } while (0) /* no trailing ; */ \
3200RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003201
anthonya322a832013-04-27 06:28:03 +00003202/* same but append a 'key' and 'string' pair */
3203#define AppendKeyValue2Text(key,string) do { \
dirk93b02b72013-11-16 16:03:36 +00003204DisableMSCWarning(4127) \
anthonya322a832013-04-27 06:28:03 +00003205 size_t length=strlen(key)+strlen(string)+2; \
anthony643c6132012-11-07 14:50:28 +00003206 if ((size_t) (q-interpret_text+length+1) >= extent) \
3207 { extent+=length; \
3208 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
cristy151b66d2015-04-15 10:50:31 +00003209 extent+MagickPathExtent,sizeof(*interpret_text)); \
anthony643c6132012-11-07 14:50:28 +00003210 if (interpret_text == (char *) NULL) \
cristyf432c632014-12-07 15:11:28 +00003211 return((char *) NULL); \
anthony643c6132012-11-07 14:50:28 +00003212 q=interpret_text+strlen(interpret_text); \
3213 } \
anthonya322a832013-04-27 06:28:03 +00003214 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(string)); \
dirk93b02b72013-11-16 16:03:36 +00003215 } while (0) /* no trailing ; */ \
3216RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003217
anthonydb2a1d62013-04-29 01:34:58 +00003218MagickExport char *InterpretImageProperties(ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00003219 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003220{
3221 char
anthony003f8992012-05-11 12:50:07 +00003222 *interpret_text;
cristy3ed852e2009-09-05 21:47:34 +00003223
cristy3ed852e2009-09-05 21:47:34 +00003224 register char
anthony643c6132012-11-07 14:50:28 +00003225 *q; /* current position in interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003226
3227 register const char
anthony643c6132012-11-07 14:50:28 +00003228 *p; /* position in embed_text string being expanded */
cristy3ed852e2009-09-05 21:47:34 +00003229
cristy3ed852e2009-09-05 21:47:34 +00003230 size_t
anthony643c6132012-11-07 14:50:28 +00003231 extent; /* allocated length of interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003232
anthony003f8992012-05-11 12:50:07 +00003233 MagickBooleanType
3234 number;
3235
cristye1c94d92015-06-28 12:16:33 +00003236 assert(image == NULL || image->signature == MagickCoreSignature);
3237 assert(image_info == NULL || image_info->signature == MagickCoreSignature);
anthonydb2a1d62013-04-29 01:34:58 +00003238
3239 if (image != (Image *) NULL && IfMagickTrue(image->debug))
3240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3241 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
3242 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
anthony2fbb5952012-05-05 12:35:30 +00003243
glennrpa22b2652013-01-05 12:37:21 +00003244 if (embed_text == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003245 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00003246 p=embed_text;
anthony2fbb5952012-05-05 12:35:30 +00003247
anthony964d28e2012-05-17 23:39:46 +00003248 if (*p == '\0')
3249 return(ConstantString(""));
3250
anthony2fbb5952012-05-05 12:35:30 +00003251 /* handle a '@' replace string from file */
anthony003f8992012-05-11 12:50:07 +00003252 if (*p == '@') {
3253 p++;
3254 if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
cristy6cad6a52015-01-06 00:54:19 +00003255 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3256 "UnableToAccessPath","%s",p);
anthony003f8992012-05-11 12:50:07 +00003257 return((char *) NULL);
3258 }
cristy3a5987c2013-11-07 14:18:46 +00003259 return(FileToString(p,~0UL,exception));
anthony003f8992012-05-11 12:50:07 +00003260 }
3261
cristy3ed852e2009-09-05 21:47:34 +00003262 /*
3263 Translate any embedded format characters.
3264 */
anthony003f8992012-05-11 12:50:07 +00003265 interpret_text=AcquireString(embed_text); /* new string with extra space */
cristy151b66d2015-04-15 10:50:31 +00003266 extent=MagickPathExtent; /* allocated space in string */
anthony003f8992012-05-11 12:50:07 +00003267 number=MagickFalse; /* is last char a number? */
dirkb9dbc292015-07-26 09:50:00 +00003268 for (q=interpret_text; *p!='\0'; number=isdigit(*p) ? MagickTrue : MagickFalse,p++)
cristy3ed852e2009-09-05 21:47:34 +00003269 {
3270 *q='\0';
cristy151b66d2015-04-15 10:50:31 +00003271 ExtendInterpretText(MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00003272 /*
anthonyd2f39e02012-08-20 12:29:28 +00003273 Look for the various escapes, (and handle other specials)
cristy3ed852e2009-09-05 21:47:34 +00003274 */
anthony003f8992012-05-11 12:50:07 +00003275 switch (*p) {
3276 case '\\':
3277 switch (*(p+1)) {
3278 case '\0':
3279 continue;
3280 case 'r': /* convert to RETURN */
3281 *q++='\r';
3282 p++;
3283 continue;
3284 case 'n': /* convert to NEWLINE */
3285 *q++='\n';
3286 p++;
3287 continue;
3288 case '\n': /* EOL removal UNIX,MacOSX */
3289 p++;
3290 continue;
3291 case '\r': /* EOL removal DOS,Windows */
3292 p++;
3293 if (*p == '\n') /* return-newline EOL */
3294 p++;
3295 continue;
3296 default:
3297 p++;
3298 *q++=(*p);
anthony003f8992012-05-11 12:50:07 +00003299 }
cristy2867b872014-05-17 11:53:50 +00003300 continue;
anthony003f8992012-05-11 12:50:07 +00003301 case '&':
anthony104f8932012-05-13 01:54:53 +00003302 if (LocaleNCompare("&lt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003303 *q++='<', p+=3;
anthony104f8932012-05-13 01:54:53 +00003304 else if (LocaleNCompare("&gt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003305 *q++='>', p+=3;
anthony104f8932012-05-13 01:54:53 +00003306 else if (LocaleNCompare("&amp;",p,5) == 0)
anthony003f8992012-05-11 12:50:07 +00003307 *q++='&', p+=4;
3308 else
3309 *q++=(*p);
cristy3ed852e2009-09-05 21:47:34 +00003310 continue;
anthony003f8992012-05-11 12:50:07 +00003311 case '%':
3312 break; /* continue to next set of handlers */
3313 default:
3314 *q++=(*p); /* any thing else is 'as normal' */
cristy3ed852e2009-09-05 21:47:34 +00003315 continue;
anthony003f8992012-05-11 12:50:07 +00003316 }
anthony104f8932012-05-13 01:54:53 +00003317 p++; /* advance beyond the percent */
anthony2fbb5952012-05-05 12:35:30 +00003318
anthony003f8992012-05-11 12:50:07 +00003319 /*
anthony2ec9bd92012-05-20 05:43:24 +00003320 Doubled Percent - or percent at end of string
anthony003f8992012-05-11 12:50:07 +00003321 */
cristy6cad6a52015-01-06 00:54:19 +00003322 if ((*p == '\0') || (*p == '\'') || (*p == '"'))
3323 p--;
3324 if (*p == '%') {
anthony89075d02012-05-18 03:40:40 +00003325 *q++='%';
anthony104f8932012-05-13 01:54:53 +00003326 continue;
3327 }
anthony003f8992012-05-11 12:50:07 +00003328 /*
anthonyd2f39e02012-08-20 12:29:28 +00003329 Single letter escapes %c
anthony003f8992012-05-11 12:50:07 +00003330 */
anthony104f8932012-05-13 01:54:53 +00003331 if ( *p != '[' ) {
anthony003f8992012-05-11 12:50:07 +00003332 const char
anthonya322a832013-04-27 06:28:03 +00003333 *string;
anthony003f8992012-05-11 12:50:07 +00003334
3335 /* But only if not preceeded by a number! */
dirkb9dbc292015-07-26 09:50:00 +00003336 if (number != MagickFalse) {
anthony104f8932012-05-13 01:54:53 +00003337 *q++='%'; /* do NOT substitute the percent */
3338 p--; /* back up one */
anthony003f8992012-05-11 12:50:07 +00003339 continue;
3340 }
anthonya322a832013-04-27 06:28:03 +00003341 string=GetMagickPropertyLetter(image_info,image,*p, exception);
3342 if (string != (char *) NULL)
cristyf57e5482013-02-17 22:11:27 +00003343 {
anthonya322a832013-04-27 06:28:03 +00003344 AppendString2Text(string);
anthonydb2a1d62013-04-29 01:34:58 +00003345 if (image != (Image *) NULL)
3346 (void)DeleteImageArtifact(image,"get-property");
3347 if (image_info != (ImageInfo *) NULL)
3348 (void)DeleteImageOption(image_info,"get-property");
cristyf57e5482013-02-17 22:11:27 +00003349 continue;
3350 }
3351 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3352 "UnknownImageProperty","\"%%%c\"",*p);
anthony003f8992012-05-11 12:50:07 +00003353 continue;
anthony2fbb5952012-05-05 12:35:30 +00003354 }
anthony2fbb5952012-05-05 12:35:30 +00003355
anthony003f8992012-05-11 12:50:07 +00003356 /*
anthonyd2f39e02012-08-20 12:29:28 +00003357 Braced Percent Escape %[...]
anthony003f8992012-05-11 12:50:07 +00003358 */
3359 {
3360 char
cristy151b66d2015-04-15 10:50:31 +00003361 pattern[2*MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003362
anthony003f8992012-05-11 12:50:07 +00003363 const char
3364 *key,
anthonya322a832013-04-27 06:28:03 +00003365 *string;
cristy3ed852e2009-09-05 21:47:34 +00003366
anthony003f8992012-05-11 12:50:07 +00003367 register ssize_t
3368 len;
cristy3ed852e2009-09-05 21:47:34 +00003369
anthony003f8992012-05-11 12:50:07 +00003370 ssize_t
3371 depth;
3372
anthonya322a832013-04-27 06:28:03 +00003373 /* get the property name framed by the %[...] */
anthony104f8932012-05-13 01:54:53 +00003374 p++; /* advance p to just inside the opening brace */
anthony003f8992012-05-11 12:50:07 +00003375 depth=1;
3376 if ( *p == ']' ) {
cristyf2a82ee2014-05-26 17:49:54 +00003377 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3378 "UnknownImageProperty","\"%%[]\"");
anthony003f8992012-05-11 12:50:07 +00003379 break;
3380 }
cristy151b66d2015-04-15 10:50:31 +00003381 for (len=0; len<(MagickPathExtent-1L) && (*p != '\0');)
anthony003f8992012-05-11 12:50:07 +00003382 {
3383 /* skip escaped braces within braced pattern */
3384 if ( (*p == '\\') && (*(p+1) != '\0') ) {
3385 pattern[len++]=(*p++);
3386 pattern[len++]=(*p++);
3387 continue;
3388 }
3389 if (*p == '[')
3390 depth++;
3391 if (*p == ']')
3392 depth--;
3393 if (depth <= 0)
3394 break;
3395 pattern[len++]=(*p++);
3396 }
3397 pattern[len]='\0';
3398 /* Check for unmatched final ']' for "%[...]" */
3399 if ( depth != 0 ) {
3400 if (len >= 64) { /* truncate string for error message */
3401 pattern[61] = '.';
3402 pattern[62] = '.';
3403 pattern[63] = '.';
3404 pattern[64] = '\0';
3405 }
3406 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003407 OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
3408 interpret_text=DestroyString(interpret_text);
cristyf432c632014-12-07 15:11:28 +00003409 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00003410 }
3411
3412 /*
anthony643c6132012-11-07 14:50:28 +00003413 Special Lookup Prefixes %[prefix:...]
anthony003f8992012-05-11 12:50:07 +00003414 */
anthonya322a832013-04-27 06:28:03 +00003415 /* fx - value calculator */
3416 if (LocaleNCompare("fx:",pattern,3) == 0)
3417 {
3418 FxInfo
3419 *fx_info;
3420
3421 double
3422 value;
3423
3424 MagickBooleanType
3425 status;
3426
3427 if (image == (Image *) NULL ) {
3428 (void) ThrowMagickException(exception,GetMagickModule(),
3429 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3430 continue; /* else no image to retrieve artifact */
3431 }
3432 fx_info=AcquireFxInfo(image,pattern+3,exception);
3433 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
3434 &value,exception);
3435 fx_info=DestroyFxInfo(fx_info);
dirkb9dbc292015-07-26 09:50:00 +00003436 if (status != MagickFalse)
anthonya322a832013-04-27 06:28:03 +00003437 {
3438 char
cristy151b66d2015-04-15 10:50:31 +00003439 result[MagickPathExtent];
anthonya322a832013-04-27 06:28:03 +00003440
cristy151b66d2015-04-15 10:50:31 +00003441 (void) FormatLocaleString(result,MagickPathExtent,"%.*g",
anthonya322a832013-04-27 06:28:03 +00003442 GetMagickPrecision(),(double) value);
anthonyec26e252013-04-28 12:18:12 +00003443 AppendString2Text(result);
anthonya322a832013-04-27 06:28:03 +00003444 }
anthonyec26e252013-04-28 12:18:12 +00003445 continue;
anthonya322a832013-04-27 06:28:03 +00003446 }
3447 /* pixel - color value calculator */
3448 if (LocaleNCompare("pixel:",pattern,6) == 0)
3449 {
3450 FxInfo
3451 *fx_info;
3452
3453 double
3454 value;
3455
cristyee1bdaa2013-07-16 16:45:03 +00003456 MagickStatusType
anthonya322a832013-04-27 06:28:03 +00003457 status;
3458
3459 PixelInfo
3460 pixel;
3461
3462 if (image == (Image *) NULL ) {
3463 (void) ThrowMagickException(exception,GetMagickModule(),
3464 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3465 continue; /* else no image to retrieve artifact */
3466 }
3467 GetPixelInfo(image,&pixel);
3468 fx_info=AcquireFxInfo(image,pattern+6,exception);
cristyee1bdaa2013-07-16 16:45:03 +00003469 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
3470 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003471 pixel.red=(double) QuantumRange*value;
cristyaae13f62013-08-15 14:41:32 +00003472 status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003473 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003474 pixel.green=(double) QuantumRange*value;
cristyaae13f62013-08-15 14:41:32 +00003475 status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003476 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003477 pixel.blue=(double) QuantumRange*value;
3478 if (image->colorspace == CMYKColorspace)
3479 {
cristyaae13f62013-08-15 14:41:32 +00003480 status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003481 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003482 pixel.black=(double) QuantumRange*value;
3483 }
cristyaae13f62013-08-15 14:41:32 +00003484 status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003485 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003486 pixel.alpha=(double) QuantumRange*value;
3487 fx_info=DestroyFxInfo(fx_info);
dirkb9dbc292015-07-26 09:50:00 +00003488 if (status != MagickFalse)
anthonya322a832013-04-27 06:28:03 +00003489 {
3490 char
cristy151b66d2015-04-15 10:50:31 +00003491 name[MagickPathExtent];
anthonya322a832013-04-27 06:28:03 +00003492
3493 (void) QueryColorname(image,&pixel,SVGCompliance,name,
3494 exception);
3495 AppendString2Text(name);
3496 }
anthonyec26e252013-04-28 12:18:12 +00003497 continue;
anthonya322a832013-04-27 06:28:03 +00003498 }
anthony643c6132012-11-07 14:50:28 +00003499 /* option - direct global option lookup (with globbing) */
3500 if (LocaleNCompare("option:",pattern,7) == 0)
3501 {
anthonya322a832013-04-27 06:28:03 +00003502 if (image_info == (ImageInfo *) NULL ) {
3503 (void) ThrowMagickException(exception,GetMagickModule(),
3504 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3505 continue; /* else no image to retrieve artifact */
3506 }
dirkb9dbc292015-07-26 09:50:00 +00003507 if (IsGlob(pattern+7) != MagickFalse)
anthony643c6132012-11-07 14:50:28 +00003508 {
3509 ResetImageOptionIterator(image_info);
3510 while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
dirkb9dbc292015-07-26 09:50:00 +00003511 if (GlobExpression(key,pattern+7,MagickTrue) != MagickFalse)
anthony643c6132012-11-07 14:50:28 +00003512 {
anthonya322a832013-04-27 06:28:03 +00003513 string=GetImageOption(image_info,key);
3514 if (string != (const char *) NULL)
3515 AppendKeyValue2Text(key,string);
3516 /* else - assertion failure? key found but no string value! */
anthony643c6132012-11-07 14:50:28 +00003517 }
3518 continue;
3519 }
anthonya322a832013-04-27 06:28:03 +00003520 string=GetImageOption(image_info,pattern+7);
3521 if (string == (char *) NULL)
3522 goto PropertyLookupFailure; /* no artifact of this specifc name */
3523 AppendString2Text(string);
anthony643c6132012-11-07 14:50:28 +00003524 continue;
3525 }
3526 /* artifact - direct image artifact lookup (with glob) */
anthonyc7994672012-11-17 05:33:27 +00003527 if (LocaleNCompare("artifact:",pattern,9) == 0)
anthony643c6132012-11-07 14:50:28 +00003528 {
anthonya322a832013-04-27 06:28:03 +00003529 if (image == (Image *) NULL ) {
3530 (void) ThrowMagickException(exception,GetMagickModule(),
3531 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
anthonyc7994672012-11-17 05:33:27 +00003532 continue; /* else no image to retrieve artifact */
anthonya322a832013-04-27 06:28:03 +00003533 }
dirkb9dbc292015-07-26 09:50:00 +00003534 if (IsGlob(pattern+9) != MagickFalse)
anthony643c6132012-11-07 14:50:28 +00003535 {
3536 ResetImageArtifactIterator(image);
3537 while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
dirkb9dbc292015-07-26 09:50:00 +00003538 if (GlobExpression(key,pattern+9,MagickTrue) != MagickFalse)
anthony643c6132012-11-07 14:50:28 +00003539 {
anthonya322a832013-04-27 06:28:03 +00003540 string=GetImageArtifact(image,key);
3541 if (string != (const char *) NULL)
3542 AppendKeyValue2Text(key,string);
3543 /* else - assertion failure? key found but no string value! */
anthony643c6132012-11-07 14:50:28 +00003544 }
3545 continue;
3546 }
anthonya322a832013-04-27 06:28:03 +00003547 string=GetImageArtifact(image,pattern+9);
3548 if (string == (char *) NULL)
3549 goto PropertyLookupFailure; /* no artifact of this specifc name */
3550 AppendString2Text(string);
anthonyc7994672012-11-17 05:33:27 +00003551 continue;
anthony643c6132012-11-07 14:50:28 +00003552 }
anthonya322a832013-04-27 06:28:03 +00003553 /* property - direct image property lookup (with glob) */
3554 if (LocaleNCompare("property:",pattern,9) == 0)
3555 {
3556 if (image == (Image *) NULL ) {
3557 (void) ThrowMagickException(exception,GetMagickModule(),
3558 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3559 continue; /* else no image to retrieve artifact */
3560 }
dirkb9dbc292015-07-26 09:50:00 +00003561 if (IsGlob(pattern+9) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003562 {
anthonya322a832013-04-27 06:28:03 +00003563 ResetImagePropertyIterator(image);
3564 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
dirkb9dbc292015-07-26 09:50:00 +00003565 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
anthonya322a832013-04-27 06:28:03 +00003566 {
3567 string=GetImageProperty(image,key,exception);
3568 if (string != (const char *) NULL)
3569 AppendKeyValue2Text(key,string);
3570 /* else - assertion failure? */
3571 }
anthony003f8992012-05-11 12:50:07 +00003572 continue;
3573 }
anthonya322a832013-04-27 06:28:03 +00003574 string=GetImageProperty(image,pattern+9,exception);
3575 if (string == (char *) NULL)
3576 goto PropertyLookupFailure; /* no artifact of this specifc name */
3577 AppendString2Text(string);
3578 continue;
3579 }
3580 /* Properties without special prefix.
3581 This handles attributes, properties, and profiles such as %[exif:...]
3582 Note the profile properties may also include a glob expansion pattern.
3583 */
dirkb9dbc292015-07-26 09:50:00 +00003584 if (image != (Image *) NULL)
anthonya322a832013-04-27 06:28:03 +00003585 {
3586 string=GetImageProperty(image,pattern,exception);
3587 if (string != (const char *) NULL)
3588 {
3589 AppendString2Text(string);
anthonydb2a1d62013-04-29 01:34:58 +00003590 if (image != (Image *) NULL)
3591 (void)DeleteImageArtifact(image,"get-property");
3592 if (image_info != (ImageInfo *) NULL)
3593 (void)DeleteImageOption(image_info,"get-property");
anthonya322a832013-04-27 06:28:03 +00003594 continue;
3595 }
3596 }
anthony003f8992012-05-11 12:50:07 +00003597 /*
cristy97fc9f32012-06-13 21:04:44 +00003598 Handle property 'glob' patterns
anthony003f8992012-05-11 12:50:07 +00003599 Such as: %[*] %[user:array_??] %[filename:e*]
3600 */
dirkb9dbc292015-07-26 09:50:00 +00003601 if (IsGlob(pattern) != MagickFalse)
anthony003f8992012-05-11 12:50:07 +00003602 {
anthonya322a832013-04-27 06:28:03 +00003603 if (image == (Image *) NULL)
3604 continue; /* else no image to retrieve proprty - no list */
anthony003f8992012-05-11 12:50:07 +00003605 ResetImagePropertyIterator(image);
anthony643c6132012-11-07 14:50:28 +00003606 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
dirkb9dbc292015-07-26 09:50:00 +00003607 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003608 {
anthonya322a832013-04-27 06:28:03 +00003609 string=GetImageProperty(image,key,exception);
3610 if (string != (const char *) NULL)
3611 AppendKeyValue2Text(key,string);
anthony643c6132012-11-07 14:50:28 +00003612 /* else - assertion failure? */
cristy3ed852e2009-09-05 21:47:34 +00003613 }
anthony003f8992012-05-11 12:50:07 +00003614 continue;
3615 }
3616 /*
3617 Look for a known property or image attribute
3618 Such as %[basename] %[denisty] %[delay]
anthony643c6132012-11-07 14:50:28 +00003619 Also handles a braced single letter: %[b] %[G] %[g]
anthony003f8992012-05-11 12:50:07 +00003620 */
anthonya322a832013-04-27 06:28:03 +00003621 string=GetMagickProperty(image_info,image,pattern,exception);
3622 if (string != (const char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003623 {
anthonya322a832013-04-27 06:28:03 +00003624 AppendString2Text(string);
anthony003f8992012-05-11 12:50:07 +00003625 continue;
3626 }
3627 /*
anthonya322a832013-04-27 06:28:03 +00003628 Look for a per-image Artifact
3629 This includes option lookup (FUTURE: interpreted according to image)
anthony003f8992012-05-11 12:50:07 +00003630 */
cristyf432c632014-12-07 15:11:28 +00003631 if (image != (Image *) NULL)
anthony003f8992012-05-11 12:50:07 +00003632 {
anthonya322a832013-04-27 06:28:03 +00003633 string=GetImageArtifact(image,pattern);
3634 if (string != (char *) NULL)
3635 {
3636 AppendString2Text(string);
3637 continue;
3638 }
anthony003f8992012-05-11 12:50:07 +00003639 }
anthonya322a832013-04-27 06:28:03 +00003640 else
3641 /* no image, so direct 'option' lookup (no delayed percent escapes) */
3642 if (image_info != (ImageInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003643 {
anthonya322a832013-04-27 06:28:03 +00003644 string=GetImageOption(image_info,pattern);
3645 if (string != (char *) NULL)
3646 {
3647 AppendString2Text(string);
3648 continue;
3649 }
cristy3ed852e2009-09-05 21:47:34 +00003650 }
anthonya322a832013-04-27 06:28:03 +00003651PropertyLookupFailure:
anthony003f8992012-05-11 12:50:07 +00003652 /*
3653 Failed to find any match anywhere!
3654 */
3655 if (len >= 64) { /* truncate string for error message */
3656 pattern[61] = '.';
3657 pattern[62] = '.';
3658 pattern[63] = '.';
3659 pattern[64] = '\0';
3660 }
3661 (void) ThrowMagickException(exception,GetMagickModule(),
3662 OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
3663 /* continue */
3664 } /* Braced Percent Escape */
3665
3666 } /* for each char in 'embed_text' */
cristy3ed852e2009-09-05 21:47:34 +00003667 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003668 return(interpret_text);
3669}
cristycef1ea02014-01-07 00:39:10 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671/*
3672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3673% %
3674% %
3675% %
3676% R e m o v e I m a g e P r o p e r t y %
3677% %
3678% %
3679% %
3680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3681%
3682% RemoveImageProperty() removes a property from the image and returns its
3683% value.
3684%
anthonyd2f39e02012-08-20 12:29:28 +00003685% In this case the ConstantString() value returned should be freed by the
3686% caller when finished.
3687%
cristy3ed852e2009-09-05 21:47:34 +00003688% The format of the RemoveImageProperty method is:
3689%
3690% char *RemoveImageProperty(Image *image,const char *property)
3691%
3692% A description of each parameter follows:
3693%
3694% o image: the image.
3695%
3696% o property: the image property.
3697%
3698*/
3699MagickExport char *RemoveImageProperty(Image *image,
3700 const char *property)
3701{
3702 char
3703 *value;
3704
3705 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003706 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +00003707 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3709 image->filename);
3710 if (image->properties == (void *) NULL)
3711 return((char *) NULL);
3712 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3713 property);
3714 return(value);
3715}
cristyf516f2f2014-01-07 01:29:28 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717/*
3718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3719% %
3720% %
3721% %
3722% R e s e t I m a g e P r o p e r t y I t e r a t o r %
3723% %
3724% %
3725% %
3726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3727%
3728% ResetImagePropertyIterator() resets the image properties iterator. Use it
3729% in conjunction with GetNextImageProperty() to iterate over all the values
3730% associated with an image property.
3731%
3732% The format of the ResetImagePropertyIterator method is:
3733%
3734% ResetImagePropertyIterator(Image *image)
3735%
3736% A description of each parameter follows:
3737%
3738% o image: the image.
3739%
3740*/
3741MagickExport void ResetImagePropertyIterator(const Image *image)
3742{
3743 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003744 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +00003745 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003746 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3747 image->filename);
3748 if (image->properties == (void *) NULL)
3749 return;
3750 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3751}
cristyf516f2f2014-01-07 01:29:28 +00003752
cristy3ed852e2009-09-05 21:47:34 +00003753/*
3754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3755% %
3756% %
3757% %
3758% S e t I m a g e P r o p e r t y %
3759% %
3760% %
3761% %
3762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3763%
anthony104f8932012-05-13 01:54:53 +00003764% SetImageProperty() saves the given string value either to specific known
cristy97fc9f32012-06-13 21:04:44 +00003765% attribute or to a freeform property string.
anthony6e2e0732012-05-06 12:22:43 +00003766%
cristy97fc9f32012-06-13 21:04:44 +00003767% Attempting to set a property that is normally calculated will produce
anthony6e2e0732012-05-06 12:22:43 +00003768% an exception.
cristy3ed852e2009-09-05 21:47:34 +00003769%
3770% The format of the SetImageProperty method is:
3771%
3772% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003773% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003774%
3775% A description of each parameter follows:
3776%
3777% o image: the image.
3778%
3779% o property: the image property.
3780%
3781% o values: the image property values.
3782%
cristyd15e6592011-10-15 00:13:06 +00003783% o exception: return any errors or warnings in this structure.
3784%
cristy3ed852e2009-09-05 21:47:34 +00003785*/
3786MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003787 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003788{
3789 MagickBooleanType
3790 status;
3791
3792 MagickStatusType
3793 flags;
3794
3795 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003796 assert(image->signature == MagickCoreSignature);
cristycb190b72014-08-31 20:04:42 +00003797 if (image->debug != MagickFalse)
anthony6e2e0732012-05-06 12:22:43 +00003798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00003799 if (image->properties == (void *) NULL)
3800 image->properties=NewSplayTree(CompareSplayTreeString,
cristy8c8d3f52014-02-14 02:22:14 +00003801 RelinquishMagickMemory,RelinquishMagickMemory); /* create splay-tree */
glennrp9831cc42013-01-08 19:08:31 +00003802 if (value == (const char *) NULL)
cristy8c8d3f52014-02-14 02:22:14 +00003803 return(DeleteImageProperty(image,property)); /* delete if NULL */
cristy3ed852e2009-09-05 21:47:34 +00003804 status=MagickTrue;
anthony6e2e0732012-05-06 12:22:43 +00003805 if (strlen(property) <= 1)
3806 {
cristy8c8d3f52014-02-14 02:22:14 +00003807 /*
3808 Do not 'set' single letter properties - read only shorthand.
3809 */
3810 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3811 "SetReadOnlyProperty","`%s'",property);
anthony6e2e0732012-05-06 12:22:43 +00003812 return(MagickFalse);
3813 }
anthony7bb7aee2012-05-15 00:09:16 +00003814
anthonya322a832013-04-27 06:28:03 +00003815 /* FUTURE: binary chars or quotes in key should produce a error */
anthonya322a832013-04-27 06:28:03 +00003816 /* Set attributes with known names or special prefixes
3817 return result is found, or break to set a free form properity
3818 */
cristy3ed852e2009-09-05 21:47:34 +00003819 switch (*property)
3820 {
anthonya322a832013-04-27 06:28:03 +00003821#if 0 /* Percent escape's sets values with this prefix: for later use
3822 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00003823 case '8':
3824 {
3825 if (LocaleNCompare("8bim:",property,5) == 0)
3826 {
3827 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003828 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003829 return(MagickFalse);
3830 }
3831 break;
3832 }
anthonyc99875a2012-11-14 05:22:17 +00003833#endif
cristy3ed852e2009-09-05 21:47:34 +00003834 case 'B':
3835 case 'b':
3836 {
anthony2f7f9ca2012-05-14 06:33:53 +00003837 if (LocaleCompare("background",property) == 0)
cristyc6c08ab2010-07-24 23:50:09 +00003838 {
cristy9950d572011-10-01 18:22:35 +00003839 (void) QueryColorCompliance(value,AllCompliance,
anthony643c6132012-11-07 14:50:28 +00003840 &image->background_color,exception);
anthonya322a832013-04-27 06:28:03 +00003841 /* check for FUTURE: value exception?? */
anthony643c6132012-11-07 14:50:28 +00003842 /* also add user input to splay tree */
cristyc6c08ab2010-07-24 23:50:09 +00003843 }
anthonya322a832013-04-27 06:28:03 +00003844 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003845 }
3846 case 'C':
3847 case 'c':
3848 {
anthony2f7f9ca2012-05-14 06:33:53 +00003849 if (LocaleCompare("channels",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003850 {
cristy8c8d3f52014-02-14 02:22:14 +00003851 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3852 "SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003853 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003854 }
anthony2f7f9ca2012-05-14 06:33:53 +00003855 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003856 {
cristybb503372010-05-27 20:51:26 +00003857 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003858 colorspace;
3859
cristy042ee782011-04-22 18:48:30 +00003860 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003861 value);
3862 if (colorspace < 0)
anthonya322a832013-04-27 06:28:03 +00003863 return(MagickFalse); /* FUTURE: value exception?? */
cristy220c4d52013-11-27 19:31:32 +00003864 return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
cristy3ed852e2009-09-05 21:47:34 +00003865 }
anthony2f7f9ca2012-05-14 06:33:53 +00003866 if (LocaleCompare("compose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003867 {
cristybb503372010-05-27 20:51:26 +00003868 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003869 compose;
3870
cristy042ee782011-04-22 18:48:30 +00003871 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003872 if (compose < 0)
anthonya322a832013-04-27 06:28:03 +00003873 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003874 image->compose=(CompositeOperator) compose;
anthony643c6132012-11-07 14:50:28 +00003875 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003876 }
anthony2f7f9ca2012-05-14 06:33:53 +00003877 if (LocaleCompare("compress",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003878 {
cristybb503372010-05-27 20:51:26 +00003879 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003880 compression;
3881
cristy042ee782011-04-22 18:48:30 +00003882 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003883 value);
3884 if (compression < 0)
anthonya322a832013-04-27 06:28:03 +00003885 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003886 image->compression=(CompressionType) compression;
anthony643c6132012-11-07 14:50:28 +00003887 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003888 }
anthonya322a832013-04-27 06:28:03 +00003889 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003890 }
3891 case 'D':
3892 case 'd':
3893 {
anthony2f7f9ca2012-05-14 06:33:53 +00003894 if (LocaleCompare("delay",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003895 {
3896 GeometryInfo
3897 geometry_info;
3898
3899 flags=ParseGeometry(value,&geometry_info);
3900 if ((flags & GreaterValue) != 0)
3901 {
cristybb503372010-05-27 20:51:26 +00003902 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3903 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003904 }
3905 else
3906 if ((flags & LessValue) != 0)
3907 {
cristybb503372010-05-27 20:51:26 +00003908 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
anthony643c6132012-11-07 14:50:28 +00003909 image->delay=(ssize_t)
cristyc6c08ab2010-07-24 23:50:09 +00003910 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003911 }
3912 else
cristybb503372010-05-27 20:51:26 +00003913 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003914 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003915 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
anthony643c6132012-11-07 14:50:28 +00003916 return(MagickTrue);
3917 }
3918 if (LocaleCompare("delay_units",property) == 0)
3919 {
3920 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003921 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003922 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00003923 }
anthony2f7f9ca2012-05-14 06:33:53 +00003924 if (LocaleCompare("density",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003925 {
3926 GeometryInfo
3927 geometry_info;
3928
3929 flags=ParseGeometry(value,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +00003930 image->resolution.x=geometry_info.rho;
3931 image->resolution.y=geometry_info.sigma;
cristyfbb56842010-08-02 11:26:33 +00003932 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +00003933 image->resolution.y=image->resolution.x;
anthony643c6132012-11-07 14:50:28 +00003934 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00003935 }
anthony2f7f9ca2012-05-14 06:33:53 +00003936 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003937 {
cristye27293e2009-12-18 02:53:20 +00003938 image->depth=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00003939 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003940 }
anthony2f7f9ca2012-05-14 06:33:53 +00003941 if (LocaleCompare("dispose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003942 {
cristybb503372010-05-27 20:51:26 +00003943 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003944 dispose;
3945
cristy042ee782011-04-22 18:48:30 +00003946 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003947 if (dispose < 0)
anthonya322a832013-04-27 06:28:03 +00003948 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003949 image->dispose=(DisposeType) dispose;
anthony643c6132012-11-07 14:50:28 +00003950 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003951 }
anthonya322a832013-04-27 06:28:03 +00003952 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00003953 }
anthonya322a832013-04-27 06:28:03 +00003954#if 0 /* Percent escape's sets values with this prefix: for later use
3955 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00003956 case 'E':
3957 case 'e':
3958 {
3959 if (LocaleNCompare("exif:",property,5) == 0)
3960 {
3961 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003962 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003963 return(MagickFalse);
3964 }
anthonya322a832013-04-27 06:28:03 +00003965 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00003966 }
3967 case 'F':
3968 case 'f':
3969 {
3970 if (LocaleNCompare("fx:",property,3) == 0)
3971 {
3972 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003973 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003974 return(MagickFalse);
3975 }
anthonya322a832013-04-27 06:28:03 +00003976 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003977 }
anthonyc99875a2012-11-14 05:22:17 +00003978#endif
cristy3ed852e2009-09-05 21:47:34 +00003979 case 'G':
3980 case 'g':
3981 {
anthony2f7f9ca2012-05-14 06:33:53 +00003982 if (LocaleCompare("gamma",property) == 0)
3983 {
3984 image->gamma=StringToDouble(value,(char **) NULL);
anthony643c6132012-11-07 14:50:28 +00003985 return(MagickTrue);
anthony2f7f9ca2012-05-14 06:33:53 +00003986 }
3987 if (LocaleCompare("gravity",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003988 {
cristybb503372010-05-27 20:51:26 +00003989 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003990 gravity;
3991
cristy042ee782011-04-22 18:48:30 +00003992 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003993 if (gravity < 0)
anthonya322a832013-04-27 06:28:03 +00003994 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003995 image->gravity=(GravityType) gravity;
anthony643c6132012-11-07 14:50:28 +00003996 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003997 }
anthonya322a832013-04-27 06:28:03 +00003998 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003999 }
anthony6e2e0732012-05-06 12:22:43 +00004000 case 'H':
4001 case 'h':
anthony643c6132012-11-07 14:50:28 +00004002 {
anthony2f7f9ca2012-05-14 06:33:53 +00004003 if (LocaleCompare("height",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004004 {
4005 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004006 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004007 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004008 }
anthonya322a832013-04-27 06:28:03 +00004009 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004010 }
cristy3ed852e2009-09-05 21:47:34 +00004011 case 'I':
4012 case 'i':
4013 {
cristy70e9f682013-03-12 22:31:22 +00004014 if (LocaleCompare("intensity",property) == 0)
4015 {
4016 ssize_t
4017 intensity;
4018
4019 intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,
4020 value);
4021 if (intensity < 0)
4022 return(MagickFalse);
4023 image->intensity=(PixelIntensityMethod) intensity;
4024 return(MagickTrue);
4025 }
anthony2f7f9ca2012-05-14 06:33:53 +00004026 if (LocaleCompare("intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004027 {
cristybb503372010-05-27 20:51:26 +00004028 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004029 rendering_intent;
4030
cristy042ee782011-04-22 18:48:30 +00004031 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004032 value);
4033 if (rendering_intent < 0)
anthonya322a832013-04-27 06:28:03 +00004034 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00004035 image->rendering_intent=(RenderingIntent) rendering_intent;
anthony643c6132012-11-07 14:50:28 +00004036 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004037 }
anthony2f7f9ca2012-05-14 06:33:53 +00004038 if (LocaleCompare("interpolate",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004039 {
cristybb503372010-05-27 20:51:26 +00004040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004041 interpolate;
4042
cristy042ee782011-04-22 18:48:30 +00004043 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004044 value);
4045 if (interpolate < 0)
anthonya322a832013-04-27 06:28:03 +00004046 return(MagickFalse); /* FUTURE: value exception?? */
cristy5c4e2582011-09-11 19:21:03 +00004047 image->interpolate=(PixelInterpolateMethod) interpolate;
anthony643c6132012-11-07 14:50:28 +00004048 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004049 }
anthonya322a832013-04-27 06:28:03 +00004050#if 0 /* Percent escape's sets values with this prefix: for later use
4051 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004052 if (LocaleNCompare("iptc:",property,5) == 0)
4053 {
4054 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004055 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004056 return(MagickFalse);
4057 }
anthonyc99875a2012-11-14 05:22:17 +00004058#endif
anthonya322a832013-04-27 06:28:03 +00004059 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004060 }
anthony6e2e0732012-05-06 12:22:43 +00004061 case 'K':
4062 case 'k':
anthony2f7f9ca2012-05-14 06:33:53 +00004063 if (LocaleCompare("kurtosis",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004064 {
4065 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004066 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004067 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004068 }
anthonya322a832013-04-27 06:28:03 +00004069 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004070 case 'L':
4071 case 'l':
4072 {
anthony2f7f9ca2012-05-14 06:33:53 +00004073 if (LocaleCompare("loop",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004074 {
cristye27293e2009-12-18 02:53:20 +00004075 image->iterations=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00004076 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004077 }
anthonya322a832013-04-27 06:28:03 +00004078 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004079 }
anthony6e2e0732012-05-06 12:22:43 +00004080 case 'M':
4081 case 'm':
anthony2f7f9ca2012-05-14 06:33:53 +00004082 if ( (LocaleCompare("magick",property) == 0) ||
4083 (LocaleCompare("max",property) == 0) ||
4084 (LocaleCompare("mean",property) == 0) ||
4085 (LocaleCompare("min",property) == 0) ||
4086 (LocaleCompare("min",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00004087 {
cristy5048d302012-08-07 01:05:16 +00004088 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +00004089 "SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004090 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004091 }
anthonya322a832013-04-27 06:28:03 +00004092 break; /* not an attribute, add as a property */
anthony6e2e0732012-05-06 12:22:43 +00004093 case 'O':
4094 case 'o':
anthony2f7f9ca2012-05-14 06:33:53 +00004095 if (LocaleCompare("opaque",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004096 {
4097 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004098 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004099 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004100 }
anthonya322a832013-04-27 06:28:03 +00004101 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004102 case 'P':
4103 case 'p':
4104 {
anthony2f7f9ca2012-05-14 06:33:53 +00004105 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004106 {
4107 char
4108 *geometry;
4109
4110 geometry=GetPageGeometry(value);
4111 flags=ParseAbsoluteGeometry(geometry,&image->page);
4112 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00004113 return(MagickTrue);
4114 }
anthonya322a832013-04-27 06:28:03 +00004115#if 0 /* Percent escape's sets values with this prefix: for later use
4116 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004117 if (LocaleNCompare("pixel:",property,6) == 0)
4118 {
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);
cristy3ed852e2009-09-05 21:47:34 +00004122 }
anthonyc99875a2012-11-14 05:22:17 +00004123#endif
anthony2f7f9ca2012-05-14 06:33:53 +00004124 if (LocaleCompare("profile",property) == 0)
cristy071dd7b2010-04-09 13:04:54 +00004125 {
4126 ImageInfo
4127 *image_info;
4128
4129 StringInfo
4130 *profile;
4131
4132 image_info=AcquireImageInfo();
cristy151b66d2015-04-15 10:50:31 +00004133 (void) CopyMagickString(image_info->filename,value,MagickPathExtent);
cristyc6c08ab2010-07-24 23:50:09 +00004134 (void) SetImageInfo(image_info,1,exception);
4135 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00004136 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00004137 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00004138 image_info=DestroyImageInfo(image_info);
anthony643c6132012-11-07 14:50:28 +00004139 return(MagickTrue);
cristy071dd7b2010-04-09 13:04:54 +00004140 }
anthonya322a832013-04-27 06:28:03 +00004141 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004142 }
4143 case 'R':
4144 case 'r':
4145 {
anthony2f7f9ca2012-05-14 06:33:53 +00004146 if (LocaleCompare("rendering-intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004147 {
cristybb503372010-05-27 20:51:26 +00004148 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004149 rendering_intent;
4150
cristy042ee782011-04-22 18:48:30 +00004151 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004152 value);
4153 if (rendering_intent < 0)
anthonya322a832013-04-27 06:28:03 +00004154 return(MagickFalse); /* FUTURE: value exception?? */
anthony643c6132012-11-07 14:50:28 +00004155 image->rendering_intent=(RenderingIntent) rendering_intent;
4156 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004157 }
anthonya322a832013-04-27 06:28:03 +00004158 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004159 }
anthony6e2e0732012-05-06 12:22:43 +00004160 case 'S':
4161 case 's':
anthony2f7f9ca2012-05-14 06:33:53 +00004162 if ( (LocaleCompare("size",property) == 0) ||
4163 (LocaleCompare("skewness",property) == 0) ||
4164 (LocaleCompare("scenes",property) == 0) ||
4165 (LocaleCompare("standard-deviation",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00004166 {
4167 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004168 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004169 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004170 }
anthonya322a832013-04-27 06:28:03 +00004171 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004172 case 'T':
4173 case 't':
4174 {
anthony2f7f9ca2012-05-14 06:33:53 +00004175 if (LocaleCompare("tile-offset",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004176 {
4177 char
4178 *geometry;
4179
4180 geometry=GetPageGeometry(value);
4181 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
4182 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00004183 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004184 }
anthonya322a832013-04-27 06:28:03 +00004185 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004186 }
cristyfbb56842010-08-02 11:26:33 +00004187 case 'U':
4188 case 'u':
4189 {
anthony2f7f9ca2012-05-14 06:33:53 +00004190 if (LocaleCompare("units",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00004191 {
4192 ssize_t
4193 units;
4194
cristy042ee782011-04-22 18:48:30 +00004195 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00004196 if (units < 0)
anthonya322a832013-04-27 06:28:03 +00004197 return(MagickFalse); /* FUTURE: value exception?? */
cristyfbb56842010-08-02 11:26:33 +00004198 image->units=(ResolutionType) units;
anthony643c6132012-11-07 14:50:28 +00004199 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00004200 }
anthonya322a832013-04-27 06:28:03 +00004201 break; /* not an attribute, add as a property */
cristyfbb56842010-08-02 11:26:33 +00004202 }
anthony6e2e0732012-05-06 12:22:43 +00004203 case 'V':
4204 case 'v':
anthony643c6132012-11-07 14:50:28 +00004205 {
anthony2f7f9ca2012-05-14 06:33:53 +00004206 if (LocaleCompare("version",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004207 {
4208 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004209 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004210 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004211 }
anthonya322a832013-04-27 06:28:03 +00004212 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004213 }
anthony6e2e0732012-05-06 12:22:43 +00004214 case 'W':
4215 case 'w':
anthony643c6132012-11-07 14:50:28 +00004216 {
anthony2f7f9ca2012-05-14 06:33:53 +00004217 if (LocaleCompare("width",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004218 {
4219 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004220 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004221 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004222 }
anthonya322a832013-04-27 06:28:03 +00004223 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004224 }
anthonya322a832013-04-27 06:28:03 +00004225#if 0 /* Percent escape's sets values with this prefix: for later use
4226 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004227 case 'X':
4228 case 'x':
cristy3ed852e2009-09-05 21:47:34 +00004229 {
anthony643c6132012-11-07 14:50:28 +00004230 if (LocaleNCompare("xmp:",property,4) == 0)
4231 {
4232 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004233 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004234 return(MagickFalse);
4235 }
anthonya322a832013-04-27 06:28:03 +00004236 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004237 }
anthonyc99875a2012-11-14 05:22:17 +00004238#endif
cristy3ed852e2009-09-05 21:47:34 +00004239 }
anthonya322a832013-04-27 06:28:03 +00004240 /* Default: not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004241 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
4242 ConstantString(property),ConstantString(value));
4243 /* FUTURE: error if status is bad? */
cristy3ed852e2009-09-05 21:47:34 +00004244 return(status);
4245}