blob: 036fabf340def742a28e2d559c5fad03922b9c78 [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)
88#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
89#include <lcms/lcms2.h>
90#elif defined(MAGICKCORE_HAVE_LCMS2_H)
91#include "lcms2.h"
92#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
93#include <lcms/lcms.h>
94#else
95#include "lcms.h"
96#endif
97#endif
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);
137 assert(image->signature == MagickSignature);
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);
141 assert(clone_image->signature == MagickSignature);
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);
145 (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
146 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
147 MaxTextExtent);
148 image->compression=clone_image->compression;
149 image->quality=clone_image->quality;
150 image->depth=clone_image->depth;
151 image->background_color=clone_image->background_color;
152 image->border_color=clone_image->border_color;
153 image->matte_color=clone_image->matte_color;
154 image->transparent_color=clone_image->transparent_color;
155 image->gamma=clone_image->gamma;
156 image->chromaticity=clone_image->chromaticity;
157 image->rendering_intent=clone_image->rendering_intent;
158 image->black_point_compensation=clone_image->black_point_compensation;
159 image->units=clone_image->units;
160 image->montage=(char *) NULL;
161 image->directory=(char *) NULL;
162 (void) CloneString(&image->geometry,clone_image->geometry);
163 image->offset=clone_image->offset;
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
234 key[MaxTextExtent],
235 value[MaxTextExtent];
236
237 register char
238 *p;
239
240 assert(image != (Image *) NULL);
241 assert(property != (const char *) NULL);
242 (void) CopyMagickString(key,property,MaxTextExtent-1);
243 for (p=key; *p != '\0'; p++)
244 if (*p == '=')
245 break;
246 *value='\0';
247 if (*p == '=')
248 (void) CopyMagickString(value,p+1,MaxTextExtent);
249 *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);
281 assert(image->signature == MagickSignature);
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);
316 assert(image->signature == MagickSignature);
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
358 value[MaxTextExtent];
359
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);
cristydb584ae2011-05-20 14:50:53 +0000373 n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
374 (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
cristybb503372010-05-27 20:51:26 +0000490static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000491{
492 if (x > y)
493 return(x);
494 return(y);
495}
496
cristy4a8b0172012-02-03 16:39:53 +0000497static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
498{
499 if (x < y)
500 return(x);
501 return(y);
502}
503
cristy3ed852e2009-09-05 21:47:34 +0000504static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
505{
506 int
507 c;
508
509 if (*length < 1)
510 return(EOF);
511 c=(int) (*(*p)++);
512 (*length)--;
513 return(c);
514}
515
cristybb503372010-05-27 20:51:26 +0000516static inline size_t ReadPropertyMSBLong(const unsigned char **p,
cristy3ed852e2009-09-05 21:47:34 +0000517 size_t *length)
518{
519 int
520 c;
521
cristybb503372010-05-27 20:51:26 +0000522 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000523 i;
524
525 unsigned char
526 buffer[4];
527
cristybb503372010-05-27 20:51:26 +0000528 size_t
cristy3ed852e2009-09-05 21:47:34 +0000529 value;
530
531 if (*length < 4)
532 return(~0UL);
533 for (i=0; i < 4; i++)
534 {
535 c=(int) (*(*p)++);
536 (*length)--;
537 buffer[i]=(unsigned char) c;
538 }
cristybb503372010-05-27 20:51:26 +0000539 value=(size_t) (buffer[0] << 24);
cristy3ed852e2009-09-05 21:47:34 +0000540 value|=buffer[1] << 16;
541 value|=buffer[2] << 8;
542 value|=buffer[3];
543 return(value & 0xffffffff);
544}
545
546static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
547 size_t *length)
548{
549 int
550 c;
551
cristybb503372010-05-27 20:51:26 +0000552 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000553 i;
554
555 unsigned char
556 buffer[2];
557
558 unsigned short
559 value;
560
561 if (*length < 2)
dirk93b02b72013-11-16 16:03:36 +0000562 return((unsigned short) ~0);
cristy3ed852e2009-09-05 21:47:34 +0000563 for (i=0; i < 2; i++)
564 {
565 c=(int) (*(*p)++);
566 (*length)--;
567 buffer[i]=(unsigned char) c;
568 }
569 value=(unsigned short) (buffer[0] << 8);
570 value|=buffer[1];
571 return((unsigned short) (value & 0xffff));
572}
573
cristyd15e6592011-10-15 00:13:06 +0000574static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
575 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000576{
577 char
578 *attribute,
579 format[MaxTextExtent],
580 name[MaxTextExtent],
581 *resource;
582
583 const StringInfo
584 *profile;
585
586 const unsigned char
587 *info;
588
cristycee97112010-05-28 00:44:52 +0000589 long
cristy3ed852e2009-09-05 21:47:34 +0000590 start,
cristycee97112010-05-28 00:44:52 +0000591 stop;
cristy3ed852e2009-09-05 21:47:34 +0000592
593 MagickBooleanType
594 status;
595
cristybb503372010-05-27 20:51:26 +0000596 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000597 i;
598
599 ssize_t
cristycee97112010-05-28 00:44:52 +0000600 count,
601 id,
602 sub_number;
cristy3ed852e2009-09-05 21:47:34 +0000603
604 size_t
605 length;
606
607 /*
glennrp2cc891a2010-12-24 13:44:32 +0000608 There are no newlines in path names, so it's safe as terminator.
cristy3ed852e2009-09-05 21:47:34 +0000609 */
610 profile=GetImageProfile(image,"8bim");
611 if (profile == (StringInfo *) NULL)
612 return(MagickFalse);
613 count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
614 format);
615 if ((count != 2) && (count != 3) && (count != 4))
616 return(MagickFalse);
617 if (count < 4)
618 (void) CopyMagickString(format,"SVG",MaxTextExtent);
619 if (count < 3)
620 *name='\0';
621 sub_number=1;
622 if (*name == '#')
cristyad740052010-07-03 01:38:03 +0000623 sub_number=(ssize_t) StringToLong(&name[1]);
cristy3ed852e2009-09-05 21:47:34 +0000624 sub_number=MagickMax(sub_number,1L);
625 resource=(char *) NULL;
626 status=MagickFalse;
627 length=GetStringInfoLength(profile);
628 info=GetStringInfoDatum(profile);
anthony2fbb5952012-05-05 12:35:30 +0000629 while ((length > 0) && IfMagickFalse(status))
cristy3ed852e2009-09-05 21:47:34 +0000630 {
631 if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
632 continue;
633 if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
634 continue;
635 if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
636 continue;
637 if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
638 continue;
cristy4a8b0172012-02-03 16:39:53 +0000639 id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
cristycee97112010-05-28 00:44:52 +0000640 if (id < (ssize_t) start)
cristy3ed852e2009-09-05 21:47:34 +0000641 continue;
cristycee97112010-05-28 00:44:52 +0000642 if (id > (ssize_t) stop)
cristy3ed852e2009-09-05 21:47:34 +0000643 continue;
644 if (resource != (char *) NULL)
645 resource=DestroyString(resource);
646 count=(ssize_t) ReadPropertyByte(&info,&length);
647 if ((count != 0) && ((size_t) count <= length))
648 {
649 resource=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +0000650 if (~((size_t) count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000651 resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
652 sizeof(*resource));
653 if (resource != (char *) NULL)
654 {
cristybb503372010-05-27 20:51:26 +0000655 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000656 resource[i]=(char) ReadPropertyByte(&info,&length);
657 resource[count]='\0';
658 }
659 }
660 if ((count & 0x01) == 0)
661 (void) ReadPropertyByte(&info,&length);
cristy55a91cd2010-12-01 00:57:40 +0000662 count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
cristy3ed852e2009-09-05 21:47:34 +0000663 if ((*name != '\0') && (*name != '#'))
664 if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
665 {
666 /*
667 No name match, scroll forward and try next.
668 */
669 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000670 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000671 continue;
672 }
673 if ((*name == '#') && (sub_number != 1))
674 {
675 /*
676 No numbered match, scroll forward and try next.
677 */
678 sub_number--;
679 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000680 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000681 continue;
682 }
683 /*
684 We have the resource of interest.
685 */
686 attribute=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +0000687 if (~((size_t) count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000688 attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
689 sizeof(*attribute));
690 if (attribute != (char *) NULL)
691 {
692 (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
693 attribute[count]='\0';
694 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000695 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000696 if ((id <= 1999) || (id >= 2999))
697 (void) SetImageProperty((Image *) image,key,(const char *)
cristyd15e6592011-10-15 00:13:06 +0000698 attribute,exception);
cristy3ed852e2009-09-05 21:47:34 +0000699 else
700 {
701 char
702 *path;
703
704 if (LocaleCompare(format,"svg") == 0)
705 path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
706 image->columns,image->rows);
707 else
708 path=TracePSClippath((unsigned char *) attribute,(size_t) count,
709 image->columns,image->rows);
cristyd15e6592011-10-15 00:13:06 +0000710 (void) SetImageProperty((Image *) image,key,(const char *) path,
711 exception);
cristy3ed852e2009-09-05 21:47:34 +0000712 path=DestroyString(path);
713 }
714 attribute=DestroyString(attribute);
715 status=MagickTrue;
716 }
717 }
718 if (resource != (char *) NULL)
719 resource=DestroyString(resource);
720 return(status);
721}
722
723static inline unsigned short ReadPropertyShort(const EndianType endian,
724 const unsigned char *buffer)
725{
726 unsigned short
727 value;
728
cristy1715f792012-11-22 16:56:12 +0000729 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +0000730 {
cristy1715f792012-11-22 16:56:12 +0000731 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
cristy3ed852e2009-09-05 21:47:34 +0000732 return((unsigned short) (value & 0xffff));
733 }
cristy1715f792012-11-22 16:56:12 +0000734 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
735 ((unsigned char *) buffer)[1]);
cristy3ed852e2009-09-05 21:47:34 +0000736 return((unsigned short) (value & 0xffff));
737}
738
cristybb503372010-05-27 20:51:26 +0000739static inline size_t ReadPropertyLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +0000740 const unsigned char *buffer)
741{
cristybb503372010-05-27 20:51:26 +0000742 size_t
cristy3ed852e2009-09-05 21:47:34 +0000743 value;
744
cristy1715f792012-11-22 16:56:12 +0000745 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +0000746 {
cristy1715f792012-11-22 16:56:12 +0000747 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
748 (buffer[1] << 8 ) | (buffer[0]));
cristybb503372010-05-27 20:51:26 +0000749 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000750 }
cristy1715f792012-11-22 16:56:12 +0000751 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
752 (buffer[2] << 8) | buffer[3]);
cristybb503372010-05-27 20:51:26 +0000753 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000754}
755
756static MagickBooleanType GetEXIFProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +0000757 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000758{
759#define MaxDirectoryStack 16
760#define EXIF_DELIMITER "\n"
761#define EXIF_NUM_FORMATS 12
762#define EXIF_FMT_BYTE 1
763#define EXIF_FMT_STRING 2
764#define EXIF_FMT_USHORT 3
765#define EXIF_FMT_ULONG 4
766#define EXIF_FMT_URATIONAL 5
767#define EXIF_FMT_SBYTE 6
768#define EXIF_FMT_UNDEFINED 7
769#define EXIF_FMT_SSHORT 8
770#define EXIF_FMT_SLONG 9
771#define EXIF_FMT_SRATIONAL 10
772#define EXIF_FMT_SINGLE 11
773#define EXIF_FMT_DOUBLE 12
774#define TAG_EXIF_OFFSET 0x8769
775#define TAG_GPS_OFFSET 0x8825
776#define TAG_INTEROP_OFFSET 0xa005
777
cristy96ea4862011-11-22 00:45:47 +0000778#define EXIFMultipleValues(size,format,arg) \
cristy3ed852e2009-09-05 21:47:34 +0000779{ \
cristybb503372010-05-27 20:51:26 +0000780 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000781 component; \
782 \
783 size_t \
784 length; \
785 \
786 unsigned char \
787 *p1; \
788 \
789 length=0; \
790 p1=p; \
791 for (component=0; component < components; component++) \
792 { \
cristyb51dff52011-05-19 16:55:47 +0000793 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristy3ed852e2009-09-05 21:47:34 +0000794 format", ",arg); \
cristy37e0b382011-06-07 13:31:21 +0000795 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000796 length=MaxTextExtent-1; \
797 p1+=size; \
798 } \
799 if (length > 1) \
800 buffer[length-2]='\0'; \
801 value=AcquireString(buffer); \
802}
803
cristy96ea4862011-11-22 00:45:47 +0000804#define EXIFMultipleFractions(size,format,arg1,arg2) \
cristy3ed852e2009-09-05 21:47:34 +0000805{ \
cristybb503372010-05-27 20:51:26 +0000806 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000807 component; \
808 \
809 size_t \
810 length; \
811 \
812 unsigned char \
813 *p1; \
814 \
815 length=0; \
816 p1=p; \
817 for (component=0; component < components; component++) \
818 { \
cristyb51dff52011-05-19 16:55:47 +0000819 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristyd66c1082012-03-28 18:17:37 +0000820 format", ",(arg1),(arg2)); \
cristy37e0b382011-06-07 13:31:21 +0000821 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000822 length=MaxTextExtent-1; \
823 p1+=size; \
824 } \
825 if (length > 1) \
826 buffer[length-2]='\0'; \
827 value=AcquireString(buffer); \
828}
829
830 typedef struct _DirectoryInfo
831 {
832 const unsigned char
833 *directory;
834
cristybb503372010-05-27 20:51:26 +0000835 size_t
cristy3d8d1f12012-02-29 01:59:28 +0000836 entry;
837
838 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000839 offset;
840 } DirectoryInfo;
841
842 typedef struct _TagInfo
843 {
cristybb503372010-05-27 20:51:26 +0000844 size_t
cristy3ed852e2009-09-05 21:47:34 +0000845 tag;
846
847 const char
848 *description;
849 } TagInfo;
850
851 static TagInfo
852 EXIFTag[] =
853 {
854 { 0x001, "exif:InteroperabilityIndex" },
855 { 0x002, "exif:InteroperabilityVersion" },
856 { 0x100, "exif:ImageWidth" },
857 { 0x101, "exif:ImageLength" },
858 { 0x102, "exif:BitsPerSample" },
859 { 0x103, "exif:Compression" },
860 { 0x106, "exif:PhotometricInterpretation" },
861 { 0x10a, "exif:FillOrder" },
862 { 0x10d, "exif:DocumentName" },
863 { 0x10e, "exif:ImageDescription" },
864 { 0x10f, "exif:Make" },
865 { 0x110, "exif:Model" },
866 { 0x111, "exif:StripOffsets" },
867 { 0x112, "exif:Orientation" },
868 { 0x115, "exif:SamplesPerPixel" },
869 { 0x116, "exif:RowsPerStrip" },
870 { 0x117, "exif:StripByteCounts" },
871 { 0x11a, "exif:XResolution" },
872 { 0x11b, "exif:YResolution" },
873 { 0x11c, "exif:PlanarConfiguration" },
874 { 0x11d, "exif:PageName" },
875 { 0x11e, "exif:XPosition" },
876 { 0x11f, "exif:YPosition" },
877 { 0x118, "exif:MinSampleValue" },
878 { 0x119, "exif:MaxSampleValue" },
879 { 0x120, "exif:FreeOffsets" },
880 { 0x121, "exif:FreeByteCounts" },
881 { 0x122, "exif:GrayResponseUnit" },
882 { 0x123, "exif:GrayResponseCurve" },
883 { 0x124, "exif:T4Options" },
884 { 0x125, "exif:T6Options" },
885 { 0x128, "exif:ResolutionUnit" },
886 { 0x12d, "exif:TransferFunction" },
887 { 0x131, "exif:Software" },
888 { 0x132, "exif:DateTime" },
889 { 0x13b, "exif:Artist" },
890 { 0x13e, "exif:WhitePoint" },
891 { 0x13f, "exif:PrimaryChromaticities" },
892 { 0x140, "exif:ColorMap" },
893 { 0x141, "exif:HalfToneHints" },
894 { 0x142, "exif:TileWidth" },
895 { 0x143, "exif:TileLength" },
896 { 0x144, "exif:TileOffsets" },
897 { 0x145, "exif:TileByteCounts" },
898 { 0x14a, "exif:SubIFD" },
899 { 0x14c, "exif:InkSet" },
900 { 0x14d, "exif:InkNames" },
901 { 0x14e, "exif:NumberOfInks" },
902 { 0x150, "exif:DotRange" },
903 { 0x151, "exif:TargetPrinter" },
904 { 0x152, "exif:ExtraSample" },
905 { 0x153, "exif:SampleFormat" },
906 { 0x154, "exif:SMinSampleValue" },
907 { 0x155, "exif:SMaxSampleValue" },
908 { 0x156, "exif:TransferRange" },
909 { 0x157, "exif:ClipPath" },
910 { 0x158, "exif:XClipPathUnits" },
911 { 0x159, "exif:YClipPathUnits" },
912 { 0x15a, "exif:Indexed" },
913 { 0x15b, "exif:JPEGTables" },
914 { 0x15f, "exif:OPIProxy" },
915 { 0x200, "exif:JPEGProc" },
916 { 0x201, "exif:JPEGInterchangeFormat" },
917 { 0x202, "exif:JPEGInterchangeFormatLength" },
918 { 0x203, "exif:JPEGRestartInterval" },
919 { 0x205, "exif:JPEGLosslessPredictors" },
920 { 0x206, "exif:JPEGPointTransforms" },
921 { 0x207, "exif:JPEGQTables" },
922 { 0x208, "exif:JPEGDCTables" },
923 { 0x209, "exif:JPEGACTables" },
924 { 0x211, "exif:YCbCrCoefficients" },
925 { 0x212, "exif:YCbCrSubSampling" },
926 { 0x213, "exif:YCbCrPositioning" },
927 { 0x214, "exif:ReferenceBlackWhite" },
928 { 0x2bc, "exif:ExtensibleMetadataPlatform" },
929 { 0x301, "exif:Gamma" },
930 { 0x302, "exif:ICCProfileDescriptor" },
931 { 0x303, "exif:SRGBRenderingIntent" },
932 { 0x320, "exif:ImageTitle" },
933 { 0x5001, "exif:ResolutionXUnit" },
934 { 0x5002, "exif:ResolutionYUnit" },
935 { 0x5003, "exif:ResolutionXLengthUnit" },
936 { 0x5004, "exif:ResolutionYLengthUnit" },
937 { 0x5005, "exif:PrintFlags" },
938 { 0x5006, "exif:PrintFlagsVersion" },
939 { 0x5007, "exif:PrintFlagsCrop" },
940 { 0x5008, "exif:PrintFlagsBleedWidth" },
941 { 0x5009, "exif:PrintFlagsBleedWidthScale" },
942 { 0x500A, "exif:HalftoneLPI" },
943 { 0x500B, "exif:HalftoneLPIUnit" },
944 { 0x500C, "exif:HalftoneDegree" },
945 { 0x500D, "exif:HalftoneShape" },
946 { 0x500E, "exif:HalftoneMisc" },
947 { 0x500F, "exif:HalftoneScreen" },
948 { 0x5010, "exif:JPEGQuality" },
949 { 0x5011, "exif:GridSize" },
950 { 0x5012, "exif:ThumbnailFormat" },
951 { 0x5013, "exif:ThumbnailWidth" },
952 { 0x5014, "exif:ThumbnailHeight" },
953 { 0x5015, "exif:ThumbnailColorDepth" },
954 { 0x5016, "exif:ThumbnailPlanes" },
955 { 0x5017, "exif:ThumbnailRawBytes" },
956 { 0x5018, "exif:ThumbnailSize" },
957 { 0x5019, "exif:ThumbnailCompressedSize" },
958 { 0x501a, "exif:ColorTransferFunction" },
959 { 0x501b, "exif:ThumbnailData" },
960 { 0x5020, "exif:ThumbnailImageWidth" },
961 { 0x5021, "exif:ThumbnailImageHeight" },
962 { 0x5022, "exif:ThumbnailBitsPerSample" },
963 { 0x5023, "exif:ThumbnailCompression" },
964 { 0x5024, "exif:ThumbnailPhotometricInterp" },
965 { 0x5025, "exif:ThumbnailImageDescription" },
966 { 0x5026, "exif:ThumbnailEquipMake" },
967 { 0x5027, "exif:ThumbnailEquipModel" },
968 { 0x5028, "exif:ThumbnailStripOffsets" },
969 { 0x5029, "exif:ThumbnailOrientation" },
970 { 0x502a, "exif:ThumbnailSamplesPerPixel" },
971 { 0x502b, "exif:ThumbnailRowsPerStrip" },
972 { 0x502c, "exif:ThumbnailStripBytesCount" },
973 { 0x502d, "exif:ThumbnailResolutionX" },
974 { 0x502e, "exif:ThumbnailResolutionY" },
975 { 0x502f, "exif:ThumbnailPlanarConfig" },
976 { 0x5030, "exif:ThumbnailResolutionUnit" },
977 { 0x5031, "exif:ThumbnailTransferFunction" },
978 { 0x5032, "exif:ThumbnailSoftwareUsed" },
979 { 0x5033, "exif:ThumbnailDateTime" },
980 { 0x5034, "exif:ThumbnailArtist" },
981 { 0x5035, "exif:ThumbnailWhitePoint" },
982 { 0x5036, "exif:ThumbnailPrimaryChromaticities" },
983 { 0x5037, "exif:ThumbnailYCbCrCoefficients" },
984 { 0x5038, "exif:ThumbnailYCbCrSubsampling" },
985 { 0x5039, "exif:ThumbnailYCbCrPositioning" },
986 { 0x503A, "exif:ThumbnailRefBlackWhite" },
987 { 0x503B, "exif:ThumbnailCopyRight" },
988 { 0x5090, "exif:LuminanceTable" },
989 { 0x5091, "exif:ChrominanceTable" },
990 { 0x5100, "exif:FrameDelay" },
991 { 0x5101, "exif:LoopCount" },
992 { 0x5110, "exif:PixelUnit" },
993 { 0x5111, "exif:PixelPerUnitX" },
994 { 0x5112, "exif:PixelPerUnitY" },
995 { 0x5113, "exif:PaletteHistogram" },
996 { 0x1000, "exif:RelatedImageFileFormat" },
997 { 0x1001, "exif:RelatedImageLength" },
998 { 0x1002, "exif:RelatedImageWidth" },
999 { 0x800d, "exif:ImageID" },
1000 { 0x80e3, "exif:Matteing" },
1001 { 0x80e4, "exif:DataType" },
1002 { 0x80e5, "exif:ImageDepth" },
1003 { 0x80e6, "exif:TileDepth" },
1004 { 0x828d, "exif:CFARepeatPatternDim" },
1005 { 0x828e, "exif:CFAPattern2" },
1006 { 0x828f, "exif:BatteryLevel" },
1007 { 0x8298, "exif:Copyright" },
1008 { 0x829a, "exif:ExposureTime" },
1009 { 0x829d, "exif:FNumber" },
1010 { 0x83bb, "exif:IPTC/NAA" },
1011 { 0x84e3, "exif:IT8RasterPadding" },
1012 { 0x84e5, "exif:IT8ColorTable" },
1013 { 0x8649, "exif:ImageResourceInformation" },
1014 { 0x8769, "exif:ExifOffset" },
1015 { 0x8773, "exif:InterColorProfile" },
1016 { 0x8822, "exif:ExposureProgram" },
1017 { 0x8824, "exif:SpectralSensitivity" },
1018 { 0x8825, "exif:GPSInfo" },
1019 { 0x8827, "exif:ISOSpeedRatings" },
1020 { 0x8828, "exif:OECF" },
1021 { 0x8829, "exif:Interlace" },
1022 { 0x882a, "exif:TimeZoneOffset" },
1023 { 0x882b, "exif:SelfTimerMode" },
1024 { 0x9000, "exif:ExifVersion" },
1025 { 0x9003, "exif:DateTimeOriginal" },
1026 { 0x9004, "exif:DateTimeDigitized" },
1027 { 0x9101, "exif:ComponentsConfiguration" },
1028 { 0x9102, "exif:CompressedBitsPerPixel" },
1029 { 0x9201, "exif:ShutterSpeedValue" },
1030 { 0x9202, "exif:ApertureValue" },
1031 { 0x9203, "exif:BrightnessValue" },
1032 { 0x9204, "exif:ExposureBiasValue" },
1033 { 0x9205, "exif:MaxApertureValue" },
1034 { 0x9206, "exif:SubjectDistance" },
1035 { 0x9207, "exif:MeteringMode" },
1036 { 0x9208, "exif:LightSource" },
1037 { 0x9209, "exif:Flash" },
1038 { 0x920a, "exif:FocalLength" },
1039 { 0x920b, "exif:FlashEnergy" },
1040 { 0x920c, "exif:SpatialFrequencyResponse" },
1041 { 0x920d, "exif:Noise" },
1042 { 0x9211, "exif:ImageNumber" },
1043 { 0x9212, "exif:SecurityClassification" },
1044 { 0x9213, "exif:ImageHistory" },
1045 { 0x9214, "exif:SubjectArea" },
1046 { 0x9215, "exif:ExposureIndex" },
1047 { 0x9216, "exif:TIFF-EPStandardID" },
1048 { 0x927c, "exif:MakerNote" },
1049 { 0x9C9b, "exif:WinXP-Title" },
1050 { 0x9C9c, "exif:WinXP-Comments" },
1051 { 0x9C9d, "exif:WinXP-Author" },
1052 { 0x9C9e, "exif:WinXP-Keywords" },
1053 { 0x9C9f, "exif:WinXP-Subject" },
1054 { 0x9286, "exif:UserComment" },
1055 { 0x9290, "exif:SubSecTime" },
1056 { 0x9291, "exif:SubSecTimeOriginal" },
1057 { 0x9292, "exif:SubSecTimeDigitized" },
1058 { 0xa000, "exif:FlashPixVersion" },
1059 { 0xa001, "exif:ColorSpace" },
1060 { 0xa002, "exif:ExifImageWidth" },
1061 { 0xa003, "exif:ExifImageLength" },
1062 { 0xa004, "exif:RelatedSoundFile" },
1063 { 0xa005, "exif:InteroperabilityOffset" },
1064 { 0xa20b, "exif:FlashEnergy" },
1065 { 0xa20c, "exif:SpatialFrequencyResponse" },
1066 { 0xa20d, "exif:Noise" },
1067 { 0xa20e, "exif:FocalPlaneXResolution" },
1068 { 0xa20f, "exif:FocalPlaneYResolution" },
1069 { 0xa210, "exif:FocalPlaneResolutionUnit" },
1070 { 0xa214, "exif:SubjectLocation" },
1071 { 0xa215, "exif:ExposureIndex" },
1072 { 0xa216, "exif:TIFF/EPStandardID" },
1073 { 0xa217, "exif:SensingMethod" },
1074 { 0xa300, "exif:FileSource" },
1075 { 0xa301, "exif:SceneType" },
1076 { 0xa302, "exif:CFAPattern" },
1077 { 0xa401, "exif:CustomRendered" },
1078 { 0xa402, "exif:ExposureMode" },
1079 { 0xa403, "exif:WhiteBalance" },
1080 { 0xa404, "exif:DigitalZoomRatio" },
1081 { 0xa405, "exif:FocalLengthIn35mmFilm" },
1082 { 0xa406, "exif:SceneCaptureType" },
1083 { 0xa407, "exif:GainControl" },
1084 { 0xa408, "exif:Contrast" },
1085 { 0xa409, "exif:Saturation" },
1086 { 0xa40a, "exif:Sharpness" },
1087 { 0xa40b, "exif:DeviceSettingDescription" },
1088 { 0xa40c, "exif:SubjectDistanceRange" },
1089 { 0xa420, "exif:ImageUniqueID" },
1090 { 0xc4a5, "exif:PrintImageMatching" },
cristyb3fea0e2009-11-28 01:46:20 +00001091 { 0xa500, "exif:Gamma" },
1092 { 0xc640, "exif:CR2Slice" },
cristy3ed852e2009-09-05 21:47:34 +00001093 { 0x10000, "exif:GPSVersionID" },
1094 { 0x10001, "exif:GPSLatitudeRef" },
1095 { 0x10002, "exif:GPSLatitude" },
1096 { 0x10003, "exif:GPSLongitudeRef" },
1097 { 0x10004, "exif:GPSLongitude" },
1098 { 0x10005, "exif:GPSAltitudeRef" },
1099 { 0x10006, "exif:GPSAltitude" },
1100 { 0x10007, "exif:GPSTimeStamp" },
1101 { 0x10008, "exif:GPSSatellites" },
1102 { 0x10009, "exif:GPSStatus" },
1103 { 0x1000a, "exif:GPSMeasureMode" },
1104 { 0x1000b, "exif:GPSDop" },
1105 { 0x1000c, "exif:GPSSpeedRef" },
1106 { 0x1000d, "exif:GPSSpeed" },
1107 { 0x1000e, "exif:GPSTrackRef" },
1108 { 0x1000f, "exif:GPSTrack" },
1109 { 0x10010, "exif:GPSImgDirectionRef" },
1110 { 0x10011, "exif:GPSImgDirection" },
1111 { 0x10012, "exif:GPSMapDatum" },
1112 { 0x10013, "exif:GPSDestLatitudeRef" },
1113 { 0x10014, "exif:GPSDestLatitude" },
1114 { 0x10015, "exif:GPSDestLongitudeRef" },
1115 { 0x10016, "exif:GPSDestLongitude" },
1116 { 0x10017, "exif:GPSDestBearingRef" },
1117 { 0x10018, "exif:GPSDestBearing" },
1118 { 0x10019, "exif:GPSDestDistanceRef" },
1119 { 0x1001a, "exif:GPSDestDistance" },
1120 { 0x1001b, "exif:GPSProcessingMethod" },
1121 { 0x1001c, "exif:GPSAreaInformation" },
1122 { 0x1001d, "exif:GPSDateStamp" },
1123 { 0x1001e, "exif:GPSDifferential" },
cristy6e617e62012-09-29 14:12:23 +00001124 { 0x00000, (const char *) NULL }
cristy3ed852e2009-09-05 21:47:34 +00001125 };
1126
1127 const StringInfo
1128 *profile;
1129
1130 const unsigned char
1131 *directory,
1132 *exif;
1133
1134 DirectoryInfo
1135 directory_stack[MaxDirectoryStack];
1136
1137 EndianType
1138 endian;
1139
cristy929ea322011-02-21 15:21:35 +00001140 MagickBooleanType
1141 status;
cristy3ed852e2009-09-05 21:47:34 +00001142
cristybb503372010-05-27 20:51:26 +00001143 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001144 i;
1145
1146 size_t
cristy3ed852e2009-09-05 21:47:34 +00001147 entry,
cristy929ea322011-02-21 15:21:35 +00001148 length,
cristy3ed852e2009-09-05 21:47:34 +00001149 number_entries,
cristy3ed852e2009-09-05 21:47:34 +00001150 tag;
1151
cristy142ab012012-02-02 02:26:18 +00001152 SplayTreeInfo
1153 *exif_resources;
1154
cristy929ea322011-02-21 15:21:35 +00001155 ssize_t
1156 all,
1157 id,
1158 level,
1159 offset,
cristy3d8d1f12012-02-29 01:59:28 +00001160 tag_offset,
cristy929ea322011-02-21 15:21:35 +00001161 tag_value;
1162
1163 static int
1164 tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1165
cristy3ed852e2009-09-05 21:47:34 +00001166 /*
1167 If EXIF data exists, then try to parse the request for a tag.
1168 */
1169 profile=GetImageProfile(image,"exif");
1170 if (profile == (StringInfo *) NULL)
1171 return(MagickFalse);
1172 if ((property == (const char *) NULL) || (*property == '\0'))
1173 return(MagickFalse);
1174 while (isspace((int) ((unsigned char) *property)) != 0)
1175 property++;
1176 all=0;
1177 tag=(~0UL);
1178 switch (*(property+5))
1179 {
1180 case '*':
1181 {
1182 /*
1183 Caller has asked for all the tags in the EXIF data.
1184 */
1185 tag=0;
1186 all=1; /* return the data in description=value format */
1187 break;
1188 }
1189 case '!':
1190 {
1191 tag=0;
1192 all=2; /* return the data in tagid=value format */
1193 break;
1194 }
1195 case '#':
1196 case '@':
1197 {
1198 int
1199 c;
1200
1201 size_t
1202 n;
1203
1204 /*
1205 Check for a hex based tag specification first.
1206 */
1207 tag=(*(property+5) == '@') ? 1UL : 0UL;
1208 property+=6;
1209 n=strlen(property);
1210 if (n != 4)
1211 return(MagickFalse);
1212 /*
1213 Parse tag specification as a hex number.
1214 */
1215 n/=4;
1216 do
1217 {
cristybb503372010-05-27 20:51:26 +00001218 for (i=(ssize_t) n-1L; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +00001219 {
1220 c=(*property++);
1221 tag<<=4;
1222 if ((c >= '0') && (c <= '9'))
1223 tag|=(c-'0');
1224 else
1225 if ((c >= 'A') && (c <= 'F'))
1226 tag|=(c-('A'-10));
1227 else
1228 if ((c >= 'a') && (c <= 'f'))
1229 tag|=(c-('a'-10));
1230 else
1231 return(MagickFalse);
1232 }
1233 } while (*property != '\0');
1234 break;
1235 }
1236 default:
1237 {
1238 /*
1239 Try to match the text with a tag name instead.
1240 */
1241 for (i=0; ; i++)
1242 {
1243 if (EXIFTag[i].tag == 0)
1244 break;
1245 if (LocaleCompare(EXIFTag[i].description,property) == 0)
1246 {
cristybb503372010-05-27 20:51:26 +00001247 tag=(size_t) EXIFTag[i].tag;
cristy3ed852e2009-09-05 21:47:34 +00001248 break;
1249 }
1250 }
1251 break;
1252 }
1253 }
1254 if (tag == (~0UL))
1255 return(MagickFalse);
1256 length=GetStringInfoLength(profile);
1257 exif=GetStringInfoDatum(profile);
1258 while (length != 0)
1259 {
1260 if (ReadPropertyByte(&exif,&length) != 0x45)
1261 continue;
1262 if (ReadPropertyByte(&exif,&length) != 0x78)
1263 continue;
1264 if (ReadPropertyByte(&exif,&length) != 0x69)
1265 continue;
1266 if (ReadPropertyByte(&exif,&length) != 0x66)
1267 continue;
1268 if (ReadPropertyByte(&exif,&length) != 0x00)
1269 continue;
1270 if (ReadPropertyByte(&exif,&length) != 0x00)
1271 continue;
1272 break;
1273 }
1274 if (length < 16)
1275 return(MagickFalse);
cristyf0696672012-02-01 23:43:06 +00001276 id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
cristy3ed852e2009-09-05 21:47:34 +00001277 endian=LSBEndian;
1278 if (id == 0x4949)
1279 endian=LSBEndian;
1280 else
1281 if (id == 0x4D4D)
1282 endian=MSBEndian;
1283 else
1284 return(MagickFalse);
1285 if (ReadPropertyShort(endian,exif+2) != 0x002a)
1286 return(MagickFalse);
1287 /*
1288 This the offset to the first IFD.
1289 */
cristy55a91cd2010-12-01 00:57:40 +00001290 offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
cristy64de1572012-02-29 02:44:11 +00001291 if ((offset < 0) || (size_t) offset >= length)
cristy3ed852e2009-09-05 21:47:34 +00001292 return(MagickFalse);
1293 /*
1294 Set the pointer to the first IFD and follow it were it leads.
1295 */
cristy929ea322011-02-21 15:21:35 +00001296 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001297 directory=exif+offset;
1298 level=0;
1299 entry=0;
1300 tag_offset=0;
cristy142ab012012-02-02 02:26:18 +00001301 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1302 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1303 do
cristy3ed852e2009-09-05 21:47:34 +00001304 {
1305 /*
1306 If there is anything on the stack then pop it off.
1307 */
1308 if (level > 0)
1309 {
1310 level--;
1311 directory=directory_stack[level].directory;
1312 entry=directory_stack[level].entry;
1313 tag_offset=directory_stack[level].offset;
1314 }
1315 /*
1316 Determine how many entries there are in the current IFD.
1317 */
cristy4a8b0172012-02-03 16:39:53 +00001318 number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
cristy3ed852e2009-09-05 21:47:34 +00001319 for ( ; entry < number_entries; entry++)
1320 {
cristy3ed852e2009-09-05 21:47:34 +00001321 register unsigned char
1322 *p,
1323 *q;
1324
1325 size_t
anthony90ebc0d2012-04-28 08:10:02 +00001326 format;
cristy3ed852e2009-09-05 21:47:34 +00001327
cristy9d314ff2011-03-09 01:30:28 +00001328 ssize_t
anthony90ebc0d2012-04-28 08:10:02 +00001329 number_bytes,
cristy9d314ff2011-03-09 01:30:28 +00001330 components;
cristy3ed852e2009-09-05 21:47:34 +00001331
cristyf0696672012-02-01 23:43:06 +00001332 q=(unsigned char *) (directory+(12*entry)+2);
cristy142ab012012-02-02 02:26:18 +00001333 if (GetValueFromSplayTree(exif_resources,q) == q)
1334 break;
1335 (void) AddValueToSplayTree(exif_resources,q,q);
cristyf0696672012-02-01 23:43:06 +00001336 tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
1337 format=(size_t) ((int) ReadPropertyShort(endian,q+2));
cristy3ed852e2009-09-05 21:47:34 +00001338 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1339 break;
cristy55a91cd2010-12-01 00:57:40 +00001340 components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00001341 number_bytes=(size_t) components*tag_bytes[format];
cristy5f06bc12012-04-03 12:47:36 +00001342 if (number_bytes < components)
1343 break; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001344 if (number_bytes <= 4)
1345 p=q+8;
1346 else
1347 {
1348 ssize_t
1349 offset;
1350
1351 /*
1352 The directory entry contains an offset.
1353 */
cristy55a91cd2010-12-01 00:57:40 +00001354 offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
cristydca8d582014-10-29 15:53:19 +00001355 if ((offset < 0) || (size_t) offset >= length)
1356 continue;
anthony90ebc0d2012-04-28 08:10:02 +00001357 if ((ssize_t) (offset+number_bytes) < offset)
1358 continue; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001359 if ((size_t) (offset+number_bytes) > length)
1360 continue;
1361 p=(unsigned char *) (exif+offset);
1362 }
cristy90e72a02012-09-29 01:08:57 +00001363 if ((all != 0) || (tag == (size_t) tag_value))
cristy3ed852e2009-09-05 21:47:34 +00001364 {
1365 char
1366 buffer[MaxTextExtent],
1367 *value;
1368
cristy7ad8a132012-03-28 22:42:29 +00001369 value=(char *) NULL;
1370 *buffer='\0';
cristy3ed852e2009-09-05 21:47:34 +00001371 switch (format)
1372 {
1373 case EXIF_FMT_BYTE:
1374 case EXIF_FMT_UNDEFINED:
1375 {
cristy13adad02011-08-16 19:22:15 +00001376 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001377 break;
1378 }
1379 case EXIF_FMT_SBYTE:
1380 {
cristye8c25f92010-06-03 00:53:06 +00001381 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001382 break;
1383 }
1384 case EXIF_FMT_SSHORT:
1385 {
1386 EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
1387 break;
1388 }
1389 case EXIF_FMT_USHORT:
1390 {
1391 EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
1392 break;
1393 }
1394 case EXIF_FMT_ULONG:
1395 {
cristye8c25f92010-06-03 00:53:06 +00001396 EXIFMultipleValues(4,"%.20g",(double)
cristy4a8b0172012-02-03 16:39:53 +00001397 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001398 break;
1399 }
1400 case EXIF_FMT_SLONG:
1401 {
cristye8c25f92010-06-03 00:53:06 +00001402 EXIFMultipleValues(4,"%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001403 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001404 break;
1405 }
1406 case EXIF_FMT_URATIONAL:
1407 {
cristye8c25f92010-06-03 00:53:06 +00001408 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristyf0696672012-02-01 23:43:06 +00001409 ((int) ReadPropertyLong(endian,p1)),(double)
1410 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001411 break;
1412 }
1413 case EXIF_FMT_SRATIONAL:
1414 {
cristye8c25f92010-06-03 00:53:06 +00001415 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001416 ((int) ReadPropertyLong(endian,p1)),(double)
1417 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001418 break;
1419 }
1420 case EXIF_FMT_SINGLE:
1421 {
1422 EXIFMultipleValues(4,"%f",(double) *(float *) p1);
1423 break;
1424 }
1425 case EXIF_FMT_DOUBLE:
1426 {
1427 EXIFMultipleValues(8,"%f",*(double *) p1);
1428 break;
1429 }
1430 default:
1431 case EXIF_FMT_STRING:
1432 {
1433 value=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +00001434 if (~((size_t) number_bytes) >= 1)
cristy3ed852e2009-09-05 21:47:34 +00001435 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1436 sizeof(*value));
1437 if (value != (char *) NULL)
1438 {
cristybb503372010-05-27 20:51:26 +00001439 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001440 i;
1441
cristybb503372010-05-27 20:51:26 +00001442 for (i=0; i < (ssize_t) number_bytes; i++)
cristy3ed852e2009-09-05 21:47:34 +00001443 {
1444 value[i]='.';
1445 if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1446 value[i]=(char) p[i];
1447 }
1448 value[i]='\0';
1449 }
1450 break;
1451 }
1452 }
1453 if (value != (char *) NULL)
1454 {
1455 char
cristy6e617e62012-09-29 14:12:23 +00001456 *key;
cristy3ed852e2009-09-05 21:47:34 +00001457
1458 register const char
1459 *p;
1460
cristy6e617e62012-09-29 14:12:23 +00001461 key=AcquireString(property);
1462 if (level == 2)
1463 (void) SubstituteString(&key,"exif:","exif:thumbnail:");
cristy3ed852e2009-09-05 21:47:34 +00001464 switch (all)
1465 {
1466 case 1:
1467 {
1468 const char
1469 *description;
1470
cristybb503372010-05-27 20:51:26 +00001471 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001472 i;
1473
1474 description="unknown";
1475 for (i=0; ; i++)
1476 {
1477 if (EXIFTag[i].tag == 0)
1478 break;
cristybb503372010-05-27 20:51:26 +00001479 if ((ssize_t) EXIFTag[i].tag == tag_value)
cristy3ed852e2009-09-05 21:47:34 +00001480 {
1481 description=EXIFTag[i].description;
1482 break;
1483 }
1484 }
cristy96ea4862011-11-22 00:45:47 +00001485 (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
cristy3ed852e2009-09-05 21:47:34 +00001486 break;
1487 }
1488 case 2:
1489 {
1490 if (tag_value < 0x10000)
cristyb51dff52011-05-19 16:55:47 +00001491 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001492 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001493 else
1494 if (tag_value < 0x20000)
cristyb51dff52011-05-19 16:55:47 +00001495 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001496 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001497 else
cristyb51dff52011-05-19 16:55:47 +00001498 (void) FormatLocaleString(key,MaxTextExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001499 break;
1500 }
1501 }
1502 p=(const char *) NULL;
1503 if (image->properties != (void *) NULL)
1504 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1505 image->properties,key);
1506 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001507 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001508 value=DestroyString(value);
cristy6e617e62012-09-29 14:12:23 +00001509 key=DestroyString(key);
cristy929ea322011-02-21 15:21:35 +00001510 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001511 }
1512 }
1513 if ((tag_value == TAG_EXIF_OFFSET) ||
cristy4a8b0172012-02-03 16:39:53 +00001514 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
cristy3ed852e2009-09-05 21:47:34 +00001515 {
cristy3d8d1f12012-02-29 01:59:28 +00001516 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001517 offset;
1518
cristy3d8d1f12012-02-29 01:59:28 +00001519 offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
1520 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00001521 {
cristy3d8d1f12012-02-29 01:59:28 +00001522 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001523 tag_offset1;
1524
cristy3d8d1f12012-02-29 01:59:28 +00001525 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1526 0);
cristy3ed852e2009-09-05 21:47:34 +00001527 directory_stack[level].directory=directory;
1528 entry++;
1529 directory_stack[level].entry=entry;
1530 directory_stack[level].offset=tag_offset;
1531 level++;
1532 directory_stack[level].directory=exif+offset;
1533 directory_stack[level].offset=tag_offset1;
1534 directory_stack[level].entry=0;
1535 level++;
1536 if ((directory+2+(12*number_entries)) > (exif+length))
1537 break;
cristy3d8d1f12012-02-29 01:59:28 +00001538 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
cristy4a8b0172012-02-03 16:39:53 +00001539 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00001540 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00001541 (level < (MaxDirectoryStack-2)))
1542 {
1543 directory_stack[level].directory=exif+offset;
1544 directory_stack[level].entry=0;
1545 directory_stack[level].offset=tag_offset1;
1546 level++;
1547 }
1548 }
1549 break;
1550 }
1551 }
cristy142ab012012-02-02 02:26:18 +00001552 } while (level > 0);
1553 exif_resources=DestroySplayTree(exif_resources);
cristy929ea322011-02-21 15:21:35 +00001554 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001555}
1556
cristy920a0e72014-01-24 14:20:14 +00001557static MagickBooleanType GetICCProperty(const Image *image,const char *property,
cristy55166322014-01-23 01:53:24 +00001558 ExceptionInfo *exception)
1559{
1560 const StringInfo
1561 *profile;
1562
cristy920a0e72014-01-24 14:20:14 +00001563 magick_unreferenced(property);
cristy65c88e92014-01-24 14:15:27 +00001564
cristy55166322014-01-23 01:53:24 +00001565 profile=GetImageProfile(image,"icc");
1566 if (profile == (StringInfo *) NULL)
1567 profile=GetImageProfile(image,"icm");
1568 if (profile == (StringInfo *) NULL)
1569 return(MagickFalse);
cristy89da59a2014-02-02 19:54:09 +00001570 if (GetStringInfoLength(profile) < 128)
1571 return(MagickFalse); /* minimum ICC profile length */
cristy55166322014-01-23 01:53:24 +00001572#if defined(MAGICKCORE_LCMS_DELEGATE)
1573 {
1574 cmsHPROFILE
1575 icc_profile;
1576
1577 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
1578 (cmsUInt32Number) GetStringInfoLength(profile));
1579 if (icc_profile != (cmsHPROFILE *) NULL)
1580 {
1581#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
1582 const char
1583 *name;
1584
1585 name=cmsTakeProductName(icc_profile);
1586 if (name != (const char *) NULL)
1587 (void) SetImageProperty((Image *) image,"icc:name",name,exception);
1588#else
1589 char
1590 info[MaxTextExtent];
1591
1592 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
1593 "en","US",info,MaxTextExtent);
1594 (void) SetImageProperty((Image *) image,"icc:description",info,
1595 exception);
1596 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,
1597 "en","US",info,MaxTextExtent);
1598 (void) SetImageProperty((Image *) image,"icc:manufacturer",info,
1599 exception);
1600 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en",
1601 "US",info,MaxTextExtent);
1602 (void) SetImageProperty((Image *) image,"icc:model",info,exception);
1603 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,
1604 "en","US",info,MaxTextExtent);
1605 (void) SetImageProperty((Image *) image,"icc:copyright",info,exception);
1606#endif
1607 (void) cmsCloseProfile(icc_profile);
1608 }
1609 }
1610#endif
1611 return(MagickTrue);
1612}
1613
dirk90d68962014-05-23 11:11:17 +00001614static MagickBooleanType SkipXMPValue(const char *value)
1615{
1616 if (value == (const char*) NULL)
1617 return(MagickTrue);
cristye9054192014-09-24 11:53:14 +00001618 while (*value != '\0')
dirk90d68962014-05-23 11:11:17 +00001619 {
cristye9054192014-09-24 11:53:14 +00001620 if (isspace((int) ((unsigned char) *value)) == 0)
dirk90d68962014-05-23 11:11:17 +00001621 return(MagickFalse);
1622 value++;
1623 }
dirk90d68962014-05-23 11:11:17 +00001624 return(MagickTrue);
1625}
1626
cristy13adad02011-08-16 19:22:15 +00001627static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001628{
1629 char
1630 *xmp_profile;
1631
dirk90d68962014-05-23 11:11:17 +00001632 const char
1633 *content;
1634
cristy3ed852e2009-09-05 21:47:34 +00001635 const StringInfo
1636 *profile;
1637
1638 ExceptionInfo
1639 *exception;
1640
1641 MagickBooleanType
1642 status;
1643
1644 register const char
1645 *p;
1646
1647 XMLTreeInfo
1648 *child,
1649 *description,
1650 *node,
1651 *rdf,
1652 *xmp;
1653
1654 profile=GetImageProfile(image,"xmp");
1655 if (profile == (StringInfo *) NULL)
1656 return(MagickFalse);
1657 if ((property == (const char *) NULL) || (*property == '\0'))
1658 return(MagickFalse);
1659 xmp_profile=StringInfoToString(profile);
1660 if (xmp_profile == (char *) NULL)
1661 return(MagickFalse);
1662 for (p=xmp_profile; *p != '\0'; p++)
1663 if ((*p == '<') && (*(p+1) == 'x'))
1664 break;
1665 exception=AcquireExceptionInfo();
1666 xmp=NewXMLTree((char *) p,exception);
1667 xmp_profile=DestroyString(xmp_profile);
1668 exception=DestroyExceptionInfo(exception);
1669 if (xmp == (XMLTreeInfo *) NULL)
1670 return(MagickFalse);
1671 status=MagickFalse;
1672 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1673 if (rdf != (XMLTreeInfo *) NULL)
1674 {
1675 if (image->properties == (void *) NULL)
1676 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1677 RelinquishMagickMemory,RelinquishMagickMemory);
1678 description=GetXMLTreeChild(rdf,"rdf:Description");
1679 while (description != (XMLTreeInfo *) NULL)
1680 {
1681 node=GetXMLTreeChild(description,(const char *) NULL);
1682 while (node != (XMLTreeInfo *) NULL)
1683 {
1684 child=GetXMLTreeChild(node,(const char *) NULL);
dirk90d68962014-05-23 11:11:17 +00001685 content=GetXMLTreeContent(node);
cristy9745ca82014-05-23 20:10:13 +00001686 if ((child == (XMLTreeInfo *) NULL) &&
1687 (SkipXMPValue(content) == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001688 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
cristy9745ca82014-05-23 20:10:13 +00001689 ConstantString(GetXMLTreeTag(node)),ConstantString(content));
cristy3ed852e2009-09-05 21:47:34 +00001690 while (child != (XMLTreeInfo *) NULL)
1691 {
dirk90d68962014-05-23 11:11:17 +00001692 content=GetXMLTreeContent(child);
1693 if (SkipXMPValue(content) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001694 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
cristy9745ca82014-05-23 20:10:13 +00001695 ConstantString(GetXMLTreeTag(child)),ConstantString(content));
cristy3ed852e2009-09-05 21:47:34 +00001696 child=GetXMLTreeSibling(child);
1697 }
1698 node=GetXMLTreeSibling(node);
1699 }
1700 description=GetNextXMLTreeTag(description);
1701 }
1702 }
1703 xmp=DestroyXMLTree(xmp);
1704 return(status);
1705}
1706
1707static char *TracePSClippath(const unsigned char *blob,size_t length,
cristyf5d0a1a2012-02-03 12:33:10 +00001708 const size_t magick_unused(columns),const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001709{
1710 char
1711 *path,
1712 *message;
1713
cristy3ed852e2009-09-05 21:47:34 +00001714 MagickBooleanType
1715 in_subpath;
1716
1717 PointInfo
1718 first[3],
1719 last[3],
1720 point[3];
1721
cristybb503372010-05-27 20:51:26 +00001722 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001723 i,
1724 x;
1725
cristy9d314ff2011-03-09 01:30:28 +00001726 ssize_t
1727 knot_count,
1728 selector,
1729 y;
1730
cristy3ed852e2009-09-05 21:47:34 +00001731 path=AcquireString((char *) NULL);
1732 if (path == (char *) NULL)
1733 return((char *) NULL);
1734 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001735 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001736 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001737 (void) FormatLocaleString(message,MaxTextExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001738 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001739 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001740 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001741 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001742 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001743 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001744 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001745 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001746 " /v {currentpoint 6 2 roll curveto} bind def\n");
1747 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001748 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001749 " /y {2 copy curveto} bind def\n");
1750 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001751 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001752 " /z {closepath} bind def\n");
1753 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001754 (void) FormatLocaleString(message,MaxTextExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001755 (void) ConcatenateString(&path,message);
1756 /*
1757 The clipping path format is defined in "Adobe Photoshop File
1758 Formats Specification" version 6.0 downloadable from adobe.com.
1759 */
1760 (void) ResetMagickMemory(point,0,sizeof(point));
1761 (void) ResetMagickMemory(first,0,sizeof(first));
1762 (void) ResetMagickMemory(last,0,sizeof(last));
1763 knot_count=0;
1764 in_subpath=MagickFalse;
1765 while (length > 0)
1766 {
cristy4a8b0172012-02-03 16:39:53 +00001767 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001768 switch (selector)
1769 {
1770 case 0:
1771 case 3:
1772 {
1773 if (knot_count != 0)
1774 {
1775 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001776 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001777 break;
1778 }
1779 /*
1780 Expected subpath length record.
1781 */
cristy4a8b0172012-02-03 16:39:53 +00001782 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001783 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001784 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001785 break;
1786 }
1787 case 1:
1788 case 2:
1789 case 4:
1790 case 5:
1791 {
1792 if (knot_count == 0)
1793 {
1794 /*
1795 Unexpected subpath knot
1796 */
1797 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001798 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001799 break;
1800 }
1801 /*
1802 Add sub-path knot
1803 */
1804 for (i=0; i < 3; i++)
1805 {
cristy13b9a2e2010-11-10 14:03:51 +00001806 size_t
cristy97433202009-10-27 02:05:08 +00001807 xx,
1808 yy;
1809
cristy4a8b0172012-02-03 16:39:53 +00001810 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1811 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001812 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001813 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001814 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001815 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001816 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001817 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001818 point[i].x=(double) x/4096/4096;
1819 point[i].y=1.0-(double) y/4096/4096;
1820 }
anthony2fbb5952012-05-05 12:35:30 +00001821 if( IfMagickFalse(in_subpath) )
cristy3ed852e2009-09-05 21:47:34 +00001822 {
cristyb51dff52011-05-19 16:55:47 +00001823 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001824 point[1].x,point[1].y);
1825 for (i=0; i < 3; i++)
1826 {
1827 first[i]=point[i];
1828 last[i]=point[i];
1829 }
1830 }
1831 else
1832 {
1833 /*
1834 Handle special cases when Bezier curves are used to describe
1835 corners and straight lines.
1836 */
1837 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1838 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001839 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001840 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001841 else
1842 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001843 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001844 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001845 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001846 else
1847 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001848 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001849 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001850 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001851 else
cristyb51dff52011-05-19 16:55:47 +00001852 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001853 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001854 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001855 for (i=0; i < 3; i++)
1856 last[i]=point[i];
1857 }
1858 (void) ConcatenateString(&path,message);
1859 in_subpath=MagickTrue;
1860 knot_count--;
1861 /*
1862 Close the subpath if there are no more knots.
1863 */
1864 if (knot_count == 0)
1865 {
1866 /*
1867 Same special handling as above except we compare to the
1868 first point in the path and close the path.
1869 */
1870 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1871 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001872 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001873 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001874 else
1875 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001876 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001877 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001878 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001879 else
1880 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001881 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001882 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001883 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001884 else
cristyb51dff52011-05-19 16:55:47 +00001885 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001886 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001887 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001888 (void) ConcatenateString(&path,message);
1889 in_subpath=MagickFalse;
1890 }
1891 break;
1892 }
1893 case 6:
1894 case 7:
1895 case 8:
1896 default:
1897 {
1898 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001899 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001900 break;
1901 }
1902 }
1903 }
1904 /*
1905 Returns an empty PS path if the path has no knots.
1906 */
cristyb51dff52011-05-19 16:55:47 +00001907 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001908 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001909 (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001910 (void) ConcatenateString(&path,message);
1911 message=DestroyString(message);
1912 return(path);
1913}
1914
1915static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001916 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001917{
1918 char
1919 *path,
1920 *message;
1921
cristy3ed852e2009-09-05 21:47:34 +00001922 MagickBooleanType
1923 in_subpath;
1924
1925 PointInfo
1926 first[3],
1927 last[3],
1928 point[3];
1929
cristybb503372010-05-27 20:51:26 +00001930 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001931 i;
1932
cristy9d314ff2011-03-09 01:30:28 +00001933 ssize_t
1934 knot_count,
1935 selector,
1936 x,
1937 y;
1938
cristy3ed852e2009-09-05 21:47:34 +00001939 path=AcquireString((char *) NULL);
1940 if (path == (char *) NULL)
1941 return((char *) NULL);
1942 message=AcquireString((char *) NULL);
dirkeba89a32014-07-19 09:26:27 +00001943 (void) FormatLocaleString(message,MaxTextExtent,
1944 ("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
1945 "<svg xmlns=\"http://www.w3.org/2000/svg\""
1946 " width=\"%.20g\" height=\"%.20g\">\n"
1947 "<g>\n"
1948 "<path fill-rule=\"evenodd\" style=\"fill:#00000000;stroke:#00000000;"
1949 "stroke-width:0;stroke-antialiasing:false\" d=\"\n"),
1950 (double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001951 (void) ConcatenateString(&path,message);
1952 (void) ResetMagickMemory(point,0,sizeof(point));
1953 (void) ResetMagickMemory(first,0,sizeof(first));
1954 (void) ResetMagickMemory(last,0,sizeof(last));
1955 knot_count=0;
1956 in_subpath=MagickFalse;
1957 while (length != 0)
1958 {
cristy4a8b0172012-02-03 16:39:53 +00001959 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001960 switch (selector)
1961 {
1962 case 0:
1963 case 3:
1964 {
1965 if (knot_count != 0)
1966 {
1967 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001968 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001969 break;
1970 }
1971 /*
1972 Expected subpath length record.
1973 */
cristy4a8b0172012-02-03 16:39:53 +00001974 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001975 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001976 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001977 break;
1978 }
1979 case 1:
1980 case 2:
1981 case 4:
1982 case 5:
1983 {
1984 if (knot_count == 0)
1985 {
1986 /*
1987 Unexpected subpath knot.
1988 */
1989 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001990 length-=MagickMin(24,(ssize_t) length);
cristy97433202009-10-27 02:05:08 +00001991 break;
1992 }
1993 /*
1994 Add sub-path knot
1995 */
1996 for (i=0; i < 3; i++)
1997 {
dirk68bd1b52014-08-28 18:58:23 +00001998 unsigned int
cristy97433202009-10-27 02:05:08 +00001999 xx,
2000 yy;
2001
dirk68bd1b52014-08-28 18:58:23 +00002002 yy=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length));
2003 xx=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00002004 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00002005 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00002006 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00002007 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00002008 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00002009 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00002010 point[i].x=(double) x*columns/4096/4096;
2011 point[i].y=(double) y*rows/4096/4096;
2012 }
cristyf516f2f2014-01-07 01:29:28 +00002013 if (in_subpath == MagickFalse)
cristy97433202009-10-27 02:05:08 +00002014 {
cristyf516f2f2014-01-07 01:29:28 +00002015 (void) FormatLocaleString(message,MaxTextExtent,"M %g %g\n",
cristy97433202009-10-27 02:05:08 +00002016 point[1].x,point[1].y);
2017 for (i=0; i < 3; i++)
2018 {
2019 first[i]=point[i];
2020 last[i]=point[i];
2021 }
cristy3ed852e2009-09-05 21:47:34 +00002022 }
2023 else
2024 {
cristy9eb18472013-11-11 14:55:48 +00002025 /*
2026 Handle special cases when Bezier curves are used to describe
2027 corners and straight lines.
2028 */
cristy97433202009-10-27 02:05:08 +00002029 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2030 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00002031 (void) FormatLocaleString(message,MaxTextExtent,
cristyf516f2f2014-01-07 01:29:28 +00002032 "L %g %g\n",point[1].x,point[1].y);
cristy9eb18472013-11-11 14:55:48 +00002033 else
dirkd8c32862014-09-18 22:01:17 +00002034 (void) FormatLocaleString(message,MaxTextExtent,
2035 "C %g %g %g %g %g %g\n",last[2].x,
2036 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00002037 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00002038 last[i]=point[i];
2039 }
2040 (void) ConcatenateString(&path,message);
2041 in_subpath=MagickTrue;
2042 knot_count--;
2043 /*
2044 Close the subpath if there are no more knots.
2045 */
2046 if (knot_count == 0)
2047 {
cristy9eb18472013-11-11 14:55:48 +00002048 /*
2049 Same special handling as above except we compare to the
2050 first point in the path and close the path.
2051 */
cristy97433202009-10-27 02:05:08 +00002052 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2053 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00002054 (void) FormatLocaleString(message,MaxTextExtent,
cristyf516f2f2014-01-07 01:29:28 +00002055 "L %g %g Z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00002056 else
dirkd8c32862014-09-18 22:01:17 +00002057 (void) FormatLocaleString(message,MaxTextExtent,
2058 "C %g %g %g %g %g %g Z\n",last[2].x,
2059 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy9eb18472013-11-11 14:55:48 +00002060 (void) ConcatenateString(&path,message);
cristy97433202009-10-27 02:05:08 +00002061 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002062 }
cristy97433202009-10-27 02:05:08 +00002063 break;
cristy3ed852e2009-09-05 21:47:34 +00002064 }
2065 case 6:
2066 case 7:
2067 case 8:
2068 default:
2069 {
2070 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00002071 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00002072 break;
2073 }
2074 }
2075 }
2076 /*
2077 Return an empty SVG image if the path does not have knots.
2078 */
dirkeba89a32014-07-19 09:26:27 +00002079 (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00002080 message=DestroyString(message);
2081 return(path);
2082}
2083
2084MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00002085 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002086{
cristy3ed852e2009-09-05 21:47:34 +00002087 register const char
2088 *p;
2089
2090 assert(image != (Image *) NULL);
2091 assert(image->signature == MagickSignature);
cristy8c8d3f52014-02-14 02:22:14 +00002092 if (IfMagickTrue(image->debug))
cristy3ed852e2009-09-05 21:47:34 +00002093 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2094 p=(const char *) NULL;
cristy27f7af22010-06-21 12:23:21 +00002095 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002096 {
cristy5eb9fc82011-02-21 15:05:41 +00002097 if (property == (const char *) NULL)
2098 {
2099 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
2100 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
2101 image->properties);
2102 return(p);
2103 }
anthonya322a832013-04-27 06:28:03 +00002104 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2105 image->properties,property);
2106 if (p != (const char *) NULL)
2107 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002108 }
cristy5eb9fc82011-02-21 15:05:41 +00002109 if ((property == (const char *) NULL) ||
2110 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002111 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002112 switch (*property)
2113 {
2114 case '8':
2115 {
2116 if (LocaleNCompare("8bim:",property,5) == 0)
2117 {
cristy8c8d3f52014-02-14 02:22:14 +00002118 (void) Get8BIMProperty(image,property,exception);
2119 break;
cristy3ed852e2009-09-05 21:47:34 +00002120 }
2121 break;
2122 }
2123 case 'E':
2124 case 'e':
2125 {
2126 if (LocaleNCompare("exif:",property,5) == 0)
2127 {
cristy8c8d3f52014-02-14 02:22:14 +00002128 (void) GetEXIFProperty(image,property,exception);
2129 break;
cristy3ed852e2009-09-05 21:47:34 +00002130 }
2131 break;
2132 }
cristy3ed852e2009-09-05 21:47:34 +00002133 case 'I':
2134 case 'i':
2135 {
cristy55166322014-01-23 01:53:24 +00002136 if ((LocaleNCompare("icc:",property,4) == 0) ||
2137 (LocaleNCompare("icm:",property,4) == 0))
2138 {
cristy8c8d3f52014-02-14 02:22:14 +00002139 (void) GetICCProperty(image,property,exception);
2140 break;
cristy55166322014-01-23 01:53:24 +00002141 }
cristy3ed852e2009-09-05 21:47:34 +00002142 if (LocaleNCompare("iptc:",property,5) == 0)
2143 {
cristy8c8d3f52014-02-14 02:22:14 +00002144 (void) GetIPTCProperty(image,property,exception);
2145 break;
cristy3ed852e2009-09-05 21:47:34 +00002146 }
2147 break;
2148 }
cristy3ed852e2009-09-05 21:47:34 +00002149 case 'X':
2150 case 'x':
2151 {
2152 if (LocaleNCompare("xmp:",property,4) == 0)
2153 {
cristy8c8d3f52014-02-14 02:22:14 +00002154 (void) GetXMPProperty(image,property);
2155 break;
cristy3ed852e2009-09-05 21:47:34 +00002156 }
2157 break;
2158 }
cristy5eb9fc82011-02-21 15:05:41 +00002159 default:
2160 break;
cristy3ed852e2009-09-05 21:47:34 +00002161 }
cristy8c8d3f52014-02-14 02:22:14 +00002162 if (image->properties != (void *) NULL)
2163 {
2164 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2165 image->properties,property);
2166 return(p);
2167 }
2168 return((const char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002169}
cristyf516f2f2014-01-07 01:29:28 +00002170
cristy3ed852e2009-09-05 21:47:34 +00002171/*
2172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2173% %
2174% %
2175% %
2176+ G e t M a g i c k P r o p e r t y %
2177% %
2178% %
2179% %
2180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2181%
anthony06762232012-04-29 11:45:40 +00002182% GetMagickProperty() gets attributes or calculated values that is associated
anthonya322a832013-04-27 06:28:03 +00002183% with a fixed known property name, or single letter property. It may be
anthonydb2a1d62013-04-29 01:34:58 +00002184% called if no image is defined (IMv7), in which case only global image_info
cristy934c77d2014-12-14 20:14:16 +00002185% values are available:
2186%
2187% \n newline
2188% \r carriage return
2189% < less-than character.
2190% > greater-than character.
2191% & ampersand character.
2192% %% a percent sign
2193% %b file size of image read in
2194% %c comment meta-data property
2195% %d directory component of path
2196% %e filename extension or suffix
2197% %f filename (including suffix)
2198% %g layer canvas page geometry (equivalent to "%Wx%H%X%Y")
2199% %h current image height in pixels
2200% %i image filename (note: becomes output filename for "info:")
2201% %k CALCULATED: number of unique colors
2202% %l label meta-data property
2203% %m image file format (file magic)
2204% %n number of images in current image sequence
2205% %o output filename (used for delegates)
2206% %p index of image in current image list
2207% %q quantum depth (compile-time constant)
2208% %r image class and colorspace
2209% %s scene number (from input unless re-assigned)
2210% %t filename without directory or extension (suffix)
2211% %u unique temporary filename (used for delegates)
2212% %w current width in pixels
2213% %x x resolution (density)
2214% %y y resolution (density)
2215% %z image depth (as read in unless modified, image save depth)
2216% %A image transparency channel enabled (true/false)
2217% %C image compression type
2218% %D image GIF dispose method
2219% %G original image size (%wx%h; before any resizes)
2220% %H page (canvas) height
2221% %M Magick filename (original file exactly as given, including read mods)
2222% %O page (canvas) offset ( = %X%Y )
2223% %P page (canvas) size ( = %Wx%H )
2224% %Q image compression quality ( 0 = default )
2225% %S ?? scenes ??
2226% %T image time delay (in centi-seconds)
2227% %U image resolution units
2228% %W page (canvas) width
2229% %X page (canvas) x offset (including sign)
2230% %Y page (canvas) y offset (including sign)
2231% %Z unique filename (used for delegates)
2232% %@ CALCULATED: trim bounding box (without actually trimming)
2233% %# CALCULATED: 'signature' hash of image values
anthony06762232012-04-29 11:45:40 +00002234%
anthonydb2a1d62013-04-29 01:34:58 +00002235% This routine only handles specifically known properties. It does not
2236% handle special prefixed properties, profiles, or expressions. Nor does
2237% it return any free-form property strings.
anthony06762232012-04-29 11:45:40 +00002238%
anthonydb2a1d62013-04-29 01:34:58 +00002239% The returned string is stored in a structure somewhere, and should not be
2240% directly freed. If the string was generated (common) the string will be
2241% stored as as either as artifact or option 'get-property'. These may be
2242% deleted (cleaned up) when no longer required, but neither artifact or
2243% option is guranteed to exist.
cristy3ed852e2009-09-05 21:47:34 +00002244%
2245% The format of the GetMagickProperty method is:
2246%
anthonydb2a1d62013-04-29 01:34:58 +00002247% const char *GetMagickProperty(ImageInfo *image_info,Image *image,
cristy97fc9f32012-06-13 21:04:44 +00002248% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002249%
2250% A description of each parameter follows:
2251%
anthonydb2a1d62013-04-29 01:34:58 +00002252% o image_info: the image info (optional)
cristy3ed852e2009-09-05 21:47:34 +00002253%
anthonydb2a1d62013-04-29 01:34:58 +00002254% o image: the image (optional)
cristy3ed852e2009-09-05 21:47:34 +00002255%
2256% o key: the key.
2257%
cristyd15e6592011-10-15 00:13:06 +00002258% o exception: return any errors or warnings in this structure.
2259%
cristy3ed852e2009-09-05 21:47:34 +00002260*/
anthonya322a832013-04-27 06:28:03 +00002261#define WarnNoImageReturn(format,arg) \
2262 if (image == (Image *) NULL ) { \
2263 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2264 "NoImageForProperty",format,arg); \
cristyf432c632014-12-07 15:11:28 +00002265 return((const char *) NULL); \
anthonya322a832013-04-27 06:28:03 +00002266 }
anthonydb2a1d62013-04-29 01:34:58 +00002267#define WarnNoImageInfoReturn(format,arg) \
cristy4d246fc2014-01-15 22:33:44 +00002268 if (image_info == (ImageInfo *) NULL ) { \
anthonydb2a1d62013-04-29 01:34:58 +00002269 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2270 "NoImageInfoForProperty",format,arg); \
cristyf432c632014-12-07 15:11:28 +00002271 return((const char *) NULL); \
anthonydb2a1d62013-04-29 01:34:58 +00002272 }
anthonya322a832013-04-27 06:28:03 +00002273
anthonydb2a1d62013-04-29 01:34:58 +00002274static const char *GetMagickPropertyLetter(ImageInfo *image_info,
anthony2fbb5952012-05-05 12:35:30 +00002275 Image *image,const char letter,ExceptionInfo *exception)
2276{
2277 char
anthonya322a832013-04-27 06:28:03 +00002278 value[MaxTextExtent]; /* formated string to store as a returned artifact */
anthony2fbb5952012-05-05 12:35:30 +00002279
anthony2cfa1a12012-05-12 05:18:07 +00002280 const char
anthonya322a832013-04-27 06:28:03 +00002281 *string; /* return a string already stored somewher */
anthony2cfa1a12012-05-12 05:18:07 +00002282
2283 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonydb2a1d62013-04-29 01:34:58 +00002285 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2286 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
2287
anthonya322a832013-04-27 06:28:03 +00002288 *value='\0'; /* formatted string */
cristy68064d72012-06-13 20:56:14 +00002289 string=(char *) NULL; /* constant string reference */
anthonya322a832013-04-27 06:28:03 +00002290
2291 /* Get properities that are directly defined by images */
anthony2fbb5952012-05-05 12:35:30 +00002292 switch (letter)
2293 {
anthony2fbb5952012-05-05 12:35:30 +00002294 case 'b': /* image size read in - in bytes */
2295 {
anthonya322a832013-04-27 06:28:03 +00002296 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002297 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2298 ((MagickOffsetType) image->extent));
2299 if (image->extent != (MagickSizeType) ((size_t) image->extent))
2300 (void) FormatMagickSize(image->extent,MagickFalse,value);
2301 ConcatenateMagickString(value,"B",MaxTextExtent);
2302 break;
2303 }
anthony7bb7aee2012-05-15 00:09:16 +00002304 case 'c': /* image comment property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002305 {
anthonya322a832013-04-27 06:28:03 +00002306 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002307 string=GetImageProperty(image,"comment",exception);
anthonya322a832013-04-27 06:28:03 +00002308 if ( string == (const char *) NULL )
anthony7bb7aee2012-05-15 00:09:16 +00002309 string="";
anthony2fbb5952012-05-05 12:35:30 +00002310 break;
2311 }
2312 case 'd': /* Directory component of filename */
2313 {
anthonya322a832013-04-27 06:28:03 +00002314 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002315 GetPathComponent(image->magick_filename,HeadPath,value);
anthonya322a832013-04-27 06:28:03 +00002316 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002317 break;
2318 }
2319 case 'e': /* Filename extension (suffix) of image file */
2320 {
anthonya322a832013-04-27 06:28:03 +00002321 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002322 GetPathComponent(image->magick_filename,ExtensionPath,value);
anthonya322a832013-04-27 06:28:03 +00002323 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002324 break;
2325 }
2326 case 'f': /* Filename without directory component */
2327 {
anthonya322a832013-04-27 06:28:03 +00002328 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002329 GetPathComponent(image->magick_filename,TailPath,value);
anthonya322a832013-04-27 06:28:03 +00002330 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002331 break;
2332 }
2333 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */
2334 {
anthonya322a832013-04-27 06:28:03 +00002335 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002336 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002337 (double) image->page.width,(double) image->page.height,
2338 (double) image->page.x,(double) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002339 break;
2340 }
2341 case 'h': /* Image height (current) */
2342 {
anthonya322a832013-04-27 06:28:03 +00002343 WarnNoImageReturn("\"%%%c\"",letter);
cristyec6934f2012-08-14 18:38:40 +00002344 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2345 (image->rows != 0 ? image->rows : image->magick_rows));
anthony2fbb5952012-05-05 12:35:30 +00002346 break;
2347 }
anthonya322a832013-04-27 06:28:03 +00002348 case 'i': /* Filename last used for an image (read or write) */
anthony2fbb5952012-05-05 12:35:30 +00002349 {
anthonya322a832013-04-27 06:28:03 +00002350 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002351 string=image->filename;
anthony2fbb5952012-05-05 12:35:30 +00002352 break;
2353 }
2354 case 'k': /* Number of unique colors */
2355 {
cristyec6934f2012-08-14 18:38:40 +00002356 /*
2357 FUTURE: ensure this does not generate the formatted comment!
2358 */
anthonya322a832013-04-27 06:28:03 +00002359 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002360 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002361 GetNumberColors(image,(FILE *) NULL,exception));
anthony2fbb5952012-05-05 12:35:30 +00002362 break;
2363 }
cristy97fc9f32012-06-13 21:04:44 +00002364 case 'l': /* Image label property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002365 {
anthonya322a832013-04-27 06:28:03 +00002366 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002367 string=GetImageProperty(image,"label",exception);
anthonya322a832013-04-27 06:28:03 +00002368 if ( string == (const char *) NULL)
anthony7bb7aee2012-05-15 00:09:16 +00002369 string="";
anthony2fbb5952012-05-05 12:35:30 +00002370 break;
2371 }
2372 case 'm': /* Image format (file magick) */
2373 {
anthonya322a832013-04-27 06:28:03 +00002374 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002375 string=image->magick;
anthony2fbb5952012-05-05 12:35:30 +00002376 break;
2377 }
2378 case 'n': /* Number of images in the list. */
2379 {
anthonya322a832013-04-27 06:28:03 +00002380 if ( image != (Image *) NULL )
2381 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2382 GetImageListLength(image));
2383 else
2384 string="0"; /* no images or scenes */
anthony2fbb5952012-05-05 12:35:30 +00002385 break;
2386 }
2387 case 'o': /* Output Filename - for delegate use only */
anthonydb2a1d62013-04-29 01:34:58 +00002388 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002389 string=image_info->filename;
anthony2fbb5952012-05-05 12:35:30 +00002390 break;
anthonya322a832013-04-27 06:28:03 +00002391 case 'p': /* Image index in current image list */
anthony2fbb5952012-05-05 12:35:30 +00002392 {
anthonya322a832013-04-27 06:28:03 +00002393 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002394 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002395 GetImageIndexInList(image));
anthony2fbb5952012-05-05 12:35:30 +00002396 break;
2397 }
2398 case 'q': /* Quantum depth of image in memory */
2399 {
anthonya322a832013-04-27 06:28:03 +00002400 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002401 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2402 MAGICKCORE_QUANTUM_DEPTH);
2403 break;
2404 }
anthonya322a832013-04-27 06:28:03 +00002405 case 'r': /* Image storage class, colorspace, and alpha enabled. */
anthony2fbb5952012-05-05 12:35:30 +00002406 {
2407 ColorspaceType
2408 colorspace;
2409
anthonya322a832013-04-27 06:28:03 +00002410 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002411 colorspace=image->colorspace;
2412 if (IfMagickTrue(IsImageGray(image,exception)))
anthonya322a832013-04-27 06:28:03 +00002413 colorspace=GRAYColorspace; /* FUTURE: this is IMv6 not IMv7 */
anthony2fbb5952012-05-05 12:35:30 +00002414 (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s",
cristy68064d72012-06-13 20:56:14 +00002415 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
anthony2fbb5952012-05-05 12:35:30 +00002416 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
cristy17f11b02014-12-20 19:37:04 +00002417 image->alpha_trait != UndefinedPixelTrait ? "Alpha" : "");
anthony2fbb5952012-05-05 12:35:30 +00002418 break;
2419 }
2420 case 's': /* Image scene number */
2421 {
anthonya322a832013-04-27 06:28:03 +00002422#if 0 /* this seems non-sensical -- simplifing */
anthony2fbb5952012-05-05 12:35:30 +00002423 if (image_info->number_scenes != 0)
2424 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002425 image_info->scene);
cristyf432c632014-12-07 15:11:28 +00002426 else if (image != (Image *) NULL)
anthony2fbb5952012-05-05 12:35:30 +00002427 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002428 image->scene);
anthonya322a832013-04-27 06:28:03 +00002429 else
2430 string="0";
2431#else
2432 WarnNoImageReturn("\"%%%c\"",letter);
2433 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2434 image->scene);
2435#endif
anthony2fbb5952012-05-05 12:35:30 +00002436 break;
2437 }
2438 case 't': /* Base filename without directory or extention */
2439 {
anthonya322a832013-04-27 06:28:03 +00002440 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002441 GetPathComponent(image->magick_filename,BasePath,value);
anthonya322a832013-04-27 06:28:03 +00002442 if (*value == '\0') string="";
anthony2fbb5952012-05-05 12:35:30 +00002443 break;
2444 }
2445 case 'u': /* Unique filename */
anthonydb2a1d62013-04-29 01:34:58 +00002446 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002447 string=image_info->unique;
anthony2fbb5952012-05-05 12:35:30 +00002448 break;
anthony2fbb5952012-05-05 12:35:30 +00002449 case 'w': /* Image width (current) */
2450 {
anthonya322a832013-04-27 06:28:03 +00002451 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002452 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002453 (image->columns != 0 ? image->columns : image->magick_columns));
anthony2fbb5952012-05-05 12:35:30 +00002454 break;
2455 }
anthonyc69008c2012-05-07 11:45:32 +00002456 case 'x': /* Image horizontal resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002457 {
anthonya322a832013-04-27 06:28:03 +00002458 WarnNoImageReturn("\"%%%c\"",letter);
cristy66a5ca22013-08-01 17:12:57 +00002459 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002460 fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0);
anthony2fbb5952012-05-05 12:35:30 +00002461 break;
2462 }
anthonyc69008c2012-05-07 11:45:32 +00002463 case 'y': /* Image vertical resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002464 {
anthonya322a832013-04-27 06:28:03 +00002465 WarnNoImageReturn("\"%%%c\"",letter);
cristy66a5ca22013-08-01 17:12:57 +00002466 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002467 fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0);
anthony2fbb5952012-05-05 12:35:30 +00002468 break;
2469 }
2470 case 'z': /* Image depth as read in */
2471 {
anthonya322a832013-04-27 06:28:03 +00002472 WarnNoImageReturn("\"%%%c\"",letter);
2473 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002474 (double) image->depth);
anthony2fbb5952012-05-05 12:35:30 +00002475 break;
2476 }
2477 case 'A': /* Image alpha channel */
2478 {
anthonya322a832013-04-27 06:28:03 +00002479 WarnNoImageReturn("\"%%%c\"",letter);
2480 string=CommandOptionToMnemonic(MagickBooleanOptions,
cristyed8d7852013-08-01 22:44:22 +00002481 (ssize_t) image->alpha_trait);
anthony2fbb5952012-05-05 12:35:30 +00002482 break;
2483 }
2484 case 'C': /* Image compression method. */
2485 {
anthonya322a832013-04-27 06:28:03 +00002486 WarnNoImageReturn("\"%%%c\"",letter);
2487 string=CommandOptionToMnemonic(MagickCompressOptions,
cristyed8d7852013-08-01 22:44:22 +00002488 (ssize_t) image->compression);
anthony2fbb5952012-05-05 12:35:30 +00002489 break;
2490 }
2491 case 'D': /* Image dispose method. */
2492 {
anthonya322a832013-04-27 06:28:03 +00002493 WarnNoImageReturn("\"%%%c\"",letter);
2494 string=CommandOptionToMnemonic(MagickDisposeOptions,
cristyed8d7852013-08-01 22:44:22 +00002495 (ssize_t) image->dispose);
anthony2fbb5952012-05-05 12:35:30 +00002496 break;
2497 }
2498 case 'G': /* Image size as geometry = "%wx%h" */
2499 {
anthonya322a832013-04-27 06:28:03 +00002500 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002501 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002502 (double)image->magick_columns,(double) image->magick_rows);
anthony2fbb5952012-05-05 12:35:30 +00002503 break;
2504 }
2505 case 'H': /* layer canvas height */
2506 {
anthonya322a832013-04-27 06:28:03 +00002507 WarnNoImageReturn("\"%%%c\"",letter);
2508 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristyed8d7852013-08-01 22:44:22 +00002509 (double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002510 break;
2511 }
2512 case 'M': /* Magick filename - filename given incl. coder & read mods */
2513 {
anthonya322a832013-04-27 06:28:03 +00002514 WarnNoImageReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002515 string=image->magick_filename;
anthony2fbb5952012-05-05 12:35:30 +00002516 break;
2517 }
2518 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2519 {
anthonya322a832013-04-27 06:28:03 +00002520 WarnNoImageReturn("\"%%%c\"",letter);
cristy68064d72012-06-13 20:56:14 +00002521 (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long)
2522 image->page.x,(long) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002523 break;
2524 }
2525 case 'P': /* layer canvas page size = "%Wx%H" */
2526 {
anthonya322a832013-04-27 06:28:03 +00002527 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002528 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002529 (double) image->page.width,(double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002530 break;
2531 }
2532 case 'Q': /* image compression quality */
2533 {
anthonya322a832013-04-27 06:28:03 +00002534 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002535 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyd46a07e2013-08-03 23:07:04 +00002536 (image->quality == 0 ? 92 : image->quality));
anthony2fbb5952012-05-05 12:35:30 +00002537 break;
2538 }
anthonya322a832013-04-27 06:28:03 +00002539 case 'S': /* Number of scenes in image list. */
anthony2fbb5952012-05-05 12:35:30 +00002540 {
anthonydb2a1d62013-04-29 01:34:58 +00002541 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthonya322a832013-04-27 06:28:03 +00002542#if 0 /* What is this number? -- it makes no sense - simplifing */
anthony2fbb5952012-05-05 12:35:30 +00002543 if (image_info->number_scenes == 0)
anthonya322a832013-04-27 06:28:03 +00002544 string="2147483647";
2545 else if ( image != (Image *) NULL )
anthony2fbb5952012-05-05 12:35:30 +00002546 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
anthonya322a832013-04-27 06:28:03 +00002547 image_info->scene+image_info->number_scenes);
anthonydb2a1d62013-04-29 01:34:58 +00002548 else
anthonya322a832013-04-27 06:28:03 +00002549 string="0";
2550#else
2551 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyab5fbd82013-04-28 17:08:53 +00002552 (image_info->number_scenes == 0 ? 2147483647 :
2553 image_info->number_scenes));
anthonya322a832013-04-27 06:28:03 +00002554#endif
anthony2fbb5952012-05-05 12:35:30 +00002555 break;
2556 }
2557 case 'T': /* image time delay for animations */
2558 {
anthonya322a832013-04-27 06:28:03 +00002559 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002560 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyed8d7852013-08-01 22:44:22 +00002561 image->delay);
anthony2fbb5952012-05-05 12:35:30 +00002562 break;
2563 }
cristye9f6b9e2013-10-27 22:04:52 +00002564 case 'U': /* Image resolution units. */
2565 {
2566 WarnNoImageReturn("\"%%%c\"",letter);
2567 string=CommandOptionToMnemonic(MagickResolutionOptions,
2568 (ssize_t) image->units);
2569 break;
2570 }
anthony2fbb5952012-05-05 12:35:30 +00002571 case 'W': /* layer canvas width */
2572 {
anthonya322a832013-04-27 06:28:03 +00002573 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002574 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002575 image->page.width);
anthony2fbb5952012-05-05 12:35:30 +00002576 break;
2577 }
2578 case 'X': /* layer canvas X offset */
2579 {
anthonya322a832013-04-27 06:28:03 +00002580 WarnNoImageReturn("\"%%%c\"",letter);
cristy68064d72012-06-13 20:56:14 +00002581 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2582 image->page.x);
anthony2fbb5952012-05-05 12:35:30 +00002583 break;
2584 }
2585 case 'Y': /* layer canvas Y offset */
2586 {
anthonya322a832013-04-27 06:28:03 +00002587 WarnNoImageReturn("\"%%%c\"",letter);
cristy68064d72012-06-13 20:56:14 +00002588 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2589 image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002590 break;
2591 }
anthony6e2e0732012-05-06 12:22:43 +00002592 case 'Z': /* Zero filename ??? */
anthonydb2a1d62013-04-29 01:34:58 +00002593 WarnNoImageInfoReturn("\"%%%c\"",letter);
anthony2cfa1a12012-05-12 05:18:07 +00002594 string=image_info->zero;
anthony2fbb5952012-05-05 12:35:30 +00002595 break;
anthonya322a832013-04-27 06:28:03 +00002596 case '%': /* percent escaped */
2597 string="%";
2598 break;
2599 case '@': /* Trim bounding box, without actually Trimming! */
anthony2fbb5952012-05-05 12:35:30 +00002600 {
2601 RectangleInfo
2602 page;
2603
anthonya322a832013-04-27 06:28:03 +00002604 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002605 page=GetImageBoundingBox(image,exception);
2606 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002607 (double) page.width,(double) page.height,(double) page.x,(double)
2608 page.y);
anthony2fbb5952012-05-05 12:35:30 +00002609 break;
2610 }
cristye5b517a2014-02-14 14:19:29 +00002611 case '#':
anthony2fbb5952012-05-05 12:35:30 +00002612 {
cristye5b517a2014-02-14 14:19:29 +00002613 /*
2614 Image signature.
2615 */
anthonya322a832013-04-27 06:28:03 +00002616 WarnNoImageReturn("\"%%%c\"",letter);
anthony2fbb5952012-05-05 12:35:30 +00002617 (void) SignatureImage(image,exception);
anthony2cfa1a12012-05-12 05:18:07 +00002618 string=GetImageProperty(image,"signature",exception);
anthony2fbb5952012-05-05 12:35:30 +00002619 break;
2620 }
anthony2fbb5952012-05-05 12:35:30 +00002621 }
cristy68064d72012-06-13 20:56:14 +00002622 if (string != (char *) NULL)
anthonya322a832013-04-27 06:28:03 +00002623 return(string);
2624 if (*value != '\0')
cristye5b517a2014-02-14 14:19:29 +00002625 {
2626 /*
2627 Create a cloned copy of result.
2628 */
cristyf432c632014-12-07 15:11:28 +00002629 if (image != (Image *) NULL)
cristye5b517a2014-02-14 14:19:29 +00002630 {
2631 (void) SetImageArtifact(image,"get-property",value);
2632 return(GetImageArtifact(image,"get-property"));
2633 }
2634 else
2635 {
2636 (void) SetImageOption(image_info,"get-property",value);
2637 return(GetImageOption(image_info,"get-property"));
2638 }
2639 }
cristyf432c632014-12-07 15:11:28 +00002640 return((char *) NULL);
anthony2fbb5952012-05-05 12:35:30 +00002641}
2642
anthonydb2a1d62013-04-29 01:34:58 +00002643MagickExport const char *GetMagickProperty(ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002644 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002645{
2646 char
anthony003f8992012-05-11 12:50:07 +00002647 value[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00002648
anthony2cfa1a12012-05-12 05:18:07 +00002649 const char
2650 *string;
2651
anthony2fbb5952012-05-05 12:35:30 +00002652 assert(property[0] != '\0');
cristyf432c632014-12-07 15:11:28 +00002653 assert(image != (Image *) NULL || image_info != (ImageInfo *) NULL );
anthonya322a832013-04-27 06:28:03 +00002654
cristy68064d72012-06-13 20:56:14 +00002655 if (property[1] == '\0') /* single letter property request */
2656 return(GetMagickPropertyLetter(image_info,image,*property,exception));
anthonya322a832013-04-27 06:28:03 +00002657
anthonydb2a1d62013-04-29 01:34:58 +00002658 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002659 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthonydb2a1d62013-04-29 01:34:58 +00002660 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
2661 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
anthonya322a832013-04-27 06:28:03 +00002662
2663 *value='\0'; /* formated string */
cristy68064d72012-06-13 20:56:14 +00002664 string=(char *) NULL; /* constant string reference */
cristyec6897c2010-05-12 00:43:15 +00002665 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002666 {
2667 case 'b':
2668 {
anthony2cfa1a12012-05-12 05:18:07 +00002669 if ((LocaleCompare("base",property) == 0) ||
2670 (LocaleCompare("basename",property) == 0) )
cristy3ed852e2009-09-05 21:47:34 +00002671 {
anthonya322a832013-04-27 06:28:03 +00002672 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002673 GetPathComponent(image->magick_filename,BasePath,value);
anthonya322a832013-04-27 06:28:03 +00002674 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002675 break;
2676 }
dirk2a08b062013-10-19 19:26:02 +00002677 if (LocaleCompare("bit-depth",property) == 0)
2678 {
2679 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2680 GetImageDepth(image, exception));
2681 break;
2682 }
cristy3ed852e2009-09-05 21:47:34 +00002683 break;
2684 }
2685 case 'c':
2686 {
anthony2cfa1a12012-05-12 05:18:07 +00002687 if (LocaleCompare("channels",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002688 {
anthonya322a832013-04-27 06:28:03 +00002689 WarnNoImageReturn("\"%%[%s]\"",property);
anthony7bb7aee2012-05-15 00:09:16 +00002690 /* FUTURE: return actual image channels */
cristyb51dff52011-05-19 16:55:47 +00002691 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002692 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002693 image->colorspace));
2694 LocaleLower(value);
cristy17f11b02014-12-20 19:37:04 +00002695 if( image->alpha_trait != UndefinedPixelTrait )
cristy3ed852e2009-09-05 21:47:34 +00002696 (void) ConcatenateMagickString(value,"a",MaxTextExtent);
2697 break;
2698 }
anthony2cfa1a12012-05-12 05:18:07 +00002699 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002700 {
anthonya322a832013-04-27 06:28:03 +00002701 WarnNoImageReturn("\"%%[%s]\"",property);
anthony7bb7aee2012-05-15 00:09:16 +00002702 /* FUTURE: return actual colorspace - no 'gray' stuff */
anthony2cfa1a12012-05-12 05:18:07 +00002703 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3f168fe2013-06-10 17:33:36 +00002704 image->colorspace);
cristy3ed852e2009-09-05 21:47:34 +00002705 break;
2706 }
anthony2cfa1a12012-05-12 05:18:07 +00002707 if (LocaleCompare("copyright",property) == 0)
cristy63054742010-09-20 12:42:28 +00002708 {
2709 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
2710 break;
2711 }
cristy3ed852e2009-09-05 21:47:34 +00002712 break;
2713 }
2714 case 'd':
2715 {
anthony2cfa1a12012-05-12 05:18:07 +00002716 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002717 {
anthonya322a832013-04-27 06:28:03 +00002718 WarnNoImageReturn("\"%%[%s]\"",property);
cristyb51dff52011-05-19 16:55:47 +00002719 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002720 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002721 break;
2722 }
anthony2cfa1a12012-05-12 05:18:07 +00002723 if (LocaleCompare("directory",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002724 {
anthonya322a832013-04-27 06:28:03 +00002725 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002726 GetPathComponent(image->magick_filename,HeadPath,value);
anthonya322a832013-04-27 06:28:03 +00002727 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002728 break;
2729 }
2730 break;
2731 }
2732 case 'e':
2733 {
cristy50908ac2014-11-09 13:49:47 +00002734 if (LocaleCompare("entropy",property) == 0)
2735 {
2736 double
2737 entropy;
2738
2739 WarnNoImageReturn("\"%%[%s]\"",property);
2740 (void) GetImageEntropy(image,&entropy,exception);
2741 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
2742 GetMagickPrecision(),entropy);
2743 break;
2744 }
anthony2cfa1a12012-05-12 05:18:07 +00002745 if (LocaleCompare("extension",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002746 {
anthonya322a832013-04-27 06:28:03 +00002747 WarnNoImageReturn("\"%%[%s]\"",property);
anthony003f8992012-05-11 12:50:07 +00002748 GetPathComponent(image->magick_filename,ExtensionPath,value);
anthonya322a832013-04-27 06:28:03 +00002749 if (*value == '\0') string="";
cristy3ed852e2009-09-05 21:47:34 +00002750 break;
2751 }
2752 break;
2753 }
2754 case 'g':
2755 {
anthony2f7f9ca2012-05-14 06:33:53 +00002756 if (LocaleCompare("gamma",property) == 0)
2757 {
anthonya322a832013-04-27 06:28:03 +00002758 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2f7f9ca2012-05-14 06:33:53 +00002759 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
2760 GetMagickPrecision(),image->gamma);
2761 break;
2762 }
anthonydb2a1d62013-04-29 01:34:58 +00002763 if (LocaleCompare("group",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002764 {
anthonydb2a1d62013-04-29 01:34:58 +00002765 WarnNoImageInfoReturn("\"%%[%s]\"",property);
cristy68064d72012-06-13 20:56:14 +00002766 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
2767 image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002768 break;
2769 }
2770 break;
2771 }
2772 case 'h':
2773 {
anthony2cfa1a12012-05-12 05:18:07 +00002774 if (LocaleCompare("height",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002775 {
anthonya322a832013-04-27 06:28:03 +00002776 WarnNoImageReturn("\"%%[%s]\"",property);
cristyb51dff52011-05-19 16:55:47 +00002777 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002778 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002779 break;
2780 }
2781 break;
2782 }
2783 case 'i':
2784 {
anthony2cfa1a12012-05-12 05:18:07 +00002785 if (LocaleCompare("input",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002786 {
anthonya322a832013-04-27 06:28:03 +00002787 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002788 string=image->filename;
cristy3ed852e2009-09-05 21:47:34 +00002789 break;
2790 }
2791 break;
2792 }
2793 case 'k':
2794 {
anthony2cfa1a12012-05-12 05:18:07 +00002795 if (LocaleCompare("kurtosis",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002796 {
2797 double
2798 kurtosis,
2799 skewness;
2800
anthonya322a832013-04-27 06:28:03 +00002801 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002802 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002803 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002804 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002805 break;
2806 }
2807 break;
2808 }
2809 case 'm':
2810 {
anthony2cfa1a12012-05-12 05:18:07 +00002811 if (LocaleCompare("magick",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002812 {
anthonya322a832013-04-27 06:28:03 +00002813 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002814 string=image->magick;
cristy3ed852e2009-09-05 21:47:34 +00002815 break;
2816 }
anthony2cfa1a12012-05-12 05:18:07 +00002817 if (LocaleCompare("max",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002818 {
2819 double
2820 maximum,
2821 minimum;
2822
anthonya322a832013-04-27 06:28:03 +00002823 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002824 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002825 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002826 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002827 break;
2828 }
anthony2cfa1a12012-05-12 05:18:07 +00002829 if (LocaleCompare("mean",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002830 {
2831 double
2832 mean,
2833 standard_deviation;
2834
anthonya322a832013-04-27 06:28:03 +00002835 WarnNoImageReturn("\"%%[%s]\"",property);
cristy68064d72012-06-13 20:56:14 +00002836 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00002837 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002838 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002839 break;
2840 }
anthony2cfa1a12012-05-12 05:18:07 +00002841 if (LocaleCompare("min",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002842 {
2843 double
2844 maximum,
2845 minimum;
2846
anthonya322a832013-04-27 06:28:03 +00002847 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002848 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002849 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002850 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002851 break;
2852 }
2853 break;
2854 }
cristy3ed852e2009-09-05 21:47:34 +00002855 case 'o':
2856 {
anthony2cfa1a12012-05-12 05:18:07 +00002857 if (LocaleCompare("opaque",property) == 0)
cristyb0094252011-03-26 12:59:45 +00002858 {
anthonya322a832013-04-27 06:28:03 +00002859 WarnNoImageReturn("\"%%[%s]\"",property);
2860 string=CommandOptionToMnemonic(MagickBooleanOptions,
2861 (ssize_t) IsImageOpaque(image,exception));
cristyb0094252011-03-26 12:59:45 +00002862 break;
2863 }
anthony2cfa1a12012-05-12 05:18:07 +00002864 if (LocaleCompare("orientation",property) == 0)
cristy42aa06c2012-04-01 14:05:10 +00002865 {
anthonya322a832013-04-27 06:28:03 +00002866 WarnNoImageReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00002867 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
2868 image->orientation);
cristy42aa06c2012-04-01 14:05:10 +00002869 break;
2870 }
anthonydb2a1d62013-04-29 01:34:58 +00002871 if (LocaleCompare("output",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002872 {
anthonydb2a1d62013-04-29 01:34:58 +00002873 WarnNoImageInfoReturn("\"%%[%s]\"",property);
cristy3ed852e2009-09-05 21:47:34 +00002874 (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
2875 break;
2876 }
2877 break;
2878 }
2879 case 'p':
2880 {
dirk6015d442013-10-19 15:07:40 +00002881#if defined(MAGICKCORE_LCMS_DELEGATE)
2882 if (LocaleCompare("profile:icc",property) == 0 ||
2883 LocaleCompare("profile:icm",property) == 0)
2884 {
cristy1a2fb522013-11-20 14:37:58 +00002885#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
2886#define cmsUInt32Number DWORD
2887#endif
2888
dirk6015d442013-10-19 15:07:40 +00002889 const StringInfo
2890 *profile;
2891
2892 cmsHPROFILE
2893 icc_profile;
2894
2895 profile=GetImageProfile(image,property+8);
2896 if (profile == (StringInfo *) NULL)
2897 break;
2898
2899 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
2900 (cmsUInt32Number) GetStringInfoLength(profile));
2901 if (icc_profile != (cmsHPROFILE *) NULL)
2902 {
2903#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
2904 string=cmsTakeProductName(icc_profile);
2905#else
2906 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
2907 "en","US",value,MaxTextExtent);
2908#endif
2909 (void) cmsCloseProfile(icc_profile);
2910 }
2911 }
2912#endif
2913 if (LocaleCompare("profiles",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002914 {
cristy5954e3c2013-10-18 23:23:44 +00002915 const char
2916 *name;
2917
2918 ResetImageProfileIterator(image);
2919 name=GetNextImageProfile(image);
dirk6015d442013-10-19 15:07:40 +00002920 if (name != (char *) NULL)
2921 {
2922 (void) CopyMagickString(value,name,MaxTextExtent);
2923 name=GetNextImageProfile(image);
2924 while (name != (char *) NULL)
2925 {
2926 ConcatenateMagickString(value,",",MaxTextExtent);
2927 ConcatenateMagickString(value,name,MaxTextExtent);
2928 name=GetNextImageProfile(image);
2929 }
2930 }
cristy3ed852e2009-09-05 21:47:34 +00002931 break;
2932 }
2933 break;
2934 }
cristy946b1032012-04-01 14:19:07 +00002935 case 'r':
2936 {
anthony2cfa1a12012-05-12 05:18:07 +00002937 if (LocaleCompare("resolution.x",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002938 {
anthonya322a832013-04-27 06:28:03 +00002939 WarnNoImageReturn("\"%%[%s]\"",property);
cristy946b1032012-04-01 14:19:07 +00002940 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2941 image->resolution.x);
2942 break;
2943 }
anthony2cfa1a12012-05-12 05:18:07 +00002944 if (LocaleCompare("resolution.y",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002945 {
anthonya322a832013-04-27 06:28:03 +00002946 WarnNoImageReturn("\"%%[%s]\"",property);
cristy946b1032012-04-01 14:19:07 +00002947 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2948 image->resolution.y);
2949 break;
2950 }
2951 break;
2952 }
cristy3ed852e2009-09-05 21:47:34 +00002953 case 's':
2954 {
anthony2cfa1a12012-05-12 05:18:07 +00002955 if (LocaleCompare("scene",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002956 {
anthonydb2a1d62013-04-29 01:34:58 +00002957 WarnNoImageInfoReturn("\"%%[%s]\"",property);
2958 if (image_info->number_scenes != 0)
cristyb51dff52011-05-19 16:55:47 +00002959 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002960 image_info->scene);
anthonya322a832013-04-27 06:28:03 +00002961 else {
2962 WarnNoImageReturn("\"%%[%s]\"",property);
anthony90ebc0d2012-04-28 08:10:02 +00002963 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002964 image->scene);
anthonya322a832013-04-27 06:28:03 +00002965 }
cristy3ed852e2009-09-05 21:47:34 +00002966 break;
2967 }
anthony2cfa1a12012-05-12 05:18:07 +00002968 if (LocaleCompare("scenes",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002969 {
anthonydb2a1d62013-04-29 01:34:58 +00002970 /* FUTURE: equivelent to %n? */
anthonya322a832013-04-27 06:28:03 +00002971 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00002972 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2973 GetImageListLength(image));
2974 break;
2975 }
anthony2cfa1a12012-05-12 05:18:07 +00002976 if (LocaleCompare("size",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002977 {
2978 char
2979 format[MaxTextExtent];
2980
anthonya322a832013-04-27 06:28:03 +00002981 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00002982 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
2983 (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
2984 break;
2985 }
anthony2cfa1a12012-05-12 05:18:07 +00002986 if (LocaleCompare("skewness",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002987 {
2988 double
2989 kurtosis,
2990 skewness;
2991
anthonya322a832013-04-27 06:28:03 +00002992 WarnNoImageReturn("\"%%[%s]\"",property);
cristyc82a27b2011-10-21 01:07:16 +00002993 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002994 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002995 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002996 break;
2997 }
dirkdc513ec2013-11-07 21:20:34 +00002998 if ((LocaleCompare("standard-deviation",property) == 0) ||
2999 (LocaleCompare("standard_deviation",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00003000 {
3001 double
3002 mean,
3003 standard_deviation;
3004
anthonya322a832013-04-27 06:28:03 +00003005 WarnNoImageReturn("\"%%[%s]\"",property);
cristy14fed162012-09-26 10:13:43 +00003006 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00003007 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00003008 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00003009 break;
3010 }
3011 break;
3012 }
cristy623aaec2012-06-21 00:49:08 +00003013 case 't':
3014 {
3015 if (LocaleCompare("type",property) == 0)
3016 {
anthonya322a832013-04-27 06:28:03 +00003017 WarnNoImageReturn("\"%%[%s]\"",property);
cristy623aaec2012-06-21 00:49:08 +00003018 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
3019 GetImageType(image,exception));
3020 break;
3021 }
3022 break;
3023 }
cristy3ed852e2009-09-05 21:47:34 +00003024 case 'u':
3025 {
anthonydb2a1d62013-04-29 01:34:58 +00003026 if (LocaleCompare("unique",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003027 {
anthonydb2a1d62013-04-29 01:34:58 +00003028 WarnNoImageInfoReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00003029 string=image_info->unique;
cristy3ed852e2009-09-05 21:47:34 +00003030 break;
3031 }
cristy9af7f5b2013-09-23 18:58:46 +00003032 if (LocaleCompare("units",property) == 0)
3033 {
3034 WarnNoImageReturn("\"%%[%s]\"",property);
3035 string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
3036 image->units);
3037 break;
3038 }
3039 if (LocaleCompare("copyright",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003040 break;
3041 }
cristy63054742010-09-20 12:42:28 +00003042 case 'v':
3043 {
anthony2cfa1a12012-05-12 05:18:07 +00003044 if (LocaleCompare("version",property) == 0)
cristy63054742010-09-20 12:42:28 +00003045 {
anthony2cfa1a12012-05-12 05:18:07 +00003046 string=GetMagickVersion((size_t *) NULL);
cristy63054742010-09-20 12:42:28 +00003047 break;
3048 }
3049 break;
3050 }
cristy3ed852e2009-09-05 21:47:34 +00003051 case 'w':
3052 {
anthony2cfa1a12012-05-12 05:18:07 +00003053 if (LocaleCompare("width",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003054 {
anthonya322a832013-04-27 06:28:03 +00003055 WarnNoImageReturn("\"%%[%s]\"",property);
cristyb51dff52011-05-19 16:55:47 +00003056 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003057 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00003058 break;
3059 }
3060 break;
3061 }
anthony2eb9a422012-05-15 02:28:02 +00003062 case 'x': /* FUTURE: Obsolete X resolution */
anthony90ebc0d2012-04-28 08:10:02 +00003063 {
anthony2cfa1a12012-05-12 05:18:07 +00003064 if ((LocaleCompare("xresolution",property) == 0) ||
3065 (LocaleCompare("x-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00003066 {
anthonya322a832013-04-27 06:28:03 +00003067 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00003068 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00003069 image->resolution.x);
anthony90ebc0d2012-04-28 08:10:02 +00003070 break;
3071 }
3072 break;
3073 }
anthony2eb9a422012-05-15 02:28:02 +00003074 case 'y': /* FUTURE: Obsolete Y resolution */
anthony90ebc0d2012-04-28 08:10:02 +00003075 {
anthony2cfa1a12012-05-12 05:18:07 +00003076 if ((LocaleCompare("yresolution",property) == 0) ||
3077 (LocaleCompare("y-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00003078 {
anthonya322a832013-04-27 06:28:03 +00003079 WarnNoImageReturn("\"%%[%s]\"",property);
anthonyc69008c2012-05-07 11:45:32 +00003080 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00003081 image->resolution.y);
anthony90ebc0d2012-04-28 08:10:02 +00003082 break;
3083 }
3084 break;
3085 }
cristy3ed852e2009-09-05 21:47:34 +00003086 case 'z':
3087 {
anthonydb2a1d62013-04-29 01:34:58 +00003088 if (LocaleCompare("zero",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003089 {
anthonydb2a1d62013-04-29 01:34:58 +00003090 WarnNoImageInfoReturn("\"%%[%s]\"",property);
anthony2cfa1a12012-05-12 05:18:07 +00003091 string=image_info->zero;
cristy3ed852e2009-09-05 21:47:34 +00003092 break;
3093 }
3094 break;
3095 }
3096 }
anthonya322a832013-04-27 06:28:03 +00003097 if (string != (char *) NULL)
3098 return(string);
anthony2cfa1a12012-05-12 05:18:07 +00003099 if (*value != '\0')
anthonydb2a1d62013-04-29 01:34:58 +00003100 {
3101 /* create a cloned copy of result, that will get cleaned up, eventually */
cristyf432c632014-12-07 15:11:28 +00003102 if (image != (Image *) NULL)
anthonydb2a1d62013-04-29 01:34:58 +00003103 {
3104 (void) SetImageArtifact(image,"get-property",value);
3105 return(GetImageArtifact(image,"get-property"));
3106 }
3107 else
3108 {
3109 (void) SetImageOption(image_info,"get-property",value);
3110 return(GetImageOption(image_info,"get-property"));
3111 }
3112 }
cristyf432c632014-12-07 15:11:28 +00003113 return((char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003114}
dirk6015d442013-10-19 15:07:40 +00003115#undef WarnNoImageReturn
cristyf516f2f2014-01-07 01:29:28 +00003116
cristy3ed852e2009-09-05 21:47:34 +00003117/*
3118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3119% %
3120% %
3121% %
3122% G e t N e x t I m a g e P r o p e r t y %
3123% %
3124% %
3125% %
3126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3127%
cristy97fc9f32012-06-13 21:04:44 +00003128% GetNextImageProperty() gets the next free-form string property name.
cristy3ed852e2009-09-05 21:47:34 +00003129%
3130% The format of the GetNextImageProperty method is:
3131%
3132% char *GetNextImageProperty(const Image *image)
3133%
3134% A description of each parameter follows:
3135%
3136% o image: the image.
3137%
3138*/
3139MagickExport char *GetNextImageProperty(const Image *image)
3140{
3141 assert(image != (Image *) NULL);
3142 assert(image->signature == MagickSignature);
cristycb190b72014-08-31 20:04:42 +00003143 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003144 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3145 image->filename);
3146 if (image->properties == (void *) NULL)
3147 return((char *) NULL);
3148 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
3149}
cristycef1ea02014-01-07 00:39:10 +00003150
cristy3ed852e2009-09-05 21:47:34 +00003151/*
3152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3153% %
3154% %
3155% %
3156% I n t e r p r e t I m a g e P r o p e r t i e s %
3157% %
3158% %
3159% %
3160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3161%
3162% InterpretImageProperties() replaces any embedded formatting characters with
anthony06762232012-04-29 11:45:40 +00003163% the appropriate image property and returns the interpreted text.
3164%
3165% This searches for and replaces
anthony003f8992012-05-11 12:50:07 +00003166% \n \r \% replaced by newline, return, and percent resp.
3167% &lt; &gt; &amp; replaced by '<', '>', '&' resp.
3168% %% replaced by percent
anthony06762232012-04-29 11:45:40 +00003169%
anthonyd2f39e02012-08-20 12:29:28 +00003170% %x %[x] where 'x' is a single letter properity, case sensitive).
3171% %[type:name] where 'type' a is special and known prefix.
anthony003f8992012-05-11 12:50:07 +00003172% %[name] where 'name' is a specifically known attribute, calculated
cristy97fc9f32012-06-13 21:04:44 +00003173% value, or a per-image property string name, or a per-image
anthonyd2f39e02012-08-20 12:29:28 +00003174% 'artifact' (as generated from a global option).
3175% It may contain ':' as long as the prefix is not special.
anthony06762232012-04-29 11:45:40 +00003176%
anthonyd2f39e02012-08-20 12:29:28 +00003177% Single letter % substitutions will only happen if the character before the
3178% percent is NOT a number. But braced substitutions will always be performed.
3179% This prevents the typical usage of percent in a interpreted geometry
3180% argument from being substituted when the percent is a geometry flag.
anthony003f8992012-05-11 12:50:07 +00003181%
3182% If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
3183% used as a search pattern to print multiple lines of "name=value\n" pairs of
anthony643c6132012-11-07 14:50:28 +00003184% the associacted set of properties.
anthony003f8992012-05-11 12:50:07 +00003185%
anthonyd2f39e02012-08-20 12:29:28 +00003186% The returned string must be freed using DestoryString() by the caller.
anthonyf68116b2012-04-14 12:54:41 +00003187%
cristy3ed852e2009-09-05 21:47:34 +00003188% The format of the InterpretImageProperties method is:
3189%
anthonydb2a1d62013-04-29 01:34:58 +00003190% char *InterpretImageProperties(ImageInfo *image_info,
anthony003f8992012-05-11 12:50:07 +00003191% Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003192%
3193% A description of each parameter follows:
3194%
anthonya322a832013-04-27 06:28:03 +00003195% o image_info: the image info. (required)
cristy3ed852e2009-09-05 21:47:34 +00003196%
anthonya322a832013-04-27 06:28:03 +00003197% o image: the image. (optional)
cristy3ed852e2009-09-05 21:47:34 +00003198%
3199% o embed_text: the address of a character string containing the embedded
3200% formatting characters.
3201%
cristy018f07f2011-09-04 21:15:19 +00003202% o exception: return any errors or warnings in this structure.
3203%
cristy3ed852e2009-09-05 21:47:34 +00003204*/
anthony643c6132012-11-07 14:50:28 +00003205
3206/* common inline code to expand the interpreted text string */
3207#define ExtendInterpretText(string_length) do { \
dirk93b02b72013-11-16 16:03:36 +00003208DisableMSCWarning(4127) \
anthony643c6132012-11-07 14:50:28 +00003209 size_t length=(string_length); \
3210 if ((size_t) (q-interpret_text+length+1) >= extent) \
3211 { extent+=length; \
3212 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
3213 extent+MaxTextExtent,sizeof(*interpret_text)); \
3214 if (interpret_text == (char *) NULL) \
cristyf432c632014-12-07 15:11:28 +00003215 return((char *) NULL); \
anthony643c6132012-11-07 14:50:28 +00003216 q=interpret_text+strlen(interpret_text); \
dirk93b02b72013-11-16 16:03:36 +00003217 } } while (0) /* no trailing ; */ \
3218RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003219
3220/* same but append the given string */
3221#define AppendString2Text(string) do { \
dirk93b02b72013-11-16 16:03:36 +00003222DisableMSCWarning(4127) \
anthony643c6132012-11-07 14:50:28 +00003223 size_t length=strlen((string)); \
3224 if ((size_t) (q-interpret_text+length+1) >= extent) \
3225 { extent+=length; \
3226 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
3227 extent+MaxTextExtent,sizeof(*interpret_text)); \
3228 if (interpret_text == (char *) NULL) \
cristyf432c632014-12-07 15:11:28 +00003229 return((char *) NULL); \
anthony643c6132012-11-07 14:50:28 +00003230 q=interpret_text+strlen(interpret_text); \
3231 } \
3232 (void) CopyMagickString(q,(string),extent); \
3233 q+=length; \
dirk93b02b72013-11-16 16:03:36 +00003234 } while (0) /* no trailing ; */ \
3235RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003236
anthonya322a832013-04-27 06:28:03 +00003237/* same but append a 'key' and 'string' pair */
3238#define AppendKeyValue2Text(key,string) do { \
dirk93b02b72013-11-16 16:03:36 +00003239DisableMSCWarning(4127) \
anthonya322a832013-04-27 06:28:03 +00003240 size_t length=strlen(key)+strlen(string)+2; \
anthony643c6132012-11-07 14:50:28 +00003241 if ((size_t) (q-interpret_text+length+1) >= extent) \
3242 { extent+=length; \
3243 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
3244 extent+MaxTextExtent,sizeof(*interpret_text)); \
3245 if (interpret_text == (char *) NULL) \
cristyf432c632014-12-07 15:11:28 +00003246 return((char *) NULL); \
anthony643c6132012-11-07 14:50:28 +00003247 q=interpret_text+strlen(interpret_text); \
3248 } \
anthonya322a832013-04-27 06:28:03 +00003249 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(string)); \
dirk93b02b72013-11-16 16:03:36 +00003250 } while (0) /* no trailing ; */ \
3251RestoreMSCWarning
anthony643c6132012-11-07 14:50:28 +00003252
anthonydb2a1d62013-04-29 01:34:58 +00003253MagickExport char *InterpretImageProperties(ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00003254 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003255{
3256 char
anthony003f8992012-05-11 12:50:07 +00003257 *interpret_text;
cristy3ed852e2009-09-05 21:47:34 +00003258
cristy3ed852e2009-09-05 21:47:34 +00003259 register char
anthony643c6132012-11-07 14:50:28 +00003260 *q; /* current position in interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003261
3262 register const char
anthony643c6132012-11-07 14:50:28 +00003263 *p; /* position in embed_text string being expanded */
cristy3ed852e2009-09-05 21:47:34 +00003264
cristy3ed852e2009-09-05 21:47:34 +00003265 size_t
anthony643c6132012-11-07 14:50:28 +00003266 extent; /* allocated length of interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003267
anthony003f8992012-05-11 12:50:07 +00003268 MagickBooleanType
3269 number;
3270
anthonydb2a1d62013-04-29 01:34:58 +00003271 assert(image == NULL || image->signature == MagickSignature);
3272 assert(image_info == NULL || image_info->signature == MagickSignature);
3273
3274 if (image != (Image *) NULL && IfMagickTrue(image->debug))
3275 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3276 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
3277 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
anthony2fbb5952012-05-05 12:35:30 +00003278
glennrpa22b2652013-01-05 12:37:21 +00003279 if (embed_text == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003280 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00003281 p=embed_text;
anthony2fbb5952012-05-05 12:35:30 +00003282
anthony964d28e2012-05-17 23:39:46 +00003283 if (*p == '\0')
3284 return(ConstantString(""));
3285
anthony2fbb5952012-05-05 12:35:30 +00003286 /* handle a '@' replace string from file */
anthony003f8992012-05-11 12:50:07 +00003287 if (*p == '@') {
3288 p++;
3289 if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
3290 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003291 OptionError,"UnableToAccessPath","%s",p);
anthony003f8992012-05-11 12:50:07 +00003292 return((char *) NULL);
3293 }
cristy3a5987c2013-11-07 14:18:46 +00003294 return(FileToString(p,~0UL,exception));
anthony003f8992012-05-11 12:50:07 +00003295 }
3296
cristy3ed852e2009-09-05 21:47:34 +00003297 /*
3298 Translate any embedded format characters.
3299 */
anthony003f8992012-05-11 12:50:07 +00003300 interpret_text=AcquireString(embed_text); /* new string with extra space */
anthony643c6132012-11-07 14:50:28 +00003301 extent=MaxTextExtent; /* allocated space in string */
anthony003f8992012-05-11 12:50:07 +00003302 number=MagickFalse; /* is last char a number? */
3303 for (q=interpret_text; *p!='\0'; number=IsMagickTrue(isdigit(*p)),p++)
cristy3ed852e2009-09-05 21:47:34 +00003304 {
3305 *q='\0';
anthony643c6132012-11-07 14:50:28 +00003306 ExtendInterpretText(MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003307 /*
anthonyd2f39e02012-08-20 12:29:28 +00003308 Look for the various escapes, (and handle other specials)
cristy3ed852e2009-09-05 21:47:34 +00003309 */
anthony003f8992012-05-11 12:50:07 +00003310 switch (*p) {
3311 case '\\':
3312 switch (*(p+1)) {
3313 case '\0':
3314 continue;
3315 case 'r': /* convert to RETURN */
3316 *q++='\r';
3317 p++;
3318 continue;
3319 case 'n': /* convert to NEWLINE */
3320 *q++='\n';
3321 p++;
3322 continue;
3323 case '\n': /* EOL removal UNIX,MacOSX */
3324 p++;
3325 continue;
3326 case '\r': /* EOL removal DOS,Windows */
3327 p++;
3328 if (*p == '\n') /* return-newline EOL */
3329 p++;
3330 continue;
3331 default:
3332 p++;
3333 *q++=(*p);
anthony003f8992012-05-11 12:50:07 +00003334 }
cristy2867b872014-05-17 11:53:50 +00003335 continue;
anthony003f8992012-05-11 12:50:07 +00003336 case '&':
anthony104f8932012-05-13 01:54:53 +00003337 if (LocaleNCompare("&lt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003338 *q++='<', p+=3;
anthony104f8932012-05-13 01:54:53 +00003339 else if (LocaleNCompare("&gt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003340 *q++='>', p+=3;
anthony104f8932012-05-13 01:54:53 +00003341 else if (LocaleNCompare("&amp;",p,5) == 0)
anthony003f8992012-05-11 12:50:07 +00003342 *q++='&', p+=4;
3343 else
3344 *q++=(*p);
cristy3ed852e2009-09-05 21:47:34 +00003345 continue;
anthony003f8992012-05-11 12:50:07 +00003346 case '%':
3347 break; /* continue to next set of handlers */
3348 default:
3349 *q++=(*p); /* any thing else is 'as normal' */
cristy3ed852e2009-09-05 21:47:34 +00003350 continue;
anthony003f8992012-05-11 12:50:07 +00003351 }
anthony104f8932012-05-13 01:54:53 +00003352 p++; /* advance beyond the percent */
anthony2fbb5952012-05-05 12:35:30 +00003353
anthony003f8992012-05-11 12:50:07 +00003354 /*
anthony2ec9bd92012-05-20 05:43:24 +00003355 Doubled Percent - or percent at end of string
anthony003f8992012-05-11 12:50:07 +00003356 */
anthony2ec9bd92012-05-20 05:43:24 +00003357 if ( *p == '\0' )
3358 p--;
anthony104f8932012-05-13 01:54:53 +00003359 if ( *p == '%' ) {
anthony89075d02012-05-18 03:40:40 +00003360 *q++='%';
anthony104f8932012-05-13 01:54:53 +00003361 continue;
3362 }
anthony003f8992012-05-11 12:50:07 +00003363
3364 /*
anthonyd2f39e02012-08-20 12:29:28 +00003365 Single letter escapes %c
anthony003f8992012-05-11 12:50:07 +00003366 */
anthony104f8932012-05-13 01:54:53 +00003367 if ( *p != '[' ) {
anthony003f8992012-05-11 12:50:07 +00003368 const char
anthonya322a832013-04-27 06:28:03 +00003369 *string;
anthony003f8992012-05-11 12:50:07 +00003370
3371 /* But only if not preceeded by a number! */
3372 if ( IfMagickTrue(number) ) {
anthony104f8932012-05-13 01:54:53 +00003373 *q++='%'; /* do NOT substitute the percent */
3374 p--; /* back up one */
anthony003f8992012-05-11 12:50:07 +00003375 continue;
3376 }
anthonya322a832013-04-27 06:28:03 +00003377 string=GetMagickPropertyLetter(image_info,image,*p, exception);
3378 if (string != (char *) NULL)
cristyf57e5482013-02-17 22:11:27 +00003379 {
anthonya322a832013-04-27 06:28:03 +00003380 AppendString2Text(string);
anthonydb2a1d62013-04-29 01:34:58 +00003381 if (image != (Image *) NULL)
3382 (void)DeleteImageArtifact(image,"get-property");
3383 if (image_info != (ImageInfo *) NULL)
3384 (void)DeleteImageOption(image_info,"get-property");
cristyf57e5482013-02-17 22:11:27 +00003385 continue;
3386 }
3387 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3388 "UnknownImageProperty","\"%%%c\"",*p);
anthony003f8992012-05-11 12:50:07 +00003389 continue;
anthony2fbb5952012-05-05 12:35:30 +00003390 }
anthony2fbb5952012-05-05 12:35:30 +00003391
anthony003f8992012-05-11 12:50:07 +00003392 /*
anthonyd2f39e02012-08-20 12:29:28 +00003393 Braced Percent Escape %[...]
anthony003f8992012-05-11 12:50:07 +00003394 */
3395 {
3396 char
cristyf2a82ee2014-05-26 17:49:54 +00003397 pattern[2*MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00003398
anthony003f8992012-05-11 12:50:07 +00003399 const char
3400 *key,
anthonya322a832013-04-27 06:28:03 +00003401 *string;
cristy3ed852e2009-09-05 21:47:34 +00003402
anthony003f8992012-05-11 12:50:07 +00003403 register ssize_t
3404 len;
cristy3ed852e2009-09-05 21:47:34 +00003405
anthony003f8992012-05-11 12:50:07 +00003406 ssize_t
3407 depth;
3408
anthonya322a832013-04-27 06:28:03 +00003409 /* get the property name framed by the %[...] */
anthony104f8932012-05-13 01:54:53 +00003410 p++; /* advance p to just inside the opening brace */
anthony003f8992012-05-11 12:50:07 +00003411 depth=1;
3412 if ( *p == ']' ) {
cristyf2a82ee2014-05-26 17:49:54 +00003413 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3414 "UnknownImageProperty","\"%%[]\"");
anthony003f8992012-05-11 12:50:07 +00003415 break;
3416 }
3417 for (len=0; len<(MaxTextExtent-1L) && (*p != '\0');)
3418 {
3419 /* skip escaped braces within braced pattern */
3420 if ( (*p == '\\') && (*(p+1) != '\0') ) {
3421 pattern[len++]=(*p++);
3422 pattern[len++]=(*p++);
3423 continue;
3424 }
3425 if (*p == '[')
3426 depth++;
3427 if (*p == ']')
3428 depth--;
3429 if (depth <= 0)
3430 break;
3431 pattern[len++]=(*p++);
3432 }
3433 pattern[len]='\0';
3434 /* Check for unmatched final ']' for "%[...]" */
3435 if ( depth != 0 ) {
3436 if (len >= 64) { /* truncate string for error message */
3437 pattern[61] = '.';
3438 pattern[62] = '.';
3439 pattern[63] = '.';
3440 pattern[64] = '\0';
3441 }
3442 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003443 OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
3444 interpret_text=DestroyString(interpret_text);
cristyf432c632014-12-07 15:11:28 +00003445 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00003446 }
3447
3448 /*
anthony643c6132012-11-07 14:50:28 +00003449 Special Lookup Prefixes %[prefix:...]
anthony003f8992012-05-11 12:50:07 +00003450 */
anthonya322a832013-04-27 06:28:03 +00003451 /* fx - value calculator */
3452 if (LocaleNCompare("fx:",pattern,3) == 0)
3453 {
3454 FxInfo
3455 *fx_info;
3456
3457 double
3458 value;
3459
3460 MagickBooleanType
3461 status;
3462
3463 if (image == (Image *) NULL ) {
3464 (void) ThrowMagickException(exception,GetMagickModule(),
3465 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3466 continue; /* else no image to retrieve artifact */
3467 }
3468 fx_info=AcquireFxInfo(image,pattern+3,exception);
3469 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
3470 &value,exception);
3471 fx_info=DestroyFxInfo(fx_info);
3472 if( IfMagickTrue(status) )
3473 {
3474 char
3475 result[MaxTextExtent];
3476
3477 (void) FormatLocaleString(result,MaxTextExtent,"%.*g",
3478 GetMagickPrecision(),(double) value);
anthonyec26e252013-04-28 12:18:12 +00003479 AppendString2Text(result);
anthonya322a832013-04-27 06:28:03 +00003480 }
anthonyec26e252013-04-28 12:18:12 +00003481 continue;
anthonya322a832013-04-27 06:28:03 +00003482 }
3483 /* pixel - color value calculator */
3484 if (LocaleNCompare("pixel:",pattern,6) == 0)
3485 {
3486 FxInfo
3487 *fx_info;
3488
3489 double
3490 value;
3491
cristyee1bdaa2013-07-16 16:45:03 +00003492 MagickStatusType
anthonya322a832013-04-27 06:28:03 +00003493 status;
3494
3495 PixelInfo
3496 pixel;
3497
3498 if (image == (Image *) NULL ) {
3499 (void) ThrowMagickException(exception,GetMagickModule(),
3500 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3501 continue; /* else no image to retrieve artifact */
3502 }
3503 GetPixelInfo(image,&pixel);
3504 fx_info=AcquireFxInfo(image,pattern+6,exception);
cristyee1bdaa2013-07-16 16:45:03 +00003505 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
3506 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003507 pixel.red=(double) QuantumRange*value;
cristyaae13f62013-08-15 14:41:32 +00003508 status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003509 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003510 pixel.green=(double) QuantumRange*value;
cristyaae13f62013-08-15 14:41:32 +00003511 status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003512 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003513 pixel.blue=(double) QuantumRange*value;
3514 if (image->colorspace == CMYKColorspace)
3515 {
cristyaae13f62013-08-15 14:41:32 +00003516 status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003517 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003518 pixel.black=(double) QuantumRange*value;
3519 }
cristyaae13f62013-08-15 14:41:32 +00003520 status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
cristyee1bdaa2013-07-16 16:45:03 +00003521 &value,exception);
anthonya322a832013-04-27 06:28:03 +00003522 pixel.alpha=(double) QuantumRange*value;
3523 fx_info=DestroyFxInfo(fx_info);
3524 if( IfMagickTrue(status) )
3525 {
3526 char
3527 name[MaxTextExtent];
3528
3529 (void) QueryColorname(image,&pixel,SVGCompliance,name,
3530 exception);
3531 AppendString2Text(name);
3532 }
anthonyec26e252013-04-28 12:18:12 +00003533 continue;
anthonya322a832013-04-27 06:28:03 +00003534 }
anthony643c6132012-11-07 14:50:28 +00003535 /* option - direct global option lookup (with globbing) */
3536 if (LocaleNCompare("option:",pattern,7) == 0)
3537 {
anthonya322a832013-04-27 06:28:03 +00003538 if (image_info == (ImageInfo *) NULL ) {
3539 (void) ThrowMagickException(exception,GetMagickModule(),
3540 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3541 continue; /* else no image to retrieve artifact */
3542 }
anthony643c6132012-11-07 14:50:28 +00003543 if( IfMagickTrue(IsGlob(pattern+7)) )
3544 {
3545 ResetImageOptionIterator(image_info);
3546 while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
3547 if( IfMagickTrue(GlobExpression(key,pattern+7,MagickTrue)) )
3548 {
anthonya322a832013-04-27 06:28:03 +00003549 string=GetImageOption(image_info,key);
3550 if (string != (const char *) NULL)
3551 AppendKeyValue2Text(key,string);
3552 /* else - assertion failure? key found but no string value! */
anthony643c6132012-11-07 14:50:28 +00003553 }
3554 continue;
3555 }
anthonya322a832013-04-27 06:28:03 +00003556 string=GetImageOption(image_info,pattern+7);
3557 if (string == (char *) NULL)
3558 goto PropertyLookupFailure; /* no artifact of this specifc name */
3559 AppendString2Text(string);
anthony643c6132012-11-07 14:50:28 +00003560 continue;
3561 }
3562 /* artifact - direct image artifact lookup (with glob) */
anthonyc7994672012-11-17 05:33:27 +00003563 if (LocaleNCompare("artifact:",pattern,9) == 0)
anthony643c6132012-11-07 14:50:28 +00003564 {
anthonya322a832013-04-27 06:28:03 +00003565 if (image == (Image *) NULL ) {
3566 (void) ThrowMagickException(exception,GetMagickModule(),
3567 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
anthonyc7994672012-11-17 05:33:27 +00003568 continue; /* else no image to retrieve artifact */
anthonya322a832013-04-27 06:28:03 +00003569 }
anthony643c6132012-11-07 14:50:28 +00003570 if( IfMagickTrue(IsGlob(pattern+9)) )
3571 {
3572 ResetImageArtifactIterator(image);
3573 while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
3574 if( IfMagickTrue(GlobExpression(key,pattern+9,MagickTrue)) )
3575 {
anthonya322a832013-04-27 06:28:03 +00003576 string=GetImageArtifact(image,key);
3577 if (string != (const char *) NULL)
3578 AppendKeyValue2Text(key,string);
3579 /* else - assertion failure? key found but no string value! */
anthony643c6132012-11-07 14:50:28 +00003580 }
3581 continue;
3582 }
anthonya322a832013-04-27 06:28:03 +00003583 string=GetImageArtifact(image,pattern+9);
3584 if (string == (char *) NULL)
3585 goto PropertyLookupFailure; /* no artifact of this specifc name */
3586 AppendString2Text(string);
anthonyc7994672012-11-17 05:33:27 +00003587 continue;
anthony643c6132012-11-07 14:50:28 +00003588 }
anthonya322a832013-04-27 06:28:03 +00003589 /* property - direct image property lookup (with glob) */
3590 if (LocaleNCompare("property:",pattern,9) == 0)
3591 {
3592 if (image == (Image *) NULL ) {
3593 (void) ThrowMagickException(exception,GetMagickModule(),
3594 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
3595 continue; /* else no image to retrieve artifact */
3596 }
3597 if( IfMagickTrue(IsGlob(pattern+9)) )
cristy3ed852e2009-09-05 21:47:34 +00003598 {
anthonya322a832013-04-27 06:28:03 +00003599 ResetImagePropertyIterator(image);
3600 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
3601 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
3602 {
3603 string=GetImageProperty(image,key,exception);
3604 if (string != (const char *) NULL)
3605 AppendKeyValue2Text(key,string);
3606 /* else - assertion failure? */
3607 }
anthony003f8992012-05-11 12:50:07 +00003608 continue;
3609 }
anthonya322a832013-04-27 06:28:03 +00003610 string=GetImageProperty(image,pattern+9,exception);
3611 if (string == (char *) NULL)
3612 goto PropertyLookupFailure; /* no artifact of this specifc name */
3613 AppendString2Text(string);
3614 continue;
3615 }
3616 /* Properties without special prefix.
3617 This handles attributes, properties, and profiles such as %[exif:...]
3618 Note the profile properties may also include a glob expansion pattern.
3619 */
cristyf432c632014-12-07 15:11:28 +00003620 if ( image != (Image *) NULL )
anthonya322a832013-04-27 06:28:03 +00003621 {
3622 string=GetImageProperty(image,pattern,exception);
3623 if (string != (const char *) NULL)
3624 {
3625 AppendString2Text(string);
anthonydb2a1d62013-04-29 01:34:58 +00003626 if (image != (Image *) NULL)
3627 (void)DeleteImageArtifact(image,"get-property");
3628 if (image_info != (ImageInfo *) NULL)
3629 (void)DeleteImageOption(image_info,"get-property");
anthonya322a832013-04-27 06:28:03 +00003630 continue;
3631 }
3632 }
anthony003f8992012-05-11 12:50:07 +00003633 /*
cristy97fc9f32012-06-13 21:04:44 +00003634 Handle property 'glob' patterns
anthony003f8992012-05-11 12:50:07 +00003635 Such as: %[*] %[user:array_??] %[filename:e*]
3636 */
3637 if( IfMagickTrue(IsGlob(pattern)) )
3638 {
anthonya322a832013-04-27 06:28:03 +00003639 if (image == (Image *) NULL)
3640 continue; /* else no image to retrieve proprty - no list */
anthony003f8992012-05-11 12:50:07 +00003641 ResetImagePropertyIterator(image);
anthony643c6132012-11-07 14:50:28 +00003642 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003643 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
cristy3ed852e2009-09-05 21:47:34 +00003644 {
anthonya322a832013-04-27 06:28:03 +00003645 string=GetImageProperty(image,key,exception);
3646 if (string != (const char *) NULL)
3647 AppendKeyValue2Text(key,string);
anthony643c6132012-11-07 14:50:28 +00003648 /* else - assertion failure? */
cristy3ed852e2009-09-05 21:47:34 +00003649 }
anthony003f8992012-05-11 12:50:07 +00003650 continue;
3651 }
3652 /*
3653 Look for a known property or image attribute
3654 Such as %[basename] %[denisty] %[delay]
anthony643c6132012-11-07 14:50:28 +00003655 Also handles a braced single letter: %[b] %[G] %[g]
anthony003f8992012-05-11 12:50:07 +00003656 */
anthonya322a832013-04-27 06:28:03 +00003657 string=GetMagickProperty(image_info,image,pattern,exception);
3658 if (string != (const char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003659 {
anthonya322a832013-04-27 06:28:03 +00003660 AppendString2Text(string);
anthony003f8992012-05-11 12:50:07 +00003661 continue;
3662 }
3663 /*
anthonya322a832013-04-27 06:28:03 +00003664 Look for a per-image Artifact
3665 This includes option lookup (FUTURE: interpreted according to image)
anthony003f8992012-05-11 12:50:07 +00003666 */
cristyf432c632014-12-07 15:11:28 +00003667 if (image != (Image *) NULL)
anthony003f8992012-05-11 12:50:07 +00003668 {
anthonya322a832013-04-27 06:28:03 +00003669 string=GetImageArtifact(image,pattern);
3670 if (string != (char *) NULL)
3671 {
3672 AppendString2Text(string);
3673 continue;
3674 }
anthony003f8992012-05-11 12:50:07 +00003675 }
anthonya322a832013-04-27 06:28:03 +00003676 else
3677 /* no image, so direct 'option' lookup (no delayed percent escapes) */
3678 if (image_info != (ImageInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003679 {
anthonya322a832013-04-27 06:28:03 +00003680 string=GetImageOption(image_info,pattern);
3681 if (string != (char *) NULL)
3682 {
3683 AppendString2Text(string);
3684 continue;
3685 }
cristy3ed852e2009-09-05 21:47:34 +00003686 }
anthonya322a832013-04-27 06:28:03 +00003687PropertyLookupFailure:
anthony003f8992012-05-11 12:50:07 +00003688 /*
3689 Failed to find any match anywhere!
3690 */
3691 if (len >= 64) { /* truncate string for error message */
3692 pattern[61] = '.';
3693 pattern[62] = '.';
3694 pattern[63] = '.';
3695 pattern[64] = '\0';
3696 }
3697 (void) ThrowMagickException(exception,GetMagickModule(),
3698 OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
3699 /* continue */
3700 } /* Braced Percent Escape */
3701
3702 } /* for each char in 'embed_text' */
cristy3ed852e2009-09-05 21:47:34 +00003703 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003704 return(interpret_text);
3705}
cristycef1ea02014-01-07 00:39:10 +00003706
cristy3ed852e2009-09-05 21:47:34 +00003707/*
3708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3709% %
3710% %
3711% %
3712% R e m o v e I m a g e P r o p e r t y %
3713% %
3714% %
3715% %
3716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3717%
3718% RemoveImageProperty() removes a property from the image and returns its
3719% value.
3720%
anthonyd2f39e02012-08-20 12:29:28 +00003721% In this case the ConstantString() value returned should be freed by the
3722% caller when finished.
3723%
cristy3ed852e2009-09-05 21:47:34 +00003724% The format of the RemoveImageProperty method is:
3725%
3726% char *RemoveImageProperty(Image *image,const char *property)
3727%
3728% A description of each parameter follows:
3729%
3730% o image: the image.
3731%
3732% o property: the image property.
3733%
3734*/
3735MagickExport char *RemoveImageProperty(Image *image,
3736 const char *property)
3737{
3738 char
3739 *value;
3740
3741 assert(image != (Image *) NULL);
3742 assert(image->signature == MagickSignature);
cristycb190b72014-08-31 20:04:42 +00003743 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003744 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3745 image->filename);
3746 if (image->properties == (void *) NULL)
3747 return((char *) NULL);
3748 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3749 property);
3750 return(value);
3751}
cristyf516f2f2014-01-07 01:29:28 +00003752
cristy3ed852e2009-09-05 21:47:34 +00003753/*
3754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3755% %
3756% %
3757% %
3758% 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 %
3759% %
3760% %
3761% %
3762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3763%
3764% ResetImagePropertyIterator() resets the image properties iterator. Use it
3765% in conjunction with GetNextImageProperty() to iterate over all the values
3766% associated with an image property.
3767%
3768% The format of the ResetImagePropertyIterator method is:
3769%
3770% ResetImagePropertyIterator(Image *image)
3771%
3772% A description of each parameter follows:
3773%
3774% o image: the image.
3775%
3776*/
3777MagickExport void ResetImagePropertyIterator(const Image *image)
3778{
3779 assert(image != (Image *) NULL);
3780 assert(image->signature == MagickSignature);
cristycb190b72014-08-31 20:04:42 +00003781 if (image->debug != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003782 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3783 image->filename);
3784 if (image->properties == (void *) NULL)
3785 return;
3786 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3787}
cristyf516f2f2014-01-07 01:29:28 +00003788
cristy3ed852e2009-09-05 21:47:34 +00003789/*
3790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3791% %
3792% %
3793% %
3794% S e t I m a g e P r o p e r t y %
3795% %
3796% %
3797% %
3798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3799%
anthony104f8932012-05-13 01:54:53 +00003800% SetImageProperty() saves the given string value either to specific known
cristy97fc9f32012-06-13 21:04:44 +00003801% attribute or to a freeform property string.
anthony6e2e0732012-05-06 12:22:43 +00003802%
cristy97fc9f32012-06-13 21:04:44 +00003803% Attempting to set a property that is normally calculated will produce
anthony6e2e0732012-05-06 12:22:43 +00003804% an exception.
cristy3ed852e2009-09-05 21:47:34 +00003805%
3806% The format of the SetImageProperty method is:
3807%
3808% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003809% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003810%
3811% A description of each parameter follows:
3812%
3813% o image: the image.
3814%
3815% o property: the image property.
3816%
3817% o values: the image property values.
3818%
cristyd15e6592011-10-15 00:13:06 +00003819% o exception: return any errors or warnings in this structure.
3820%
cristy3ed852e2009-09-05 21:47:34 +00003821*/
3822MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003823 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003824{
3825 MagickBooleanType
3826 status;
3827
3828 MagickStatusType
3829 flags;
3830
3831 assert(image != (Image *) NULL);
3832 assert(image->signature == MagickSignature);
cristycb190b72014-08-31 20:04:42 +00003833 if (image->debug != MagickFalse)
anthony6e2e0732012-05-06 12:22:43 +00003834 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00003835 if (image->properties == (void *) NULL)
3836 image->properties=NewSplayTree(CompareSplayTreeString,
cristy8c8d3f52014-02-14 02:22:14 +00003837 RelinquishMagickMemory,RelinquishMagickMemory); /* create splay-tree */
glennrp9831cc42013-01-08 19:08:31 +00003838 if (value == (const char *) NULL)
cristy8c8d3f52014-02-14 02:22:14 +00003839 return(DeleteImageProperty(image,property)); /* delete if NULL */
cristy3ed852e2009-09-05 21:47:34 +00003840 status=MagickTrue;
anthony6e2e0732012-05-06 12:22:43 +00003841 if (strlen(property) <= 1)
3842 {
cristy8c8d3f52014-02-14 02:22:14 +00003843 /*
3844 Do not 'set' single letter properties - read only shorthand.
3845 */
3846 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3847 "SetReadOnlyProperty","`%s'",property);
anthony6e2e0732012-05-06 12:22:43 +00003848 return(MagickFalse);
3849 }
anthony7bb7aee2012-05-15 00:09:16 +00003850
anthonya322a832013-04-27 06:28:03 +00003851 /* FUTURE: binary chars or quotes in key should produce a error */
anthonya322a832013-04-27 06:28:03 +00003852 /* Set attributes with known names or special prefixes
3853 return result is found, or break to set a free form properity
3854 */
cristy3ed852e2009-09-05 21:47:34 +00003855 switch (*property)
3856 {
anthonya322a832013-04-27 06:28:03 +00003857#if 0 /* Percent escape's sets values with this prefix: for later use
3858 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00003859 case '8':
3860 {
3861 if (LocaleNCompare("8bim:",property,5) == 0)
3862 {
3863 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003864 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003865 return(MagickFalse);
3866 }
3867 break;
3868 }
anthonyc99875a2012-11-14 05:22:17 +00003869#endif
cristy3ed852e2009-09-05 21:47:34 +00003870 case 'B':
3871 case 'b':
3872 {
anthony2f7f9ca2012-05-14 06:33:53 +00003873 if (LocaleCompare("background",property) == 0)
cristyc6c08ab2010-07-24 23:50:09 +00003874 {
cristy9950d572011-10-01 18:22:35 +00003875 (void) QueryColorCompliance(value,AllCompliance,
anthony643c6132012-11-07 14:50:28 +00003876 &image->background_color,exception);
anthonya322a832013-04-27 06:28:03 +00003877 /* check for FUTURE: value exception?? */
anthony643c6132012-11-07 14:50:28 +00003878 /* also add user input to splay tree */
cristyc6c08ab2010-07-24 23:50:09 +00003879 }
anthonya322a832013-04-27 06:28:03 +00003880 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003881 }
3882 case 'C':
3883 case 'c':
3884 {
anthony2f7f9ca2012-05-14 06:33:53 +00003885 if (LocaleCompare("channels",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003886 {
cristy8c8d3f52014-02-14 02:22:14 +00003887 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3888 "SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003889 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003890 }
anthony2f7f9ca2012-05-14 06:33:53 +00003891 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003892 {
cristybb503372010-05-27 20:51:26 +00003893 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003894 colorspace;
3895
cristy042ee782011-04-22 18:48:30 +00003896 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003897 value);
3898 if (colorspace < 0)
anthonya322a832013-04-27 06:28:03 +00003899 return(MagickFalse); /* FUTURE: value exception?? */
cristy220c4d52013-11-27 19:31:32 +00003900 return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
cristy3ed852e2009-09-05 21:47:34 +00003901 }
anthony2f7f9ca2012-05-14 06:33:53 +00003902 if (LocaleCompare("compose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003903 {
cristybb503372010-05-27 20:51:26 +00003904 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003905 compose;
3906
cristy042ee782011-04-22 18:48:30 +00003907 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003908 if (compose < 0)
anthonya322a832013-04-27 06:28:03 +00003909 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003910 image->compose=(CompositeOperator) compose;
anthony643c6132012-11-07 14:50:28 +00003911 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003912 }
anthony2f7f9ca2012-05-14 06:33:53 +00003913 if (LocaleCompare("compress",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003914 {
cristybb503372010-05-27 20:51:26 +00003915 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003916 compression;
3917
cristy042ee782011-04-22 18:48:30 +00003918 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003919 value);
3920 if (compression < 0)
anthonya322a832013-04-27 06:28:03 +00003921 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003922 image->compression=(CompressionType) compression;
anthony643c6132012-11-07 14:50:28 +00003923 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003924 }
anthonya322a832013-04-27 06:28:03 +00003925 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00003926 }
3927 case 'D':
3928 case 'd':
3929 {
anthony2f7f9ca2012-05-14 06:33:53 +00003930 if (LocaleCompare("delay",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003931 {
3932 GeometryInfo
3933 geometry_info;
3934
3935 flags=ParseGeometry(value,&geometry_info);
3936 if ((flags & GreaterValue) != 0)
3937 {
cristybb503372010-05-27 20:51:26 +00003938 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3939 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003940 }
3941 else
3942 if ((flags & LessValue) != 0)
3943 {
cristybb503372010-05-27 20:51:26 +00003944 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
anthony643c6132012-11-07 14:50:28 +00003945 image->delay=(ssize_t)
cristyc6c08ab2010-07-24 23:50:09 +00003946 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003947 }
3948 else
cristybb503372010-05-27 20:51:26 +00003949 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003950 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003951 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
anthony643c6132012-11-07 14:50:28 +00003952 return(MagickTrue);
3953 }
3954 if (LocaleCompare("delay_units",property) == 0)
3955 {
3956 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003957 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003958 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00003959 }
anthony2f7f9ca2012-05-14 06:33:53 +00003960 if (LocaleCompare("density",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003961 {
3962 GeometryInfo
3963 geometry_info;
3964
3965 flags=ParseGeometry(value,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +00003966 image->resolution.x=geometry_info.rho;
3967 image->resolution.y=geometry_info.sigma;
cristyfbb56842010-08-02 11:26:33 +00003968 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +00003969 image->resolution.y=image->resolution.x;
anthony643c6132012-11-07 14:50:28 +00003970 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00003971 }
anthony2f7f9ca2012-05-14 06:33:53 +00003972 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003973 {
cristye27293e2009-12-18 02:53:20 +00003974 image->depth=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00003975 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003976 }
anthony2f7f9ca2012-05-14 06:33:53 +00003977 if (LocaleCompare("dispose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003978 {
cristybb503372010-05-27 20:51:26 +00003979 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003980 dispose;
3981
cristy042ee782011-04-22 18:48:30 +00003982 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003983 if (dispose < 0)
anthonya322a832013-04-27 06:28:03 +00003984 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00003985 image->dispose=(DisposeType) dispose;
anthony643c6132012-11-07 14:50:28 +00003986 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003987 }
anthonya322a832013-04-27 06:28:03 +00003988 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00003989 }
anthonya322a832013-04-27 06:28:03 +00003990#if 0 /* Percent escape's sets values with this prefix: for later use
3991 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00003992 case 'E':
3993 case 'e':
3994 {
3995 if (LocaleNCompare("exif:",property,5) == 0)
3996 {
3997 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00003998 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00003999 return(MagickFalse);
4000 }
anthonya322a832013-04-27 06:28:03 +00004001 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004002 }
4003 case 'F':
4004 case 'f':
4005 {
4006 if (LocaleNCompare("fx:",property,3) == 0)
4007 {
4008 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004009 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004010 return(MagickFalse);
4011 }
anthonya322a832013-04-27 06:28:03 +00004012 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004013 }
anthonyc99875a2012-11-14 05:22:17 +00004014#endif
cristy3ed852e2009-09-05 21:47:34 +00004015 case 'G':
4016 case 'g':
4017 {
anthony2f7f9ca2012-05-14 06:33:53 +00004018 if (LocaleCompare("gamma",property) == 0)
4019 {
4020 image->gamma=StringToDouble(value,(char **) NULL);
anthony643c6132012-11-07 14:50:28 +00004021 return(MagickTrue);
anthony2f7f9ca2012-05-14 06:33:53 +00004022 }
4023 if (LocaleCompare("gravity",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004024 {
cristybb503372010-05-27 20:51:26 +00004025 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004026 gravity;
4027
cristy042ee782011-04-22 18:48:30 +00004028 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00004029 if (gravity < 0)
anthonya322a832013-04-27 06:28:03 +00004030 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00004031 image->gravity=(GravityType) gravity;
anthony643c6132012-11-07 14:50:28 +00004032 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004033 }
anthonya322a832013-04-27 06:28:03 +00004034 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004035 }
anthony6e2e0732012-05-06 12:22:43 +00004036 case 'H':
4037 case 'h':
anthony643c6132012-11-07 14:50:28 +00004038 {
anthony2f7f9ca2012-05-14 06:33:53 +00004039 if (LocaleCompare("height",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004040 {
4041 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004042 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004043 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004044 }
anthonya322a832013-04-27 06:28:03 +00004045 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004046 }
cristy3ed852e2009-09-05 21:47:34 +00004047 case 'I':
4048 case 'i':
4049 {
cristy70e9f682013-03-12 22:31:22 +00004050 if (LocaleCompare("intensity",property) == 0)
4051 {
4052 ssize_t
4053 intensity;
4054
4055 intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,
4056 value);
4057 if (intensity < 0)
4058 return(MagickFalse);
4059 image->intensity=(PixelIntensityMethod) intensity;
4060 return(MagickTrue);
4061 }
anthony2f7f9ca2012-05-14 06:33:53 +00004062 if (LocaleCompare("intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004063 {
cristybb503372010-05-27 20:51:26 +00004064 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004065 rendering_intent;
4066
cristy042ee782011-04-22 18:48:30 +00004067 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004068 value);
4069 if (rendering_intent < 0)
anthonya322a832013-04-27 06:28:03 +00004070 return(MagickFalse); /* FUTURE: value exception?? */
cristy3ed852e2009-09-05 21:47:34 +00004071 image->rendering_intent=(RenderingIntent) rendering_intent;
anthony643c6132012-11-07 14:50:28 +00004072 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004073 }
anthony2f7f9ca2012-05-14 06:33:53 +00004074 if (LocaleCompare("interpolate",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004075 {
cristybb503372010-05-27 20:51:26 +00004076 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004077 interpolate;
4078
cristy042ee782011-04-22 18:48:30 +00004079 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004080 value);
4081 if (interpolate < 0)
anthonya322a832013-04-27 06:28:03 +00004082 return(MagickFalse); /* FUTURE: value exception?? */
cristy5c4e2582011-09-11 19:21:03 +00004083 image->interpolate=(PixelInterpolateMethod) interpolate;
anthony643c6132012-11-07 14:50:28 +00004084 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004085 }
anthonya322a832013-04-27 06:28:03 +00004086#if 0 /* Percent escape's sets values with this prefix: for later use
4087 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004088 if (LocaleNCompare("iptc:",property,5) == 0)
4089 {
4090 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004091 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004092 return(MagickFalse);
4093 }
anthonyc99875a2012-11-14 05:22:17 +00004094#endif
anthonya322a832013-04-27 06:28:03 +00004095 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004096 }
anthony6e2e0732012-05-06 12:22:43 +00004097 case 'K':
4098 case 'k':
anthony2f7f9ca2012-05-14 06:33:53 +00004099 if (LocaleCompare("kurtosis",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004100 {
4101 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004102 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004103 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004104 }
anthonya322a832013-04-27 06:28:03 +00004105 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004106 case 'L':
4107 case 'l':
4108 {
anthony2f7f9ca2012-05-14 06:33:53 +00004109 if (LocaleCompare("loop",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004110 {
cristye27293e2009-12-18 02:53:20 +00004111 image->iterations=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00004112 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004113 }
anthonya322a832013-04-27 06:28:03 +00004114 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004115 }
anthony6e2e0732012-05-06 12:22:43 +00004116 case 'M':
4117 case 'm':
anthony2f7f9ca2012-05-14 06:33:53 +00004118 if ( (LocaleCompare("magick",property) == 0) ||
4119 (LocaleCompare("max",property) == 0) ||
4120 (LocaleCompare("mean",property) == 0) ||
4121 (LocaleCompare("min",property) == 0) ||
4122 (LocaleCompare("min",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00004123 {
cristy5048d302012-08-07 01:05:16 +00004124 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +00004125 "SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004126 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004127 }
anthonya322a832013-04-27 06:28:03 +00004128 break; /* not an attribute, add as a property */
anthony6e2e0732012-05-06 12:22:43 +00004129 case 'O':
4130 case 'o':
anthony2f7f9ca2012-05-14 06:33:53 +00004131 if (LocaleCompare("opaque",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004132 {
4133 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004134 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004135 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004136 }
anthonya322a832013-04-27 06:28:03 +00004137 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004138 case 'P':
4139 case 'p':
4140 {
anthony2f7f9ca2012-05-14 06:33:53 +00004141 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004142 {
4143 char
4144 *geometry;
4145
4146 geometry=GetPageGeometry(value);
4147 flags=ParseAbsoluteGeometry(geometry,&image->page);
4148 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00004149 return(MagickTrue);
4150 }
anthonya322a832013-04-27 06:28:03 +00004151#if 0 /* Percent escape's sets values with this prefix: for later use
4152 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004153 if (LocaleNCompare("pixel:",property,6) == 0)
4154 {
4155 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004156 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004157 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00004158 }
anthonyc99875a2012-11-14 05:22:17 +00004159#endif
anthony2f7f9ca2012-05-14 06:33:53 +00004160 if (LocaleCompare("profile",property) == 0)
cristy071dd7b2010-04-09 13:04:54 +00004161 {
4162 ImageInfo
4163 *image_info;
4164
4165 StringInfo
4166 *profile;
4167
4168 image_info=AcquireImageInfo();
4169 (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
cristyc6c08ab2010-07-24 23:50:09 +00004170 (void) SetImageInfo(image_info,1,exception);
4171 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00004172 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00004173 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00004174 image_info=DestroyImageInfo(image_info);
anthony643c6132012-11-07 14:50:28 +00004175 return(MagickTrue);
cristy071dd7b2010-04-09 13:04:54 +00004176 }
anthonya322a832013-04-27 06:28:03 +00004177 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004178 }
4179 case 'R':
4180 case 'r':
4181 {
anthony2f7f9ca2012-05-14 06:33:53 +00004182 if (LocaleCompare("rendering-intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004183 {
cristybb503372010-05-27 20:51:26 +00004184 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004185 rendering_intent;
4186
cristy042ee782011-04-22 18:48:30 +00004187 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00004188 value);
4189 if (rendering_intent < 0)
anthonya322a832013-04-27 06:28:03 +00004190 return(MagickFalse); /* FUTURE: value exception?? */
anthony643c6132012-11-07 14:50:28 +00004191 image->rendering_intent=(RenderingIntent) rendering_intent;
4192 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004193 }
anthonya322a832013-04-27 06:28:03 +00004194 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004195 }
anthony6e2e0732012-05-06 12:22:43 +00004196 case 'S':
4197 case 's':
anthony2f7f9ca2012-05-14 06:33:53 +00004198 if ( (LocaleCompare("size",property) == 0) ||
4199 (LocaleCompare("skewness",property) == 0) ||
4200 (LocaleCompare("scenes",property) == 0) ||
4201 (LocaleCompare("standard-deviation",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00004202 {
4203 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004204 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004205 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004206 }
anthonya322a832013-04-27 06:28:03 +00004207 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004208 case 'T':
4209 case 't':
4210 {
anthony2f7f9ca2012-05-14 06:33:53 +00004211 if (LocaleCompare("tile-offset",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00004212 {
4213 char
4214 *geometry;
4215
4216 geometry=GetPageGeometry(value);
4217 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
4218 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00004219 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00004220 }
anthonya322a832013-04-27 06:28:03 +00004221 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004222 }
cristyfbb56842010-08-02 11:26:33 +00004223 case 'U':
4224 case 'u':
4225 {
anthony2f7f9ca2012-05-14 06:33:53 +00004226 if (LocaleCompare("units",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00004227 {
4228 ssize_t
4229 units;
4230
cristy042ee782011-04-22 18:48:30 +00004231 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00004232 if (units < 0)
anthonya322a832013-04-27 06:28:03 +00004233 return(MagickFalse); /* FUTURE: value exception?? */
cristyfbb56842010-08-02 11:26:33 +00004234 image->units=(ResolutionType) units;
anthony643c6132012-11-07 14:50:28 +00004235 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00004236 }
anthonya322a832013-04-27 06:28:03 +00004237 break; /* not an attribute, add as a property */
cristyfbb56842010-08-02 11:26:33 +00004238 }
anthony6e2e0732012-05-06 12:22:43 +00004239 case 'V':
4240 case 'v':
anthony643c6132012-11-07 14:50:28 +00004241 {
anthony2f7f9ca2012-05-14 06:33:53 +00004242 if (LocaleCompare("version",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004243 {
4244 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004245 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004246 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004247 }
anthonya322a832013-04-27 06:28:03 +00004248 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004249 }
anthony6e2e0732012-05-06 12:22:43 +00004250 case 'W':
4251 case 'w':
anthony643c6132012-11-07 14:50:28 +00004252 {
anthony2f7f9ca2012-05-14 06:33:53 +00004253 if (LocaleCompare("width",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00004254 {
4255 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004256 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004257 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00004258 }
anthonya322a832013-04-27 06:28:03 +00004259 break; /* not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004260 }
anthonya322a832013-04-27 06:28:03 +00004261#if 0 /* Percent escape's sets values with this prefix: for later use
4262 Throwing an exception causes this setting to fail */
anthony643c6132012-11-07 14:50:28 +00004263 case 'X':
4264 case 'x':
cristy3ed852e2009-09-05 21:47:34 +00004265 {
anthony643c6132012-11-07 14:50:28 +00004266 if (LocaleNCompare("xmp:",property,4) == 0)
4267 {
4268 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00004269 OptionError,"SetReadOnlyProperty","`%s'",property);
anthony643c6132012-11-07 14:50:28 +00004270 return(MagickFalse);
4271 }
anthonya322a832013-04-27 06:28:03 +00004272 break; /* not an attribute, add as a property */
cristy3ed852e2009-09-05 21:47:34 +00004273 }
anthonyc99875a2012-11-14 05:22:17 +00004274#endif
cristy3ed852e2009-09-05 21:47:34 +00004275 }
anthonya322a832013-04-27 06:28:03 +00004276 /* Default: not an attribute, add as a property */
anthony643c6132012-11-07 14:50:28 +00004277 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
4278 ConstantString(property),ConstantString(value));
4279 /* FUTURE: error if status is bad? */
cristy3ed852e2009-09-05 21:47:34 +00004280 return(status);
4281}