blob: 80263480ecc11e533692f0262efd4e13329540fd [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 %
16% John Cristy %
17% March 2000 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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*/
39
40/*
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"
47#include "MagickCore/color.h"
cristyd228c032012-06-03 15:01:15 +000048#include "MagickCore/color-private.h"
cristy3d9f5ba2012-06-26 13:37:31 +000049#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000050#include "MagickCore/compare.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/effect.h"
54#include "MagickCore/exception.h"
55#include "MagickCore/exception-private.h"
56#include "MagickCore/fx.h"
57#include "MagickCore/fx-private.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
cristy4c08aed2011-07-01 19:47:50 +000062#include "MagickCore/layer.h"
cristy7832dc22011-09-05 01:21:53 +000063#include "MagickCore/locale-private.h"
cristy4c08aed2011-07-01 19:47:50 +000064#include "MagickCore/list.h"
65#include "MagickCore/magick.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/montage.h"
69#include "MagickCore/option.h"
70#include "MagickCore/profile.h"
71#include "MagickCore/property.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/resource_.h"
74#include "MagickCore/splay-tree.h"
cristy7832dc22011-09-05 01:21:53 +000075#include "MagickCore/signature.h"
cristy4c08aed2011-07-01 19:47:50 +000076#include "MagickCore/statistic.h"
77#include "MagickCore/string_.h"
78#include "MagickCore/string-private.h"
79#include "MagickCore/token.h"
cristy7832dc22011-09-05 01:21:53 +000080#include "MagickCore/token-private.h"
cristy4c08aed2011-07-01 19:47:50 +000081#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000082#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000083#include "MagickCore/version.h"
84#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000085#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000086
87/*
88%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89% %
90% %
91% %
92% C l o n e I m a g e P r o p e r t i e s %
93% %
94% %
95% %
96%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97%
anthonyd2f39e02012-08-20 12:29:28 +000098% CloneImageProperties() clones all the image properties to another image.
cristy3ed852e2009-09-05 21:47:34 +000099%
100% The format of the CloneImageProperties method is:
101%
102% MagickBooleanType CloneImageProperties(Image *image,
103% const Image *clone_image)
104%
105% A description of each parameter follows:
106%
107% o image: the image.
108%
109% o clone_image: the clone image.
110%
111*/
112MagickExport MagickBooleanType CloneImageProperties(Image *image,
113 const Image *clone_image)
114{
115 assert(image != (Image *) NULL);
116 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000117 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000118 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
119 assert(clone_image != (const Image *) NULL);
120 assert(clone_image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000121 if( IfMagickTrue(clone_image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000122 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
123 clone_image->filename);
124 (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
125 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
126 MaxTextExtent);
127 image->compression=clone_image->compression;
128 image->quality=clone_image->quality;
129 image->depth=clone_image->depth;
130 image->background_color=clone_image->background_color;
131 image->border_color=clone_image->border_color;
132 image->matte_color=clone_image->matte_color;
133 image->transparent_color=clone_image->transparent_color;
134 image->gamma=clone_image->gamma;
135 image->chromaticity=clone_image->chromaticity;
136 image->rendering_intent=clone_image->rendering_intent;
137 image->black_point_compensation=clone_image->black_point_compensation;
138 image->units=clone_image->units;
139 image->montage=(char *) NULL;
140 image->directory=(char *) NULL;
141 (void) CloneString(&image->geometry,clone_image->geometry);
142 image->offset=clone_image->offset;
cristy2a11bef2011-10-28 18:33:11 +0000143 image->resolution.x=clone_image->resolution.x;
144 image->resolution.y=clone_image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +0000145 image->page=clone_image->page;
146 image->tile_offset=clone_image->tile_offset;
147 image->extract_info=clone_image->extract_info;
cristy3ed852e2009-09-05 21:47:34 +0000148 image->filter=clone_image->filter;
cristy3ed852e2009-09-05 21:47:34 +0000149 image->fuzz=clone_image->fuzz;
150 image->interlace=clone_image->interlace;
151 image->interpolate=clone_image->interpolate;
152 image->endian=clone_image->endian;
153 image->gravity=clone_image->gravity;
154 image->compose=clone_image->compose;
155 image->scene=clone_image->scene;
156 image->orientation=clone_image->orientation;
157 image->dispose=clone_image->dispose;
158 image->delay=clone_image->delay;
159 image->ticks_per_second=clone_image->ticks_per_second;
160 image->iterations=clone_image->iterations;
161 image->total_colors=clone_image->total_colors;
162 image->taint=clone_image->taint;
163 image->progress_monitor=clone_image->progress_monitor;
164 image->client_data=clone_image->client_data;
165 image->start_loop=clone_image->start_loop;
166 image->error=clone_image->error;
167 image->signature=clone_image->signature;
168 if (clone_image->properties != (void *) NULL)
169 {
170 if (image->properties != (void *) NULL)
171 DestroyImageProperties(image);
172 image->properties=CloneSplayTree((SplayTreeInfo *)
173 clone_image->properties,(void *(*)(void *)) ConstantString,
174 (void *(*)(void *)) ConstantString);
175 }
176 return(MagickTrue);
177}
178
179/*
180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181% %
182% %
183% %
184% D e f i n e I m a g e P r o p e r t y %
185% %
186% %
187% %
188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189%
anthonyd2f39e02012-08-20 12:29:28 +0000190% DefineImageProperty() associates an assignment string of the form
191% "key=value" with per-image artifact. It is equivelent to
192% SetImageProperity().
cristy3ed852e2009-09-05 21:47:34 +0000193%
194% The format of the DefineImageProperty method is:
195%
196% MagickBooleanType DefineImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000197% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000198%
199% A description of each parameter follows:
200%
201% o image: the image.
202%
203% o property: the image property.
204%
cristyd15e6592011-10-15 00:13:06 +0000205% o exception: return any errors or warnings in this structure.
206%
cristy3ed852e2009-09-05 21:47:34 +0000207*/
208MagickExport MagickBooleanType DefineImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000209 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000210{
211 char
212 key[MaxTextExtent],
213 value[MaxTextExtent];
214
215 register char
216 *p;
217
218 assert(image != (Image *) NULL);
219 assert(property != (const char *) NULL);
220 (void) CopyMagickString(key,property,MaxTextExtent-1);
221 for (p=key; *p != '\0'; p++)
222 if (*p == '=')
223 break;
224 *value='\0';
225 if (*p == '=')
226 (void) CopyMagickString(value,p+1,MaxTextExtent);
227 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000228 return(SetImageProperty(image,key,value,exception));
cristy3ed852e2009-09-05 21:47:34 +0000229}
230
231/*
232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233% %
234% %
235% %
236% D e l e t e I m a g e P r o p e r t y %
237% %
238% %
239% %
240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241%
242% DeleteImageProperty() deletes an image property.
243%
244% The format of the DeleteImageProperty method is:
245%
246% MagickBooleanType DeleteImageProperty(Image *image,const char *property)
247%
248% A description of each parameter follows:
249%
250% o image: the image.
251%
252% o property: the image property.
253%
254*/
255MagickExport MagickBooleanType DeleteImageProperty(Image *image,
256 const char *property)
257{
258 assert(image != (Image *) NULL);
259 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000260 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000261 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
262 image->filename);
263 if (image->properties == (void *) NULL)
264 return(MagickFalse);
265 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
266}
267
268/*
269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270% %
271% %
272% %
273% D e s t r o y I m a g e P r o p e r t i e s %
274% %
275% %
276% %
277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278%
anthonyd2f39e02012-08-20 12:29:28 +0000279% DestroyImageProperties() destroys all properities and associated memory
280% attached to the given image.
cristy3ed852e2009-09-05 21:47:34 +0000281%
282% The format of the DestroyDefines method is:
283%
284% void DestroyImageProperties(Image *image)
285%
286% A description of each parameter follows:
287%
288% o image: the image.
289%
290*/
291MagickExport void DestroyImageProperties(Image *image)
292{
293 assert(image != (Image *) NULL);
294 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +0000295 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +0000296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
297 image->filename);
298 if (image->properties != (void *) NULL)
299 image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
300 image->properties);
301}
302
303/*
304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305% %
306% %
307% %
308% F o r m a t I m a g e P r o p e r t y %
309% %
310% %
311% %
312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313%
314% FormatImageProperty() permits formatted property/value pairs to be saved as
glennrp2cc891a2010-12-24 13:44:32 +0000315% an image property.
cristy3ed852e2009-09-05 21:47:34 +0000316%
317% The format of the FormatImageProperty method is:
318%
319% MagickBooleanType FormatImageProperty(Image *image,const char *property,
320% const char *format,...)
321%
322% A description of each parameter follows.
323%
324% o image: The image.
325%
326% o property: The attribute property.
327%
328% o format: A string describing the format to use to write the remaining
329% arguments.
330%
331*/
cristydb584ae2011-05-20 14:50:53 +0000332MagickExport MagickBooleanType FormatImageProperty(Image *image,
333 const char *property,const char *format,...)
cristy3ed852e2009-09-05 21:47:34 +0000334{
335 char
336 value[MaxTextExtent];
337
cristyc82a27b2011-10-21 01:07:16 +0000338 ExceptionInfo
339 *exception;
340
341 MagickBooleanType
342 status;
343
cristy20ec7592011-05-29 01:28:05 +0000344 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000345 n;
346
cristy3ed852e2009-09-05 21:47:34 +0000347 va_list
348 operands;
349
350 va_start(operands,format);
cristydb584ae2011-05-20 14:50:53 +0000351 n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
352 (void) n;
cristy3ed852e2009-09-05 21:47:34 +0000353 va_end(operands);
cristyc82a27b2011-10-21 01:07:16 +0000354 exception=AcquireExceptionInfo();
355 status=SetImageProperty(image,property,value,exception);
356 exception=DestroyExceptionInfo(exception);
357 return(status);
cristy3ed852e2009-09-05 21:47:34 +0000358}
359
360/*
361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362% %
363% %
364% %
365% G e t I m a g e P r o p e r t y %
366% %
367% %
368% %
369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370%
371% GetImageProperty() gets a value associated with an image property.
372%
anthonyd2f39e02012-08-20 12:29:28 +0000373% The returned string is a constant string in the tree and should NOT be
374% freed by the caller.
375%
cristy3ed852e2009-09-05 21:47:34 +0000376% The format of the GetImageProperty method is:
377%
cristyd15e6592011-10-15 00:13:06 +0000378% const char *GetImageProperty(const Image *image,const char *key,
379% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000380%
381% A description of each parameter follows:
382%
383% o image: the image.
384%
385% o key: the key.
386%
cristyd15e6592011-10-15 00:13:06 +0000387% o exception: return any errors or warnings in this structure.
388%
cristy3ed852e2009-09-05 21:47:34 +0000389*/
390
391static char
cristybb503372010-05-27 20:51:26 +0000392 *TracePSClippath(const unsigned char *,size_t,const size_t,
393 const size_t),
394 *TraceSVGClippath(const unsigned char *,size_t,const size_t,
395 const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000396
cristyd15e6592011-10-15 00:13:06 +0000397static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
398 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000399{
400 char
401 *attribute,
402 *message;
403
404 const StringInfo
405 *profile;
406
cristycee97112010-05-28 00:44:52 +0000407 long
cristy3ed852e2009-09-05 21:47:34 +0000408 count,
409 dataset,
410 record;
411
cristybb503372010-05-27 20:51:26 +0000412 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000413 i;
414
415 size_t
416 length;
417
418 profile=GetImageProfile(image,"iptc");
419 if (profile == (StringInfo *) NULL)
420 profile=GetImageProfile(image,"8bim");
421 if (profile == (StringInfo *) NULL)
422 return(MagickFalse);
423 count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
424 if (count != 2)
425 return(MagickFalse);
426 attribute=(char *) NULL;
cristybb503372010-05-27 20:51:26 +0000427 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
cristy3ed852e2009-09-05 21:47:34 +0000428 {
429 length=1;
cristybb503372010-05-27 20:51:26 +0000430 if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
cristy3ed852e2009-09-05 21:47:34 +0000431 continue;
432 length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
433 length|=GetStringInfoDatum(profile)[i+4];
cristycee97112010-05-28 00:44:52 +0000434 if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
435 ((long) GetStringInfoDatum(profile)[i+2] == record))
cristy3ed852e2009-09-05 21:47:34 +0000436 {
437 message=(char *) NULL;
438 if (~length >= 1)
439 message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
440 if (message != (char *) NULL)
441 {
442 (void) CopyMagickString(message,(char *) GetStringInfoDatum(
443 profile)+i+5,length+1);
444 (void) ConcatenateString(&attribute,message);
445 (void) ConcatenateString(&attribute,";");
446 message=DestroyString(message);
447 }
448 }
449 i+=5;
450 }
451 if ((attribute == (char *) NULL) || (*attribute == ';'))
452 {
453 if (attribute != (char *) NULL)
454 attribute=DestroyString(attribute);
455 return(MagickFalse);
456 }
457 attribute[strlen(attribute)-1]='\0';
cristyd15e6592011-10-15 00:13:06 +0000458 (void) SetImageProperty((Image *) image,key,(const char *) attribute,
459 exception);
cristy3ed852e2009-09-05 21:47:34 +0000460 attribute=DestroyString(attribute);
461 return(MagickTrue);
462}
463
cristybb503372010-05-27 20:51:26 +0000464static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000465{
466 if (x > y)
467 return(x);
468 return(y);
469}
470
cristy4a8b0172012-02-03 16:39:53 +0000471static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
472{
473 if (x < y)
474 return(x);
475 return(y);
476}
477
cristy3ed852e2009-09-05 21:47:34 +0000478static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
479{
480 int
481 c;
482
483 if (*length < 1)
484 return(EOF);
485 c=(int) (*(*p)++);
486 (*length)--;
487 return(c);
488}
489
cristybb503372010-05-27 20:51:26 +0000490static inline size_t ReadPropertyMSBLong(const unsigned char **p,
cristy3ed852e2009-09-05 21:47:34 +0000491 size_t *length)
492{
493 int
494 c;
495
cristybb503372010-05-27 20:51:26 +0000496 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000497 i;
498
499 unsigned char
500 buffer[4];
501
cristybb503372010-05-27 20:51:26 +0000502 size_t
cristy3ed852e2009-09-05 21:47:34 +0000503 value;
504
505 if (*length < 4)
506 return(~0UL);
507 for (i=0; i < 4; i++)
508 {
509 c=(int) (*(*p)++);
510 (*length)--;
511 buffer[i]=(unsigned char) c;
512 }
cristybb503372010-05-27 20:51:26 +0000513 value=(size_t) (buffer[0] << 24);
cristy3ed852e2009-09-05 21:47:34 +0000514 value|=buffer[1] << 16;
515 value|=buffer[2] << 8;
516 value|=buffer[3];
517 return(value & 0xffffffff);
518}
519
520static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
521 size_t *length)
522{
523 int
524 c;
525
cristybb503372010-05-27 20:51:26 +0000526 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000527 i;
528
529 unsigned char
530 buffer[2];
531
532 unsigned short
533 value;
534
535 if (*length < 2)
536 return((unsigned short) ~0U);
537 for (i=0; i < 2; i++)
538 {
539 c=(int) (*(*p)++);
540 (*length)--;
541 buffer[i]=(unsigned char) c;
542 }
543 value=(unsigned short) (buffer[0] << 8);
544 value|=buffer[1];
545 return((unsigned short) (value & 0xffff));
546}
547
cristyd15e6592011-10-15 00:13:06 +0000548static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
549 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000550{
551 char
552 *attribute,
553 format[MaxTextExtent],
554 name[MaxTextExtent],
555 *resource;
556
557 const StringInfo
558 *profile;
559
560 const unsigned char
561 *info;
562
cristycee97112010-05-28 00:44:52 +0000563 long
cristy3ed852e2009-09-05 21:47:34 +0000564 start,
cristycee97112010-05-28 00:44:52 +0000565 stop;
cristy3ed852e2009-09-05 21:47:34 +0000566
567 MagickBooleanType
568 status;
569
cristybb503372010-05-27 20:51:26 +0000570 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000571 i;
572
573 ssize_t
cristycee97112010-05-28 00:44:52 +0000574 count,
575 id,
576 sub_number;
cristy3ed852e2009-09-05 21:47:34 +0000577
578 size_t
579 length;
580
581 /*
glennrp2cc891a2010-12-24 13:44:32 +0000582 There are no newlines in path names, so it's safe as terminator.
cristy3ed852e2009-09-05 21:47:34 +0000583 */
584 profile=GetImageProfile(image,"8bim");
585 if (profile == (StringInfo *) NULL)
586 return(MagickFalse);
587 count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
588 format);
589 if ((count != 2) && (count != 3) && (count != 4))
590 return(MagickFalse);
591 if (count < 4)
592 (void) CopyMagickString(format,"SVG",MaxTextExtent);
593 if (count < 3)
594 *name='\0';
595 sub_number=1;
596 if (*name == '#')
cristyad740052010-07-03 01:38:03 +0000597 sub_number=(ssize_t) StringToLong(&name[1]);
cristy3ed852e2009-09-05 21:47:34 +0000598 sub_number=MagickMax(sub_number,1L);
599 resource=(char *) NULL;
600 status=MagickFalse;
601 length=GetStringInfoLength(profile);
602 info=GetStringInfoDatum(profile);
anthony2fbb5952012-05-05 12:35:30 +0000603 while ((length > 0) && IfMagickFalse(status))
cristy3ed852e2009-09-05 21:47:34 +0000604 {
605 if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
606 continue;
607 if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
608 continue;
609 if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
610 continue;
611 if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
612 continue;
cristy4a8b0172012-02-03 16:39:53 +0000613 id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
cristycee97112010-05-28 00:44:52 +0000614 if (id < (ssize_t) start)
cristy3ed852e2009-09-05 21:47:34 +0000615 continue;
cristycee97112010-05-28 00:44:52 +0000616 if (id > (ssize_t) stop)
cristy3ed852e2009-09-05 21:47:34 +0000617 continue;
618 if (resource != (char *) NULL)
619 resource=DestroyString(resource);
620 count=(ssize_t) ReadPropertyByte(&info,&length);
621 if ((count != 0) && ((size_t) count <= length))
622 {
623 resource=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +0000624 if (~((size_t) count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000625 resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
626 sizeof(*resource));
627 if (resource != (char *) NULL)
628 {
cristybb503372010-05-27 20:51:26 +0000629 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000630 resource[i]=(char) ReadPropertyByte(&info,&length);
631 resource[count]='\0';
632 }
633 }
634 if ((count & 0x01) == 0)
635 (void) ReadPropertyByte(&info,&length);
cristy55a91cd2010-12-01 00:57:40 +0000636 count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
cristy3ed852e2009-09-05 21:47:34 +0000637 if ((*name != '\0') && (*name != '#'))
638 if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
639 {
640 /*
641 No name match, scroll forward and try next.
642 */
643 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000644 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000645 continue;
646 }
647 if ((*name == '#') && (sub_number != 1))
648 {
649 /*
650 No numbered match, scroll forward and try next.
651 */
652 sub_number--;
653 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000654 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000655 continue;
656 }
657 /*
658 We have the resource of interest.
659 */
660 attribute=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +0000661 if (~((size_t) count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000662 attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
663 sizeof(*attribute));
664 if (attribute != (char *) NULL)
665 {
666 (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
667 attribute[count]='\0';
668 info+=count;
cristy4a8b0172012-02-03 16:39:53 +0000669 length-=MagickMin(count,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +0000670 if ((id <= 1999) || (id >= 2999))
671 (void) SetImageProperty((Image *) image,key,(const char *)
cristyd15e6592011-10-15 00:13:06 +0000672 attribute,exception);
cristy3ed852e2009-09-05 21:47:34 +0000673 else
674 {
675 char
676 *path;
677
678 if (LocaleCompare(format,"svg") == 0)
679 path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
680 image->columns,image->rows);
681 else
682 path=TracePSClippath((unsigned char *) attribute,(size_t) count,
683 image->columns,image->rows);
cristyd15e6592011-10-15 00:13:06 +0000684 (void) SetImageProperty((Image *) image,key,(const char *) path,
685 exception);
cristy3ed852e2009-09-05 21:47:34 +0000686 path=DestroyString(path);
687 }
688 attribute=DestroyString(attribute);
689 status=MagickTrue;
690 }
691 }
692 if (resource != (char *) NULL)
693 resource=DestroyString(resource);
694 return(status);
695}
696
697static inline unsigned short ReadPropertyShort(const EndianType endian,
698 const unsigned char *buffer)
699{
700 unsigned short
701 value;
702
703 if (endian == MSBEndian)
704 {
705 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
706 ((unsigned char *) buffer)[1]);
707 return((unsigned short) (value & 0xffff));
708 }
709 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
710 return((unsigned short) (value & 0xffff));
711}
712
cristybb503372010-05-27 20:51:26 +0000713static inline size_t ReadPropertyLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +0000714 const unsigned char *buffer)
715{
cristybb503372010-05-27 20:51:26 +0000716 size_t
cristy3ed852e2009-09-05 21:47:34 +0000717 value;
718
719 if (endian == MSBEndian)
720 {
cristybb503372010-05-27 20:51:26 +0000721 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +0000722 (buffer[2] << 8) | buffer[3]);
cristybb503372010-05-27 20:51:26 +0000723 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000724 }
cristybb503372010-05-27 20:51:26 +0000725 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
cristy3ed852e2009-09-05 21:47:34 +0000726 (buffer[1] << 8 ) | (buffer[0]));
cristybb503372010-05-27 20:51:26 +0000727 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000728}
729
730static MagickBooleanType GetEXIFProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +0000731 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000732{
733#define MaxDirectoryStack 16
734#define EXIF_DELIMITER "\n"
735#define EXIF_NUM_FORMATS 12
736#define EXIF_FMT_BYTE 1
737#define EXIF_FMT_STRING 2
738#define EXIF_FMT_USHORT 3
739#define EXIF_FMT_ULONG 4
740#define EXIF_FMT_URATIONAL 5
741#define EXIF_FMT_SBYTE 6
742#define EXIF_FMT_UNDEFINED 7
743#define EXIF_FMT_SSHORT 8
744#define EXIF_FMT_SLONG 9
745#define EXIF_FMT_SRATIONAL 10
746#define EXIF_FMT_SINGLE 11
747#define EXIF_FMT_DOUBLE 12
748#define TAG_EXIF_OFFSET 0x8769
749#define TAG_GPS_OFFSET 0x8825
750#define TAG_INTEROP_OFFSET 0xa005
751
cristy96ea4862011-11-22 00:45:47 +0000752#define EXIFMultipleValues(size,format,arg) \
cristy3ed852e2009-09-05 21:47:34 +0000753{ \
cristybb503372010-05-27 20:51:26 +0000754 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000755 component; \
756 \
757 size_t \
758 length; \
759 \
760 unsigned char \
761 *p1; \
762 \
763 length=0; \
764 p1=p; \
765 for (component=0; component < components; component++) \
766 { \
cristyb51dff52011-05-19 16:55:47 +0000767 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristy3ed852e2009-09-05 21:47:34 +0000768 format", ",arg); \
cristy37e0b382011-06-07 13:31:21 +0000769 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000770 length=MaxTextExtent-1; \
771 p1+=size; \
772 } \
773 if (length > 1) \
774 buffer[length-2]='\0'; \
775 value=AcquireString(buffer); \
776}
777
cristy96ea4862011-11-22 00:45:47 +0000778#define EXIFMultipleFractions(size,format,arg1,arg2) \
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, \
cristyd66c1082012-03-28 18:17:37 +0000794 format", ",(arg1),(arg2)); \
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
804 typedef struct _DirectoryInfo
805 {
806 const unsigned char
807 *directory;
808
cristybb503372010-05-27 20:51:26 +0000809 size_t
cristy3d8d1f12012-02-29 01:59:28 +0000810 entry;
811
812 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000813 offset;
814 } DirectoryInfo;
815
816 typedef struct _TagInfo
817 {
cristybb503372010-05-27 20:51:26 +0000818 size_t
cristy3ed852e2009-09-05 21:47:34 +0000819 tag;
820
821 const char
822 *description;
823 } TagInfo;
824
825 static TagInfo
826 EXIFTag[] =
827 {
828 { 0x001, "exif:InteroperabilityIndex" },
829 { 0x002, "exif:InteroperabilityVersion" },
830 { 0x100, "exif:ImageWidth" },
831 { 0x101, "exif:ImageLength" },
832 { 0x102, "exif:BitsPerSample" },
833 { 0x103, "exif:Compression" },
834 { 0x106, "exif:PhotometricInterpretation" },
835 { 0x10a, "exif:FillOrder" },
836 { 0x10d, "exif:DocumentName" },
837 { 0x10e, "exif:ImageDescription" },
838 { 0x10f, "exif:Make" },
839 { 0x110, "exif:Model" },
840 { 0x111, "exif:StripOffsets" },
841 { 0x112, "exif:Orientation" },
842 { 0x115, "exif:SamplesPerPixel" },
843 { 0x116, "exif:RowsPerStrip" },
844 { 0x117, "exif:StripByteCounts" },
845 { 0x11a, "exif:XResolution" },
846 { 0x11b, "exif:YResolution" },
847 { 0x11c, "exif:PlanarConfiguration" },
848 { 0x11d, "exif:PageName" },
849 { 0x11e, "exif:XPosition" },
850 { 0x11f, "exif:YPosition" },
851 { 0x118, "exif:MinSampleValue" },
852 { 0x119, "exif:MaxSampleValue" },
853 { 0x120, "exif:FreeOffsets" },
854 { 0x121, "exif:FreeByteCounts" },
855 { 0x122, "exif:GrayResponseUnit" },
856 { 0x123, "exif:GrayResponseCurve" },
857 { 0x124, "exif:T4Options" },
858 { 0x125, "exif:T6Options" },
859 { 0x128, "exif:ResolutionUnit" },
860 { 0x12d, "exif:TransferFunction" },
861 { 0x131, "exif:Software" },
862 { 0x132, "exif:DateTime" },
863 { 0x13b, "exif:Artist" },
864 { 0x13e, "exif:WhitePoint" },
865 { 0x13f, "exif:PrimaryChromaticities" },
866 { 0x140, "exif:ColorMap" },
867 { 0x141, "exif:HalfToneHints" },
868 { 0x142, "exif:TileWidth" },
869 { 0x143, "exif:TileLength" },
870 { 0x144, "exif:TileOffsets" },
871 { 0x145, "exif:TileByteCounts" },
872 { 0x14a, "exif:SubIFD" },
873 { 0x14c, "exif:InkSet" },
874 { 0x14d, "exif:InkNames" },
875 { 0x14e, "exif:NumberOfInks" },
876 { 0x150, "exif:DotRange" },
877 { 0x151, "exif:TargetPrinter" },
878 { 0x152, "exif:ExtraSample" },
879 { 0x153, "exif:SampleFormat" },
880 { 0x154, "exif:SMinSampleValue" },
881 { 0x155, "exif:SMaxSampleValue" },
882 { 0x156, "exif:TransferRange" },
883 { 0x157, "exif:ClipPath" },
884 { 0x158, "exif:XClipPathUnits" },
885 { 0x159, "exif:YClipPathUnits" },
886 { 0x15a, "exif:Indexed" },
887 { 0x15b, "exif:JPEGTables" },
888 { 0x15f, "exif:OPIProxy" },
889 { 0x200, "exif:JPEGProc" },
890 { 0x201, "exif:JPEGInterchangeFormat" },
891 { 0x202, "exif:JPEGInterchangeFormatLength" },
892 { 0x203, "exif:JPEGRestartInterval" },
893 { 0x205, "exif:JPEGLosslessPredictors" },
894 { 0x206, "exif:JPEGPointTransforms" },
895 { 0x207, "exif:JPEGQTables" },
896 { 0x208, "exif:JPEGDCTables" },
897 { 0x209, "exif:JPEGACTables" },
898 { 0x211, "exif:YCbCrCoefficients" },
899 { 0x212, "exif:YCbCrSubSampling" },
900 { 0x213, "exif:YCbCrPositioning" },
901 { 0x214, "exif:ReferenceBlackWhite" },
902 { 0x2bc, "exif:ExtensibleMetadataPlatform" },
903 { 0x301, "exif:Gamma" },
904 { 0x302, "exif:ICCProfileDescriptor" },
905 { 0x303, "exif:SRGBRenderingIntent" },
906 { 0x320, "exif:ImageTitle" },
907 { 0x5001, "exif:ResolutionXUnit" },
908 { 0x5002, "exif:ResolutionYUnit" },
909 { 0x5003, "exif:ResolutionXLengthUnit" },
910 { 0x5004, "exif:ResolutionYLengthUnit" },
911 { 0x5005, "exif:PrintFlags" },
912 { 0x5006, "exif:PrintFlagsVersion" },
913 { 0x5007, "exif:PrintFlagsCrop" },
914 { 0x5008, "exif:PrintFlagsBleedWidth" },
915 { 0x5009, "exif:PrintFlagsBleedWidthScale" },
916 { 0x500A, "exif:HalftoneLPI" },
917 { 0x500B, "exif:HalftoneLPIUnit" },
918 { 0x500C, "exif:HalftoneDegree" },
919 { 0x500D, "exif:HalftoneShape" },
920 { 0x500E, "exif:HalftoneMisc" },
921 { 0x500F, "exif:HalftoneScreen" },
922 { 0x5010, "exif:JPEGQuality" },
923 { 0x5011, "exif:GridSize" },
924 { 0x5012, "exif:ThumbnailFormat" },
925 { 0x5013, "exif:ThumbnailWidth" },
926 { 0x5014, "exif:ThumbnailHeight" },
927 { 0x5015, "exif:ThumbnailColorDepth" },
928 { 0x5016, "exif:ThumbnailPlanes" },
929 { 0x5017, "exif:ThumbnailRawBytes" },
930 { 0x5018, "exif:ThumbnailSize" },
931 { 0x5019, "exif:ThumbnailCompressedSize" },
932 { 0x501a, "exif:ColorTransferFunction" },
933 { 0x501b, "exif:ThumbnailData" },
934 { 0x5020, "exif:ThumbnailImageWidth" },
935 { 0x5021, "exif:ThumbnailImageHeight" },
936 { 0x5022, "exif:ThumbnailBitsPerSample" },
937 { 0x5023, "exif:ThumbnailCompression" },
938 { 0x5024, "exif:ThumbnailPhotometricInterp" },
939 { 0x5025, "exif:ThumbnailImageDescription" },
940 { 0x5026, "exif:ThumbnailEquipMake" },
941 { 0x5027, "exif:ThumbnailEquipModel" },
942 { 0x5028, "exif:ThumbnailStripOffsets" },
943 { 0x5029, "exif:ThumbnailOrientation" },
944 { 0x502a, "exif:ThumbnailSamplesPerPixel" },
945 { 0x502b, "exif:ThumbnailRowsPerStrip" },
946 { 0x502c, "exif:ThumbnailStripBytesCount" },
947 { 0x502d, "exif:ThumbnailResolutionX" },
948 { 0x502e, "exif:ThumbnailResolutionY" },
949 { 0x502f, "exif:ThumbnailPlanarConfig" },
950 { 0x5030, "exif:ThumbnailResolutionUnit" },
951 { 0x5031, "exif:ThumbnailTransferFunction" },
952 { 0x5032, "exif:ThumbnailSoftwareUsed" },
953 { 0x5033, "exif:ThumbnailDateTime" },
954 { 0x5034, "exif:ThumbnailArtist" },
955 { 0x5035, "exif:ThumbnailWhitePoint" },
956 { 0x5036, "exif:ThumbnailPrimaryChromaticities" },
957 { 0x5037, "exif:ThumbnailYCbCrCoefficients" },
958 { 0x5038, "exif:ThumbnailYCbCrSubsampling" },
959 { 0x5039, "exif:ThumbnailYCbCrPositioning" },
960 { 0x503A, "exif:ThumbnailRefBlackWhite" },
961 { 0x503B, "exif:ThumbnailCopyRight" },
962 { 0x5090, "exif:LuminanceTable" },
963 { 0x5091, "exif:ChrominanceTable" },
964 { 0x5100, "exif:FrameDelay" },
965 { 0x5101, "exif:LoopCount" },
966 { 0x5110, "exif:PixelUnit" },
967 { 0x5111, "exif:PixelPerUnitX" },
968 { 0x5112, "exif:PixelPerUnitY" },
969 { 0x5113, "exif:PaletteHistogram" },
970 { 0x1000, "exif:RelatedImageFileFormat" },
971 { 0x1001, "exif:RelatedImageLength" },
972 { 0x1002, "exif:RelatedImageWidth" },
973 { 0x800d, "exif:ImageID" },
974 { 0x80e3, "exif:Matteing" },
975 { 0x80e4, "exif:DataType" },
976 { 0x80e5, "exif:ImageDepth" },
977 { 0x80e6, "exif:TileDepth" },
978 { 0x828d, "exif:CFARepeatPatternDim" },
979 { 0x828e, "exif:CFAPattern2" },
980 { 0x828f, "exif:BatteryLevel" },
981 { 0x8298, "exif:Copyright" },
982 { 0x829a, "exif:ExposureTime" },
983 { 0x829d, "exif:FNumber" },
984 { 0x83bb, "exif:IPTC/NAA" },
985 { 0x84e3, "exif:IT8RasterPadding" },
986 { 0x84e5, "exif:IT8ColorTable" },
987 { 0x8649, "exif:ImageResourceInformation" },
988 { 0x8769, "exif:ExifOffset" },
989 { 0x8773, "exif:InterColorProfile" },
990 { 0x8822, "exif:ExposureProgram" },
991 { 0x8824, "exif:SpectralSensitivity" },
992 { 0x8825, "exif:GPSInfo" },
993 { 0x8827, "exif:ISOSpeedRatings" },
994 { 0x8828, "exif:OECF" },
995 { 0x8829, "exif:Interlace" },
996 { 0x882a, "exif:TimeZoneOffset" },
997 { 0x882b, "exif:SelfTimerMode" },
998 { 0x9000, "exif:ExifVersion" },
999 { 0x9003, "exif:DateTimeOriginal" },
1000 { 0x9004, "exif:DateTimeDigitized" },
1001 { 0x9101, "exif:ComponentsConfiguration" },
1002 { 0x9102, "exif:CompressedBitsPerPixel" },
1003 { 0x9201, "exif:ShutterSpeedValue" },
1004 { 0x9202, "exif:ApertureValue" },
1005 { 0x9203, "exif:BrightnessValue" },
1006 { 0x9204, "exif:ExposureBiasValue" },
1007 { 0x9205, "exif:MaxApertureValue" },
1008 { 0x9206, "exif:SubjectDistance" },
1009 { 0x9207, "exif:MeteringMode" },
1010 { 0x9208, "exif:LightSource" },
1011 { 0x9209, "exif:Flash" },
1012 { 0x920a, "exif:FocalLength" },
1013 { 0x920b, "exif:FlashEnergy" },
1014 { 0x920c, "exif:SpatialFrequencyResponse" },
1015 { 0x920d, "exif:Noise" },
1016 { 0x9211, "exif:ImageNumber" },
1017 { 0x9212, "exif:SecurityClassification" },
1018 { 0x9213, "exif:ImageHistory" },
1019 { 0x9214, "exif:SubjectArea" },
1020 { 0x9215, "exif:ExposureIndex" },
1021 { 0x9216, "exif:TIFF-EPStandardID" },
1022 { 0x927c, "exif:MakerNote" },
1023 { 0x9C9b, "exif:WinXP-Title" },
1024 { 0x9C9c, "exif:WinXP-Comments" },
1025 { 0x9C9d, "exif:WinXP-Author" },
1026 { 0x9C9e, "exif:WinXP-Keywords" },
1027 { 0x9C9f, "exif:WinXP-Subject" },
1028 { 0x9286, "exif:UserComment" },
1029 { 0x9290, "exif:SubSecTime" },
1030 { 0x9291, "exif:SubSecTimeOriginal" },
1031 { 0x9292, "exif:SubSecTimeDigitized" },
1032 { 0xa000, "exif:FlashPixVersion" },
1033 { 0xa001, "exif:ColorSpace" },
1034 { 0xa002, "exif:ExifImageWidth" },
1035 { 0xa003, "exif:ExifImageLength" },
1036 { 0xa004, "exif:RelatedSoundFile" },
1037 { 0xa005, "exif:InteroperabilityOffset" },
1038 { 0xa20b, "exif:FlashEnergy" },
1039 { 0xa20c, "exif:SpatialFrequencyResponse" },
1040 { 0xa20d, "exif:Noise" },
1041 { 0xa20e, "exif:FocalPlaneXResolution" },
1042 { 0xa20f, "exif:FocalPlaneYResolution" },
1043 { 0xa210, "exif:FocalPlaneResolutionUnit" },
1044 { 0xa214, "exif:SubjectLocation" },
1045 { 0xa215, "exif:ExposureIndex" },
1046 { 0xa216, "exif:TIFF/EPStandardID" },
1047 { 0xa217, "exif:SensingMethod" },
1048 { 0xa300, "exif:FileSource" },
1049 { 0xa301, "exif:SceneType" },
1050 { 0xa302, "exif:CFAPattern" },
1051 { 0xa401, "exif:CustomRendered" },
1052 { 0xa402, "exif:ExposureMode" },
1053 { 0xa403, "exif:WhiteBalance" },
1054 { 0xa404, "exif:DigitalZoomRatio" },
1055 { 0xa405, "exif:FocalLengthIn35mmFilm" },
1056 { 0xa406, "exif:SceneCaptureType" },
1057 { 0xa407, "exif:GainControl" },
1058 { 0xa408, "exif:Contrast" },
1059 { 0xa409, "exif:Saturation" },
1060 { 0xa40a, "exif:Sharpness" },
1061 { 0xa40b, "exif:DeviceSettingDescription" },
1062 { 0xa40c, "exif:SubjectDistanceRange" },
1063 { 0xa420, "exif:ImageUniqueID" },
1064 { 0xc4a5, "exif:PrintImageMatching" },
cristyb3fea0e2009-11-28 01:46:20 +00001065 { 0xa500, "exif:Gamma" },
1066 { 0xc640, "exif:CR2Slice" },
cristy3ed852e2009-09-05 21:47:34 +00001067 { 0x10000, "exif:GPSVersionID" },
1068 { 0x10001, "exif:GPSLatitudeRef" },
1069 { 0x10002, "exif:GPSLatitude" },
1070 { 0x10003, "exif:GPSLongitudeRef" },
1071 { 0x10004, "exif:GPSLongitude" },
1072 { 0x10005, "exif:GPSAltitudeRef" },
1073 { 0x10006, "exif:GPSAltitude" },
1074 { 0x10007, "exif:GPSTimeStamp" },
1075 { 0x10008, "exif:GPSSatellites" },
1076 { 0x10009, "exif:GPSStatus" },
1077 { 0x1000a, "exif:GPSMeasureMode" },
1078 { 0x1000b, "exif:GPSDop" },
1079 { 0x1000c, "exif:GPSSpeedRef" },
1080 { 0x1000d, "exif:GPSSpeed" },
1081 { 0x1000e, "exif:GPSTrackRef" },
1082 { 0x1000f, "exif:GPSTrack" },
1083 { 0x10010, "exif:GPSImgDirectionRef" },
1084 { 0x10011, "exif:GPSImgDirection" },
1085 { 0x10012, "exif:GPSMapDatum" },
1086 { 0x10013, "exif:GPSDestLatitudeRef" },
1087 { 0x10014, "exif:GPSDestLatitude" },
1088 { 0x10015, "exif:GPSDestLongitudeRef" },
1089 { 0x10016, "exif:GPSDestLongitude" },
1090 { 0x10017, "exif:GPSDestBearingRef" },
1091 { 0x10018, "exif:GPSDestBearing" },
1092 { 0x10019, "exif:GPSDestDistanceRef" },
1093 { 0x1001a, "exif:GPSDestDistance" },
1094 { 0x1001b, "exif:GPSProcessingMethod" },
1095 { 0x1001c, "exif:GPSAreaInformation" },
1096 { 0x1001d, "exif:GPSDateStamp" },
1097 { 0x1001e, "exif:GPSDifferential" },
1098 { 0x0000, NULL}
1099 };
1100
1101 const StringInfo
1102 *profile;
1103
1104 const unsigned char
1105 *directory,
1106 *exif;
1107
1108 DirectoryInfo
1109 directory_stack[MaxDirectoryStack];
1110
1111 EndianType
1112 endian;
1113
cristy929ea322011-02-21 15:21:35 +00001114 MagickBooleanType
1115 status;
cristy3ed852e2009-09-05 21:47:34 +00001116
cristybb503372010-05-27 20:51:26 +00001117 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001118 i;
1119
1120 size_t
cristy3ed852e2009-09-05 21:47:34 +00001121 entry,
cristy929ea322011-02-21 15:21:35 +00001122 length,
cristy3ed852e2009-09-05 21:47:34 +00001123 number_entries,
cristy3ed852e2009-09-05 21:47:34 +00001124 tag;
1125
cristy142ab012012-02-02 02:26:18 +00001126 SplayTreeInfo
1127 *exif_resources;
1128
cristy929ea322011-02-21 15:21:35 +00001129 ssize_t
1130 all,
1131 id,
1132 level,
1133 offset,
cristy3d8d1f12012-02-29 01:59:28 +00001134 tag_offset,
cristy929ea322011-02-21 15:21:35 +00001135 tag_value;
1136
1137 static int
1138 tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1139
cristy3ed852e2009-09-05 21:47:34 +00001140 /*
1141 If EXIF data exists, then try to parse the request for a tag.
1142 */
1143 profile=GetImageProfile(image,"exif");
1144 if (profile == (StringInfo *) NULL)
1145 return(MagickFalse);
1146 if ((property == (const char *) NULL) || (*property == '\0'))
1147 return(MagickFalse);
1148 while (isspace((int) ((unsigned char) *property)) != 0)
1149 property++;
1150 all=0;
1151 tag=(~0UL);
1152 switch (*(property+5))
1153 {
1154 case '*':
1155 {
1156 /*
1157 Caller has asked for all the tags in the EXIF data.
1158 */
1159 tag=0;
1160 all=1; /* return the data in description=value format */
1161 break;
1162 }
1163 case '!':
1164 {
1165 tag=0;
1166 all=2; /* return the data in tagid=value format */
1167 break;
1168 }
1169 case '#':
1170 case '@':
1171 {
1172 int
1173 c;
1174
1175 size_t
1176 n;
1177
1178 /*
1179 Check for a hex based tag specification first.
1180 */
1181 tag=(*(property+5) == '@') ? 1UL : 0UL;
1182 property+=6;
1183 n=strlen(property);
1184 if (n != 4)
1185 return(MagickFalse);
1186 /*
1187 Parse tag specification as a hex number.
1188 */
1189 n/=4;
1190 do
1191 {
cristybb503372010-05-27 20:51:26 +00001192 for (i=(ssize_t) n-1L; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +00001193 {
1194 c=(*property++);
1195 tag<<=4;
1196 if ((c >= '0') && (c <= '9'))
1197 tag|=(c-'0');
1198 else
1199 if ((c >= 'A') && (c <= 'F'))
1200 tag|=(c-('A'-10));
1201 else
1202 if ((c >= 'a') && (c <= 'f'))
1203 tag|=(c-('a'-10));
1204 else
1205 return(MagickFalse);
1206 }
1207 } while (*property != '\0');
1208 break;
1209 }
1210 default:
1211 {
1212 /*
1213 Try to match the text with a tag name instead.
1214 */
1215 for (i=0; ; i++)
1216 {
1217 if (EXIFTag[i].tag == 0)
1218 break;
1219 if (LocaleCompare(EXIFTag[i].description,property) == 0)
1220 {
cristybb503372010-05-27 20:51:26 +00001221 tag=(size_t) EXIFTag[i].tag;
cristy3ed852e2009-09-05 21:47:34 +00001222 break;
1223 }
1224 }
1225 break;
1226 }
1227 }
1228 if (tag == (~0UL))
1229 return(MagickFalse);
1230 length=GetStringInfoLength(profile);
1231 exif=GetStringInfoDatum(profile);
1232 while (length != 0)
1233 {
1234 if (ReadPropertyByte(&exif,&length) != 0x45)
1235 continue;
1236 if (ReadPropertyByte(&exif,&length) != 0x78)
1237 continue;
1238 if (ReadPropertyByte(&exif,&length) != 0x69)
1239 continue;
1240 if (ReadPropertyByte(&exif,&length) != 0x66)
1241 continue;
1242 if (ReadPropertyByte(&exif,&length) != 0x00)
1243 continue;
1244 if (ReadPropertyByte(&exif,&length) != 0x00)
1245 continue;
1246 break;
1247 }
1248 if (length < 16)
1249 return(MagickFalse);
cristyf0696672012-02-01 23:43:06 +00001250 id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
cristy3ed852e2009-09-05 21:47:34 +00001251 endian=LSBEndian;
1252 if (id == 0x4949)
1253 endian=LSBEndian;
1254 else
1255 if (id == 0x4D4D)
1256 endian=MSBEndian;
1257 else
1258 return(MagickFalse);
1259 if (ReadPropertyShort(endian,exif+2) != 0x002a)
1260 return(MagickFalse);
1261 /*
1262 This the offset to the first IFD.
1263 */
cristy55a91cd2010-12-01 00:57:40 +00001264 offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
cristy64de1572012-02-29 02:44:11 +00001265 if ((offset < 0) || (size_t) offset >= length)
cristy3ed852e2009-09-05 21:47:34 +00001266 return(MagickFalse);
1267 /*
1268 Set the pointer to the first IFD and follow it were it leads.
1269 */
cristy929ea322011-02-21 15:21:35 +00001270 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001271 directory=exif+offset;
1272 level=0;
1273 entry=0;
1274 tag_offset=0;
cristy142ab012012-02-02 02:26:18 +00001275 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1276 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1277 do
cristy3ed852e2009-09-05 21:47:34 +00001278 {
1279 /*
1280 If there is anything on the stack then pop it off.
1281 */
1282 if (level > 0)
1283 {
1284 level--;
1285 directory=directory_stack[level].directory;
1286 entry=directory_stack[level].entry;
1287 tag_offset=directory_stack[level].offset;
1288 }
1289 /*
1290 Determine how many entries there are in the current IFD.
1291 */
cristy4a8b0172012-02-03 16:39:53 +00001292 number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
cristy3ed852e2009-09-05 21:47:34 +00001293 for ( ; entry < number_entries; entry++)
1294 {
cristy3ed852e2009-09-05 21:47:34 +00001295 register unsigned char
1296 *p,
1297 *q;
1298
1299 size_t
anthony90ebc0d2012-04-28 08:10:02 +00001300 format;
cristy3ed852e2009-09-05 21:47:34 +00001301
cristy9d314ff2011-03-09 01:30:28 +00001302 ssize_t
anthony90ebc0d2012-04-28 08:10:02 +00001303 number_bytes,
cristy9d314ff2011-03-09 01:30:28 +00001304 components;
cristy3ed852e2009-09-05 21:47:34 +00001305
cristyf0696672012-02-01 23:43:06 +00001306 q=(unsigned char *) (directory+(12*entry)+2);
cristy142ab012012-02-02 02:26:18 +00001307 if (GetValueFromSplayTree(exif_resources,q) == q)
1308 break;
1309 (void) AddValueToSplayTree(exif_resources,q,q);
cristyf0696672012-02-01 23:43:06 +00001310 tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
1311 format=(size_t) ((int) ReadPropertyShort(endian,q+2));
cristy3ed852e2009-09-05 21:47:34 +00001312 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1313 break;
cristy55a91cd2010-12-01 00:57:40 +00001314 components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00001315 number_bytes=(size_t) components*tag_bytes[format];
cristy5f06bc12012-04-03 12:47:36 +00001316 if (number_bytes < components)
1317 break; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001318 if (number_bytes <= 4)
1319 p=q+8;
1320 else
1321 {
1322 ssize_t
1323 offset;
1324
1325 /*
1326 The directory entry contains an offset.
1327 */
cristy55a91cd2010-12-01 00:57:40 +00001328 offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
anthony90ebc0d2012-04-28 08:10:02 +00001329 if ((ssize_t) (offset+number_bytes) < offset)
1330 continue; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00001331 if ((size_t) (offset+number_bytes) > length)
1332 continue;
1333 p=(unsigned char *) (exif+offset);
1334 }
cristybb503372010-05-27 20:51:26 +00001335 if ((all != 0) || (tag == (size_t) tag_value))
cristy3ed852e2009-09-05 21:47:34 +00001336 {
1337 char
1338 buffer[MaxTextExtent],
1339 *value;
1340
cristy7ad8a132012-03-28 22:42:29 +00001341 value=(char *) NULL;
1342 *buffer='\0';
cristy3ed852e2009-09-05 21:47:34 +00001343 switch (format)
1344 {
1345 case EXIF_FMT_BYTE:
1346 case EXIF_FMT_UNDEFINED:
1347 {
cristy13adad02011-08-16 19:22:15 +00001348 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001349 break;
1350 }
1351 case EXIF_FMT_SBYTE:
1352 {
cristye8c25f92010-06-03 00:53:06 +00001353 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001354 break;
1355 }
1356 case EXIF_FMT_SSHORT:
1357 {
1358 EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
1359 break;
1360 }
1361 case EXIF_FMT_USHORT:
1362 {
1363 EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
1364 break;
1365 }
1366 case EXIF_FMT_ULONG:
1367 {
cristye8c25f92010-06-03 00:53:06 +00001368 EXIFMultipleValues(4,"%.20g",(double)
cristy4a8b0172012-02-03 16:39:53 +00001369 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001370 break;
1371 }
1372 case EXIF_FMT_SLONG:
1373 {
cristye8c25f92010-06-03 00:53:06 +00001374 EXIFMultipleValues(4,"%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001375 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001376 break;
1377 }
1378 case EXIF_FMT_URATIONAL:
1379 {
cristye8c25f92010-06-03 00:53:06 +00001380 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristyf0696672012-02-01 23:43:06 +00001381 ((int) ReadPropertyLong(endian,p1)),(double)
1382 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001383 break;
1384 }
1385 case EXIF_FMT_SRATIONAL:
1386 {
cristye8c25f92010-06-03 00:53:06 +00001387 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001388 ((int) ReadPropertyLong(endian,p1)),(double)
1389 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001390 break;
1391 }
1392 case EXIF_FMT_SINGLE:
1393 {
1394 EXIFMultipleValues(4,"%f",(double) *(float *) p1);
1395 break;
1396 }
1397 case EXIF_FMT_DOUBLE:
1398 {
1399 EXIFMultipleValues(8,"%f",*(double *) p1);
1400 break;
1401 }
1402 default:
1403 case EXIF_FMT_STRING:
1404 {
1405 value=(char *) NULL;
cristy9c66d8c2012-08-10 11:05:36 +00001406 if (~((size_t) number_bytes) >= 1)
cristy3ed852e2009-09-05 21:47:34 +00001407 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1408 sizeof(*value));
1409 if (value != (char *) NULL)
1410 {
cristybb503372010-05-27 20:51:26 +00001411 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001412 i;
1413
cristybb503372010-05-27 20:51:26 +00001414 for (i=0; i < (ssize_t) number_bytes; i++)
cristy3ed852e2009-09-05 21:47:34 +00001415 {
1416 value[i]='.';
1417 if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1418 value[i]=(char) p[i];
1419 }
1420 value[i]='\0';
1421 }
1422 break;
1423 }
1424 }
1425 if (value != (char *) NULL)
1426 {
1427 char
1428 key[MaxTextExtent];
1429
1430 register const char
1431 *p;
1432
1433 (void) CopyMagickString(key,property,MaxTextExtent);
1434 switch (all)
1435 {
1436 case 1:
1437 {
1438 const char
1439 *description;
1440
cristybb503372010-05-27 20:51:26 +00001441 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001442 i;
1443
1444 description="unknown";
1445 for (i=0; ; i++)
1446 {
1447 if (EXIFTag[i].tag == 0)
1448 break;
cristybb503372010-05-27 20:51:26 +00001449 if ((ssize_t) EXIFTag[i].tag == tag_value)
cristy3ed852e2009-09-05 21:47:34 +00001450 {
1451 description=EXIFTag[i].description;
1452 break;
1453 }
1454 }
cristy96ea4862011-11-22 00:45:47 +00001455 (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
cristy3ed852e2009-09-05 21:47:34 +00001456 break;
1457 }
1458 case 2:
1459 {
1460 if (tag_value < 0x10000)
cristyb51dff52011-05-19 16:55:47 +00001461 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001462 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001463 else
1464 if (tag_value < 0x20000)
cristyb51dff52011-05-19 16:55:47 +00001465 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001466 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001467 else
cristyb51dff52011-05-19 16:55:47 +00001468 (void) FormatLocaleString(key,MaxTextExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001469 break;
1470 }
1471 }
1472 p=(const char *) NULL;
1473 if (image->properties != (void *) NULL)
1474 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1475 image->properties,key);
1476 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001477 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001478 value=DestroyString(value);
cristy929ea322011-02-21 15:21:35 +00001479 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001480 }
1481 }
1482 if ((tag_value == TAG_EXIF_OFFSET) ||
cristy4a8b0172012-02-03 16:39:53 +00001483 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
cristy3ed852e2009-09-05 21:47:34 +00001484 {
cristy3d8d1f12012-02-29 01:59:28 +00001485 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 offset;
1487
cristy3d8d1f12012-02-29 01:59:28 +00001488 offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
1489 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00001490 {
cristy3d8d1f12012-02-29 01:59:28 +00001491 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001492 tag_offset1;
1493
cristy3d8d1f12012-02-29 01:59:28 +00001494 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1495 0);
cristy3ed852e2009-09-05 21:47:34 +00001496 directory_stack[level].directory=directory;
1497 entry++;
1498 directory_stack[level].entry=entry;
1499 directory_stack[level].offset=tag_offset;
1500 level++;
1501 directory_stack[level].directory=exif+offset;
1502 directory_stack[level].offset=tag_offset1;
1503 directory_stack[level].entry=0;
1504 level++;
1505 if ((directory+2+(12*number_entries)) > (exif+length))
1506 break;
cristy3d8d1f12012-02-29 01:59:28 +00001507 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
cristy4a8b0172012-02-03 16:39:53 +00001508 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00001509 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00001510 (level < (MaxDirectoryStack-2)))
1511 {
1512 directory_stack[level].directory=exif+offset;
1513 directory_stack[level].entry=0;
1514 directory_stack[level].offset=tag_offset1;
1515 level++;
1516 }
1517 }
1518 break;
1519 }
1520 }
cristy142ab012012-02-02 02:26:18 +00001521 } while (level > 0);
1522 exif_resources=DestroySplayTree(exif_resources);
cristy929ea322011-02-21 15:21:35 +00001523 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001524}
1525
cristy13adad02011-08-16 19:22:15 +00001526static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001527{
1528 char
1529 *xmp_profile;
1530
1531 const StringInfo
1532 *profile;
1533
1534 ExceptionInfo
1535 *exception;
1536
1537 MagickBooleanType
1538 status;
1539
1540 register const char
1541 *p;
1542
1543 XMLTreeInfo
1544 *child,
1545 *description,
1546 *node,
1547 *rdf,
1548 *xmp;
1549
1550 profile=GetImageProfile(image,"xmp");
1551 if (profile == (StringInfo *) NULL)
1552 return(MagickFalse);
1553 if ((property == (const char *) NULL) || (*property == '\0'))
1554 return(MagickFalse);
1555 xmp_profile=StringInfoToString(profile);
1556 if (xmp_profile == (char *) NULL)
1557 return(MagickFalse);
1558 for (p=xmp_profile; *p != '\0'; p++)
1559 if ((*p == '<') && (*(p+1) == 'x'))
1560 break;
1561 exception=AcquireExceptionInfo();
1562 xmp=NewXMLTree((char *) p,exception);
1563 xmp_profile=DestroyString(xmp_profile);
1564 exception=DestroyExceptionInfo(exception);
1565 if (xmp == (XMLTreeInfo *) NULL)
1566 return(MagickFalse);
1567 status=MagickFalse;
1568 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1569 if (rdf != (XMLTreeInfo *) NULL)
1570 {
1571 if (image->properties == (void *) NULL)
1572 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1573 RelinquishMagickMemory,RelinquishMagickMemory);
1574 description=GetXMLTreeChild(rdf,"rdf:Description");
1575 while (description != (XMLTreeInfo *) NULL)
1576 {
1577 node=GetXMLTreeChild(description,(const char *) NULL);
1578 while (node != (XMLTreeInfo *) NULL)
1579 {
1580 child=GetXMLTreeChild(node,(const char *) NULL);
1581 if (child == (XMLTreeInfo *) NULL)
1582 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1583 ConstantString(GetXMLTreeTag(node)),
1584 ConstantString(GetXMLTreeContent(node)));
1585 while (child != (XMLTreeInfo *) NULL)
1586 {
1587 if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
1588 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1589 ConstantString(GetXMLTreeTag(child)),
1590 ConstantString(GetXMLTreeContent(child)));
1591 child=GetXMLTreeSibling(child);
1592 }
1593 node=GetXMLTreeSibling(node);
1594 }
1595 description=GetNextXMLTreeTag(description);
1596 }
1597 }
1598 xmp=DestroyXMLTree(xmp);
1599 return(status);
1600}
1601
1602static char *TracePSClippath(const unsigned char *blob,size_t length,
cristyf5d0a1a2012-02-03 12:33:10 +00001603 const size_t magick_unused(columns),const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001604{
1605 char
1606 *path,
1607 *message;
1608
cristy3ed852e2009-09-05 21:47:34 +00001609 MagickBooleanType
1610 in_subpath;
1611
1612 PointInfo
1613 first[3],
1614 last[3],
1615 point[3];
1616
cristybb503372010-05-27 20:51:26 +00001617 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001618 i,
1619 x;
1620
cristy9d314ff2011-03-09 01:30:28 +00001621 ssize_t
1622 knot_count,
1623 selector,
1624 y;
1625
cristy3ed852e2009-09-05 21:47:34 +00001626 path=AcquireString((char *) NULL);
1627 if (path == (char *) NULL)
1628 return((char *) NULL);
1629 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001630 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001631 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001632 (void) FormatLocaleString(message,MaxTextExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001633 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001634 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001635 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001636 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001637 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001638 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001639 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001640 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001641 " /v {currentpoint 6 2 roll curveto} bind def\n");
1642 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001643 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001644 " /y {2 copy curveto} bind def\n");
1645 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001646 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001647 " /z {closepath} bind def\n");
1648 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001649 (void) FormatLocaleString(message,MaxTextExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001650 (void) ConcatenateString(&path,message);
1651 /*
1652 The clipping path format is defined in "Adobe Photoshop File
1653 Formats Specification" version 6.0 downloadable from adobe.com.
1654 */
1655 (void) ResetMagickMemory(point,0,sizeof(point));
1656 (void) ResetMagickMemory(first,0,sizeof(first));
1657 (void) ResetMagickMemory(last,0,sizeof(last));
1658 knot_count=0;
1659 in_subpath=MagickFalse;
1660 while (length > 0)
1661 {
cristy4a8b0172012-02-03 16:39:53 +00001662 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001663 switch (selector)
1664 {
1665 case 0:
1666 case 3:
1667 {
1668 if (knot_count != 0)
1669 {
1670 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001671 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001672 break;
1673 }
1674 /*
1675 Expected subpath length record.
1676 */
cristy4a8b0172012-02-03 16:39:53 +00001677 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001678 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001679 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001680 break;
1681 }
1682 case 1:
1683 case 2:
1684 case 4:
1685 case 5:
1686 {
1687 if (knot_count == 0)
1688 {
1689 /*
1690 Unexpected subpath knot
1691 */
1692 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001693 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001694 break;
1695 }
1696 /*
1697 Add sub-path knot
1698 */
1699 for (i=0; i < 3; i++)
1700 {
cristy13b9a2e2010-11-10 14:03:51 +00001701 size_t
cristy97433202009-10-27 02:05:08 +00001702 xx,
1703 yy;
1704
cristy4a8b0172012-02-03 16:39:53 +00001705 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1706 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001707 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001708 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001709 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001710 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001711 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001712 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001713 point[i].x=(double) x/4096/4096;
1714 point[i].y=1.0-(double) y/4096/4096;
1715 }
anthony2fbb5952012-05-05 12:35:30 +00001716 if( IfMagickFalse(in_subpath) )
cristy3ed852e2009-09-05 21:47:34 +00001717 {
cristyb51dff52011-05-19 16:55:47 +00001718 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001719 point[1].x,point[1].y);
1720 for (i=0; i < 3; i++)
1721 {
1722 first[i]=point[i];
1723 last[i]=point[i];
1724 }
1725 }
1726 else
1727 {
1728 /*
1729 Handle special cases when Bezier curves are used to describe
1730 corners and straight lines.
1731 */
1732 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1733 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001734 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001735 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001736 else
1737 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001738 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001739 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001740 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001741 else
1742 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001743 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001744 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001745 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001746 else
cristyb51dff52011-05-19 16:55:47 +00001747 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001748 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001749 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001750 for (i=0; i < 3; i++)
1751 last[i]=point[i];
1752 }
1753 (void) ConcatenateString(&path,message);
1754 in_subpath=MagickTrue;
1755 knot_count--;
1756 /*
1757 Close the subpath if there are no more knots.
1758 */
1759 if (knot_count == 0)
1760 {
1761 /*
1762 Same special handling as above except we compare to the
1763 first point in the path and close the path.
1764 */
1765 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1766 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001767 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001768 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001769 else
1770 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001771 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001772 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001773 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001774 else
1775 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001776 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001777 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001778 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001779 else
cristyb51dff52011-05-19 16:55:47 +00001780 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001781 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001782 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001783 (void) ConcatenateString(&path,message);
1784 in_subpath=MagickFalse;
1785 }
1786 break;
1787 }
1788 case 6:
1789 case 7:
1790 case 8:
1791 default:
1792 {
1793 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001794 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001795 break;
1796 }
1797 }
1798 }
1799 /*
1800 Returns an empty PS path if the path has no knots.
1801 */
cristyb51dff52011-05-19 16:55:47 +00001802 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001803 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001804 (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001805 (void) ConcatenateString(&path,message);
1806 message=DestroyString(message);
1807 return(path);
1808}
1809
1810static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001811 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001812{
1813 char
1814 *path,
1815 *message;
1816
cristy3ed852e2009-09-05 21:47:34 +00001817 MagickBooleanType
1818 in_subpath;
1819
1820 PointInfo
1821 first[3],
1822 last[3],
1823 point[3];
1824
cristybb503372010-05-27 20:51:26 +00001825 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001826 i;
1827
cristy9d314ff2011-03-09 01:30:28 +00001828 ssize_t
1829 knot_count,
1830 selector,
1831 x,
1832 y;
1833
cristy3ed852e2009-09-05 21:47:34 +00001834 path=AcquireString((char *) NULL);
1835 if (path == (char *) NULL)
1836 return((char *) NULL);
1837 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001838 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001839 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
1840 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001841 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001842 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001843 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001844 (void) FormatLocaleString(message,MaxTextExtent,"<g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001845 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001846 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001847 "<path style=\"fill:#00000000;stroke:#00000000;");
1848 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001849 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001850 "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
1851 (void) ConcatenateString(&path,message);
1852 (void) ResetMagickMemory(point,0,sizeof(point));
1853 (void) ResetMagickMemory(first,0,sizeof(first));
1854 (void) ResetMagickMemory(last,0,sizeof(last));
1855 knot_count=0;
1856 in_subpath=MagickFalse;
1857 while (length != 0)
1858 {
cristy4a8b0172012-02-03 16:39:53 +00001859 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001860 switch (selector)
1861 {
1862 case 0:
1863 case 3:
1864 {
1865 if (knot_count != 0)
1866 {
1867 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001868 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001869 break;
1870 }
1871 /*
1872 Expected subpath length record.
1873 */
cristy4a8b0172012-02-03 16:39:53 +00001874 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001875 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001876 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001877 break;
1878 }
1879 case 1:
1880 case 2:
1881 case 4:
1882 case 5:
1883 {
1884 if (knot_count == 0)
1885 {
1886 /*
1887 Unexpected subpath knot.
1888 */
1889 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001890 length-=MagickMin(24,(ssize_t) length);
cristy97433202009-10-27 02:05:08 +00001891 break;
1892 }
1893 /*
1894 Add sub-path knot
1895 */
1896 for (i=0; i < 3; i++)
1897 {
cristyd4982b42011-03-21 14:24:23 +00001898 size_t
cristy97433202009-10-27 02:05:08 +00001899 xx,
1900 yy;
1901
cristy4a8b0172012-02-03 16:39:53 +00001902 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1903 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001904 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001905 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001906 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001907 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001908 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001909 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00001910 point[i].x=(double) x*columns/4096/4096;
1911 point[i].y=(double) y*rows/4096/4096;
1912 }
anthony2fbb5952012-05-05 12:35:30 +00001913 if( IfMagickFalse(in_subpath) )
cristy97433202009-10-27 02:05:08 +00001914 {
cristyb51dff52011-05-19 16:55:47 +00001915 (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001916 point[1].x,point[1].y);
1917 for (i=0; i < 3; i++)
1918 {
1919 first[i]=point[i];
1920 last[i]=point[i];
1921 }
cristy3ed852e2009-09-05 21:47:34 +00001922 }
1923 else
1924 {
cristy97433202009-10-27 02:05:08 +00001925 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1926 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001927 (void) FormatLocaleString(message,MaxTextExtent,"L %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001928 point[1].x,point[1].y);
1929 else
cristyb51dff52011-05-19 16:55:47 +00001930 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001931 "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
cristy97433202009-10-27 02:05:08 +00001932 point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001933 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00001934 last[i]=point[i];
1935 }
1936 (void) ConcatenateString(&path,message);
1937 in_subpath=MagickTrue;
1938 knot_count--;
1939 /*
1940 Close the subpath if there are no more knots.
1941 */
1942 if (knot_count == 0)
1943 {
1944 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1945 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001946 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001947 "L %g,%g Z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001948 else
1949 {
cristyb51dff52011-05-19 16:55:47 +00001950 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001951 "C %g,%g %g,%g %g,%g Z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001952 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy97433202009-10-27 02:05:08 +00001953 (void) ConcatenateString(&path,message);
cristy3ed852e2009-09-05 21:47:34 +00001954 }
cristy97433202009-10-27 02:05:08 +00001955 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001956 }
cristy97433202009-10-27 02:05:08 +00001957 break;
cristy3ed852e2009-09-05 21:47:34 +00001958 }
1959 case 6:
1960 case 7:
1961 case 8:
1962 default:
1963 {
1964 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001965 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001966 break;
1967 }
1968 }
1969 }
1970 /*
1971 Return an empty SVG image if the path does not have knots.
1972 */
cristyb51dff52011-05-19 16:55:47 +00001973 (void) FormatLocaleString(message,MaxTextExtent,"\"/>\n");
cristy3ed852e2009-09-05 21:47:34 +00001974 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001975 (void) FormatLocaleString(message,MaxTextExtent,"</g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001976 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001977 (void) FormatLocaleString(message,MaxTextExtent,"</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00001978 (void) ConcatenateString(&path,message);
1979 message=DestroyString(message);
1980 return(path);
1981}
1982
1983MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00001984 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001985{
cristy3ed852e2009-09-05 21:47:34 +00001986 FxInfo
1987 *fx_info;
1988
cristya19f1d72012-08-07 18:24:38 +00001989 double
cristy3ed852e2009-09-05 21:47:34 +00001990 alpha;
1991
1992 MagickStatusType
1993 status;
1994
1995 register const char
1996 *p;
1997
1998 assert(image != (Image *) NULL);
1999 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00002000 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00002001 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2002 p=(const char *) NULL;
cristy27f7af22010-06-21 12:23:21 +00002003 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002004 {
cristy5eb9fc82011-02-21 15:05:41 +00002005 if (property == (const char *) NULL)
2006 {
2007 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
2008 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
2009 image->properties);
2010 return(p);
2011 }
anthonyd2f39e02012-08-20 12:29:28 +00002012 if (LocaleNCompare("fx:",property,3) != 0) /* NOT %[fx:..] !!!! */
cristyd4982b42011-03-21 14:24:23 +00002013 {
2014 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2015 image->properties,property);
2016 if (p != (const char *) NULL)
2017 return(p);
2018 }
cristy3ed852e2009-09-05 21:47:34 +00002019 }
cristy5eb9fc82011-02-21 15:05:41 +00002020 if ((property == (const char *) NULL) ||
2021 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002022 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002023 switch (*property)
2024 {
2025 case '8':
2026 {
2027 if (LocaleNCompare("8bim:",property,5) == 0)
2028 {
anthony2fbb5952012-05-05 12:35:30 +00002029 if( IfMagickTrue(Get8BIMProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002030 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002031 {
2032 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2033 image->properties,property);
2034 return(p);
2035 }
2036 }
2037 break;
2038 }
2039 case 'E':
2040 case 'e':
2041 {
2042 if (LocaleNCompare("exif:",property,5) == 0)
2043 {
anthony2fbb5952012-05-05 12:35:30 +00002044 if( IfMagickTrue(GetEXIFProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002045 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002046 {
2047 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2048 image->properties,property);
2049 return(p);
2050 }
2051 }
2052 break;
2053 }
2054 case 'F':
2055 case 'f':
2056 {
cristy27f7af22010-06-21 12:23:21 +00002057 if (LocaleNCompare("fx:",property,3) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002058 {
cristydb070952012-04-20 14:33:00 +00002059 fx_info=AcquireFxInfo(image,property+3,exception);
cristy2bddff82011-07-25 18:39:12 +00002060 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
cristy0568ffc2011-07-25 16:54:14 +00002061 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002062 fx_info=DestroyFxInfo(fx_info);
anthony2fbb5952012-05-05 12:35:30 +00002063 if( IfMagickTrue(status) )
cristy3ed852e2009-09-05 21:47:34 +00002064 {
2065 char
2066 value[MaxTextExtent];
2067
cristyb51dff52011-05-19 16:55:47 +00002068 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002069 GetMagickPrecision(),(double) alpha);
cristyd15e6592011-10-15 00:13:06 +00002070 (void) SetImageProperty((Image *) image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00002071 }
cristy5eb9fc82011-02-21 15:05:41 +00002072 if (image->properties != (void *) NULL)
2073 {
2074 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2075 image->properties,property);
2076 return(p);
2077 }
cristy3ed852e2009-09-05 21:47:34 +00002078 }
2079 break;
2080 }
2081 case 'I':
2082 case 'i':
2083 {
2084 if (LocaleNCompare("iptc:",property,5) == 0)
2085 {
anthony2fbb5952012-05-05 12:35:30 +00002086 if( IfMagickTrue(GetIPTCProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002087 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002088 {
2089 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2090 image->properties,property);
2091 return(p);
2092 }
2093 }
2094 break;
2095 }
2096 case 'P':
2097 case 'p':
2098 {
2099 if (LocaleNCompare("pixel:",property,6) == 0)
2100 {
cristy4c08aed2011-07-01 19:47:50 +00002101 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002102 pixel;
2103
cristy4c08aed2011-07-01 19:47:50 +00002104 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +00002105 fx_info=AcquireFxInfo(image,property+6,exception);
cristy0568ffc2011-07-25 16:54:14 +00002106 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
2107 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002108 pixel.red=(double) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002109 status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
2110 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002111 pixel.green=(double) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002112 status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
2113 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002114 pixel.blue=(double) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002115 if (image->colorspace == CMYKColorspace)
2116 {
cristy0568ffc2011-07-25 16:54:14 +00002117 status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristy3ed852e2009-09-05 21:47:34 +00002118 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002119 pixel.black=(double) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002120 }
cristy0568ffc2011-07-25 16:54:14 +00002121 status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
2122 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002123 pixel.alpha=(double) QuantumRange*(1.0-alpha);
cristy3ed852e2009-09-05 21:47:34 +00002124 fx_info=DestroyFxInfo(fx_info);
anthony2fbb5952012-05-05 12:35:30 +00002125 if( IfMagickTrue(status) )
cristy3ed852e2009-09-05 21:47:34 +00002126 {
2127 char
2128 name[MaxTextExtent];
2129
cristy269c9412011-10-13 23:41:15 +00002130 (void) QueryColorname(image,&pixel,SVGCompliance,name,
cristy3ed852e2009-09-05 21:47:34 +00002131 exception);
cristyd15e6592011-10-15 00:13:06 +00002132 (void) SetImageProperty((Image *) image,property,name,exception);
2133 return(GetImageProperty(image,property,exception));
cristy3ed852e2009-09-05 21:47:34 +00002134 }
2135 }
2136 break;
2137 }
2138 case 'X':
2139 case 'x':
2140 {
2141 if (LocaleNCompare("xmp:",property,4) == 0)
2142 {
anthony2fbb5952012-05-05 12:35:30 +00002143 if( IfMagickTrue(GetXMPProperty(image,property)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002144 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002145 {
2146 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2147 image->properties,property);
2148 return(p);
2149 }
2150 }
2151 break;
2152 }
cristy5eb9fc82011-02-21 15:05:41 +00002153 default:
2154 break;
cristy3ed852e2009-09-05 21:47:34 +00002155 }
2156 return(p);
2157}
2158
2159/*
2160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2161% %
2162% %
2163% %
2164+ G e t M a g i c k P r o p e r t y %
2165% %
2166% %
2167% %
2168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2169%
anthony06762232012-04-29 11:45:40 +00002170% GetMagickProperty() gets attributes or calculated values that is associated
cristy97fc9f32012-06-13 21:04:44 +00002171% with a fixed known property name, or single letter property.
anthony06762232012-04-29 11:45:40 +00002172%
cristy97fc9f32012-06-13 21:04:44 +00002173% This does not return, special profile or property expressions. Nor does it
2174% return free-form property strings, unless referenced by a single letter
2175% property name.
anthony06762232012-04-29 11:45:40 +00002176%
anthony2cfa1a12012-05-12 05:18:07 +00002177% The returned string is stored as the image artifact 'get-property' (not as
2178% another property), and as such should not be freed. Later calls however
2179% will overwrite this value so if needed for a longer period a copy should be
2180% made. This artifact can be deleted when no longer required.
cristy3ed852e2009-09-05 21:47:34 +00002181%
2182% The format of the GetMagickProperty method is:
2183%
cristyad785752011-07-27 23:13:03 +00002184% const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
cristy97fc9f32012-06-13 21:04:44 +00002185% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002186%
2187% A description of each parameter follows:
2188%
2189% o image_info: the image info.
2190%
2191% o image: the image.
2192%
2193% o key: the key.
2194%
cristyd15e6592011-10-15 00:13:06 +00002195% o exception: return any errors or warnings in this structure.
2196%
cristy3ed852e2009-09-05 21:47:34 +00002197*/
anthony2fbb5952012-05-05 12:35:30 +00002198static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
2199 Image *image,const char letter,ExceptionInfo *exception)
2200{
2201 char
anthony2cfa1a12012-05-12 05:18:07 +00002202 value[MaxTextExtent];
anthony2fbb5952012-05-05 12:35:30 +00002203
anthony2cfa1a12012-05-12 05:18:07 +00002204 const char
2205 *string;
2206
2207 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002208 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2209 *value='\0'; /* formatted string */
2210 string=(char *) NULL; /* constant string reference */
anthony2fbb5952012-05-05 12:35:30 +00002211 switch (letter)
2212 {
anthony2fbb5952012-05-05 12:35:30 +00002213 case 'b': /* image size read in - in bytes */
2214 {
2215 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2216 ((MagickOffsetType) image->extent));
2217 if (image->extent != (MagickSizeType) ((size_t) image->extent))
2218 (void) FormatMagickSize(image->extent,MagickFalse,value);
2219 ConcatenateMagickString(value,"B",MaxTextExtent);
2220 break;
2221 }
anthony7bb7aee2012-05-15 00:09:16 +00002222 case 'c': /* image comment property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002223 {
anthony2cfa1a12012-05-12 05:18:07 +00002224 string=GetImageProperty(image,"comment",exception);
cristyec6934f2012-08-14 18:38:40 +00002225 if (string == (const char *) NULL)
anthony7bb7aee2012-05-15 00:09:16 +00002226 string="";
anthony2fbb5952012-05-05 12:35:30 +00002227 break;
2228 }
2229 case 'd': /* Directory component of filename */
2230 {
2231 GetPathComponent(image->magick_filename,HeadPath,value);
2232 break;
2233 }
2234 case 'e': /* Filename extension (suffix) of image file */
2235 {
2236 GetPathComponent(image->magick_filename,ExtensionPath,value);
2237 break;
2238 }
2239 case 'f': /* Filename without directory component */
2240 {
2241 GetPathComponent(image->magick_filename,TailPath,value);
2242 break;
2243 }
2244 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */
2245 {
2246 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002247 (double) image->page.width,(double) image->page.height,
2248 (double) image->page.x,(double) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002249 break;
2250 }
2251 case 'h': /* Image height (current) */
2252 {
cristyec6934f2012-08-14 18:38:40 +00002253 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2254 (image->rows != 0 ? image->rows : image->magick_rows));
anthony2fbb5952012-05-05 12:35:30 +00002255 break;
2256 }
anthonyc69008c2012-05-07 11:45:32 +00002257 case 'i': /* Filename last used for image (read or write) */
anthony2fbb5952012-05-05 12:35:30 +00002258 {
anthony2cfa1a12012-05-12 05:18:07 +00002259 string=image->filename;
anthony2fbb5952012-05-05 12:35:30 +00002260 break;
2261 }
2262 case 'k': /* Number of unique colors */
2263 {
cristyec6934f2012-08-14 18:38:40 +00002264 /*
2265 FUTURE: ensure this does not generate the formatted comment!
2266 */
anthony2fbb5952012-05-05 12:35:30 +00002267 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002268 GetNumberColors(image,(FILE *) NULL,exception));
anthony2fbb5952012-05-05 12:35:30 +00002269 break;
2270 }
cristy97fc9f32012-06-13 21:04:44 +00002271 case 'l': /* Image label property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002272 {
anthony2cfa1a12012-05-12 05:18:07 +00002273 string=GetImageProperty(image,"label",exception);
anthony7bb7aee2012-05-15 00:09:16 +00002274 if ( string == (const char *)NULL)
2275 string="";
anthony2fbb5952012-05-05 12:35:30 +00002276 break;
2277 }
2278 case 'm': /* Image format (file magick) */
2279 {
anthony2cfa1a12012-05-12 05:18:07 +00002280 string=image->magick;
anthony2fbb5952012-05-05 12:35:30 +00002281 break;
2282 }
2283 case 'n': /* Number of images in the list. */
2284 {
2285 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002286 GetImageListLength(image));
anthony2fbb5952012-05-05 12:35:30 +00002287 break;
2288 }
2289 case 'o': /* Output Filename - for delegate use only */
2290 {
anthony2cfa1a12012-05-12 05:18:07 +00002291 string=image_info->filename;
anthony2fbb5952012-05-05 12:35:30 +00002292 break;
2293 }
2294 case 'p': /* Image index in current image list -- As 'n' OBSOLETE */
2295 {
2296 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002297 GetImageIndexInList(image));
anthony2fbb5952012-05-05 12:35:30 +00002298 break;
2299 }
2300 case 'q': /* Quantum depth of image in memory */
2301 {
2302 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2303 MAGICKCORE_QUANTUM_DEPTH);
2304 break;
2305 }
2306 case 'r': /* Image storage class and colorspace. */
2307 {
2308 ColorspaceType
2309 colorspace;
2310
2311 colorspace=image->colorspace;
2312 if (IfMagickTrue(IsImageGray(image,exception)))
2313 colorspace=GRAYColorspace;
2314 (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s",
cristy68064d72012-06-13 20:56:14 +00002315 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
anthony2fbb5952012-05-05 12:35:30 +00002316 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
cristy8a46d822012-08-28 23:32:39 +00002317 image->alpha_trait == BlendPixelTrait ? "Matte" : "");
anthony2fbb5952012-05-05 12:35:30 +00002318 break;
2319 }
2320 case 's': /* Image scene number */
2321 {
2322 if (image_info->number_scenes != 0)
2323 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002324 image_info->scene);
anthony2fbb5952012-05-05 12:35:30 +00002325 else
2326 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002327 image->scene);
anthony2fbb5952012-05-05 12:35:30 +00002328 break;
2329 }
2330 case 't': /* Base filename without directory or extention */
2331 {
2332 GetPathComponent(image->magick_filename,BasePath,value);
2333 break;
2334 }
2335 case 'u': /* Unique filename */
2336 {
anthony2cfa1a12012-05-12 05:18:07 +00002337 string=image_info->unique;
anthony2fbb5952012-05-05 12:35:30 +00002338 break;
2339 }
2340 case 'w': /* Image width (current) */
2341 {
2342 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002343 (image->columns != 0 ? image->columns : image->magick_columns));
anthony2fbb5952012-05-05 12:35:30 +00002344 break;
2345 }
anthonyc69008c2012-05-07 11:45:32 +00002346 case 'x': /* Image horizontal resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002347 {
anthonyc69008c2012-05-07 11:45:32 +00002348 (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
cristy68064d72012-06-13 20:56:14 +00002349 image->resolution.x,CommandOptionToMnemonic(
2350 MagickResolutionOptions,(ssize_t)image->units));
anthony2fbb5952012-05-05 12:35:30 +00002351 break;
2352 }
anthonyc69008c2012-05-07 11:45:32 +00002353 case 'y': /* Image vertical resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002354 {
anthonyc69008c2012-05-07 11:45:32 +00002355 (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
cristy68064d72012-06-13 20:56:14 +00002356 image->resolution.y,CommandOptionToMnemonic(MagickResolutionOptions,
2357 (ssize_t) image->units));
anthony2fbb5952012-05-05 12:35:30 +00002358 break;
2359 }
2360 case 'z': /* Image depth as read in */
2361 {
2362 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002363 image->depth);
anthony2fbb5952012-05-05 12:35:30 +00002364 break;
2365 }
2366 case 'A': /* Image alpha channel */
2367 {
2368 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy8a46d822012-08-28 23:32:39 +00002369 CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t) image->alpha_trait));
anthony2fbb5952012-05-05 12:35:30 +00002370 break;
2371 }
2372 case 'C': /* Image compression method. */
2373 {
2374 (void) FormatLocaleString(value,MaxTextExtent,"%s",
2375 CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
2376 image->compression));
2377 break;
2378 }
2379 case 'D': /* Image dispose method. */
2380 {
2381 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy68064d72012-06-13 20:56:14 +00002382 CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) image->dispose));
anthony2fbb5952012-05-05 12:35:30 +00002383 break;
2384 }
2385 case 'G': /* Image size as geometry = "%wx%h" */
2386 {
2387 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002388 (double)image->magick_columns,(double) image->magick_rows);
anthony2fbb5952012-05-05 12:35:30 +00002389 break;
2390 }
2391 case 'H': /* layer canvas height */
2392 {
2393 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002394 image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002395 break;
2396 }
2397 case 'M': /* Magick filename - filename given incl. coder & read mods */
2398 {
anthony2cfa1a12012-05-12 05:18:07 +00002399 string=image->magick_filename;
anthony2fbb5952012-05-05 12:35:30 +00002400 break;
2401 }
2402 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2403 {
cristy68064d72012-06-13 20:56:14 +00002404 (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long)
2405 image->page.x,(long) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002406 break;
2407 }
2408 case 'P': /* layer canvas page size = "%Wx%H" */
2409 {
2410 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002411 (double) image->page.width,(double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002412 break;
2413 }
2414 case 'Q': /* image compression quality */
2415 {
2416 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002417 image->quality);
anthony2fbb5952012-05-05 12:35:30 +00002418 break;
2419 }
2420 case 'S': /* Image scenes ???? */
2421 {
2422 if (image_info->number_scenes == 0)
anthony2cfa1a12012-05-12 05:18:07 +00002423 string="2147483647";
anthony2fbb5952012-05-05 12:35:30 +00002424 else
2425 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002426 image_info->scene+image_info->number_scenes);
anthony2fbb5952012-05-05 12:35:30 +00002427 break;
2428 }
2429 case 'T': /* image time delay for animations */
2430 {
2431 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002432 image->delay);
anthony2fbb5952012-05-05 12:35:30 +00002433 break;
2434 }
2435 case 'W': /* layer canvas width */
2436 {
2437 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002438 image->page.width);
anthony2fbb5952012-05-05 12:35:30 +00002439 break;
2440 }
2441 case 'X': /* layer canvas X offset */
2442 {
cristy68064d72012-06-13 20:56:14 +00002443 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2444 image->page.x);
anthony2fbb5952012-05-05 12:35:30 +00002445 break;
2446 }
2447 case 'Y': /* layer canvas Y offset */
2448 {
cristy68064d72012-06-13 20:56:14 +00002449 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2450 image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002451 break;
2452 }
anthony6e2e0732012-05-06 12:22:43 +00002453 case 'Z': /* Zero filename ??? */
anthony2fbb5952012-05-05 12:35:30 +00002454 {
anthony2cfa1a12012-05-12 05:18:07 +00002455 string=image_info->zero;
anthony2fbb5952012-05-05 12:35:30 +00002456 break;
2457 }
2458 case '@': /* Trim bounding box, without Trimming! */
2459 {
2460 RectangleInfo
2461 page;
2462
2463 page=GetImageBoundingBox(image,exception);
2464 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002465 (double) page.width,(double) page.height,(double) page.x,(double)
2466 page.y);
anthony2fbb5952012-05-05 12:35:30 +00002467 break;
2468 }
2469 case '#': /* Image signature */
2470 {
2471 (void) SignatureImage(image,exception);
anthony2cfa1a12012-05-12 05:18:07 +00002472 string=GetImageProperty(image,"signature",exception);
anthony2fbb5952012-05-05 12:35:30 +00002473 break;
2474 }
2475 case '%': /* percent escaped */
2476 {
anthony2cfa1a12012-05-12 05:18:07 +00002477 string="%";
anthony2fbb5952012-05-05 12:35:30 +00002478 break;
2479 }
2480 }
2481 if (*value != '\0')
anthony2cfa1a12012-05-12 05:18:07 +00002482 string=value;
cristy68064d72012-06-13 20:56:14 +00002483 if (string != (char *) NULL)
2484 {
cristyec6934f2012-08-14 18:38:40 +00002485 (void) SetImageArtifact(image,"get-property",string);
2486 return(GetImageArtifact(image,"get-property"));
cristy68064d72012-06-13 20:56:14 +00002487 }
anthony2cfa1a12012-05-12 05:18:07 +00002488 return((char *)NULL);
anthony2fbb5952012-05-05 12:35:30 +00002489}
2490
cristy3ed852e2009-09-05 21:47:34 +00002491MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002492 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002493{
2494 char
anthony003f8992012-05-11 12:50:07 +00002495 value[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00002496
anthony2cfa1a12012-05-12 05:18:07 +00002497 const char
2498 *string;
2499
anthony2fbb5952012-05-05 12:35:30 +00002500 assert(property[0] != '\0');
cristy68064d72012-06-13 20:56:14 +00002501 if (property[1] == '\0') /* single letter property request */
2502 return(GetMagickPropertyLetter(image_info,image,*property,exception));
2503 if ((image != (Image *) NULL) && IfMagickTrue(image->debug))
2504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2505 *value='\0'; /* formated string */
2506 string=(char *) NULL; /* constant string reference */
cristyec6897c2010-05-12 00:43:15 +00002507 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002508 {
2509 case 'b':
2510 {
anthony2cfa1a12012-05-12 05:18:07 +00002511 if ((LocaleCompare("base",property) == 0) ||
2512 (LocaleCompare("basename",property) == 0) )
cristy3ed852e2009-09-05 21:47:34 +00002513 {
anthony003f8992012-05-11 12:50:07 +00002514 GetPathComponent(image->magick_filename,BasePath,value);
cristy3ed852e2009-09-05 21:47:34 +00002515 break;
2516 }
2517 break;
2518 }
2519 case 'c':
2520 {
anthony2cfa1a12012-05-12 05:18:07 +00002521 if (LocaleCompare("channels",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002522 {
anthony7bb7aee2012-05-15 00:09:16 +00002523 /* FUTURE: return actual image channels */
cristyb51dff52011-05-19 16:55:47 +00002524 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002525 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002526 image->colorspace));
2527 LocaleLower(value);
cristy8a46d822012-08-28 23:32:39 +00002528 if( image->alpha_trait == BlendPixelTrait )
cristy3ed852e2009-09-05 21:47:34 +00002529 (void) ConcatenateMagickString(value,"a",MaxTextExtent);
2530 break;
2531 }
anthony2cfa1a12012-05-12 05:18:07 +00002532 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002533 {
2534 ColorspaceType
2535 colorspace;
2536
anthony7bb7aee2012-05-15 00:09:16 +00002537 /* FUTURE: return actual colorspace - no 'gray' stuff */
cristy3ed852e2009-09-05 21:47:34 +00002538 colorspace=image->colorspace;
anthony2fbb5952012-05-05 12:35:30 +00002539 if( IfMagickTrue(IsImageGray(image,exception)) )
cristy3ed852e2009-09-05 21:47:34 +00002540 colorspace=GRAYColorspace;
anthony2cfa1a12012-05-12 05:18:07 +00002541 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
2542 colorspace);
cristy3ed852e2009-09-05 21:47:34 +00002543 break;
2544 }
anthony2cfa1a12012-05-12 05:18:07 +00002545 if (LocaleCompare("copyright",property) == 0)
cristy63054742010-09-20 12:42:28 +00002546 {
2547 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
2548 break;
2549 }
cristy3ed852e2009-09-05 21:47:34 +00002550 break;
2551 }
2552 case 'd':
2553 {
anthony2cfa1a12012-05-12 05:18:07 +00002554 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002555 {
cristyb51dff52011-05-19 16:55:47 +00002556 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002557 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002558 break;
2559 }
anthony2cfa1a12012-05-12 05:18:07 +00002560 if (LocaleCompare("directory",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002561 {
anthony003f8992012-05-11 12:50:07 +00002562 GetPathComponent(image->magick_filename,HeadPath,value);
cristy3ed852e2009-09-05 21:47:34 +00002563 break;
2564 }
2565 break;
2566 }
2567 case 'e':
2568 {
anthony2cfa1a12012-05-12 05:18:07 +00002569 if (LocaleCompare("extension",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002570 {
anthony003f8992012-05-11 12:50:07 +00002571 GetPathComponent(image->magick_filename,ExtensionPath,value);
cristy3ed852e2009-09-05 21:47:34 +00002572 break;
2573 }
2574 break;
2575 }
2576 case 'g':
2577 {
anthony2f7f9ca2012-05-14 06:33:53 +00002578 if (LocaleCompare("gamma",property) == 0)
2579 {
2580 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
2581 GetMagickPrecision(),image->gamma);
2582 break;
2583 }
anthony003f8992012-05-11 12:50:07 +00002584 if ( (image_info != (ImageInfo *) NULL) &&
anthony2cfa1a12012-05-12 05:18:07 +00002585 (LocaleCompare("group",property) == 0) )
cristy3ed852e2009-09-05 21:47:34 +00002586 {
cristy68064d72012-06-13 20:56:14 +00002587 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
2588 image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002589 break;
2590 }
2591 break;
2592 }
2593 case 'h':
2594 {
anthony2cfa1a12012-05-12 05:18:07 +00002595 if (LocaleCompare("height",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002596 {
cristyb51dff52011-05-19 16:55:47 +00002597 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002598 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002599 break;
2600 }
2601 break;
2602 }
2603 case 'i':
2604 {
anthony2cfa1a12012-05-12 05:18:07 +00002605 if (LocaleCompare("input",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002606 {
anthony2cfa1a12012-05-12 05:18:07 +00002607 string=image->filename;
cristy3ed852e2009-09-05 21:47:34 +00002608 break;
2609 }
2610 break;
2611 }
2612 case 'k':
2613 {
anthony2cfa1a12012-05-12 05:18:07 +00002614 if (LocaleCompare("kurtosis",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002615 {
2616 double
2617 kurtosis,
2618 skewness;
2619
cristyc82a27b2011-10-21 01:07:16 +00002620 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002621 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002622 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002623 break;
2624 }
2625 break;
2626 }
2627 case 'm':
2628 {
anthony2cfa1a12012-05-12 05:18:07 +00002629 if (LocaleCompare("magick",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002630 {
anthony2cfa1a12012-05-12 05:18:07 +00002631 string=image->magick;
cristy3ed852e2009-09-05 21:47:34 +00002632 break;
2633 }
anthony2cfa1a12012-05-12 05:18:07 +00002634 if (LocaleCompare("max",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002635 {
2636 double
2637 maximum,
2638 minimum;
2639
cristyc82a27b2011-10-21 01:07:16 +00002640 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002641 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002642 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002643 break;
2644 }
anthony2cfa1a12012-05-12 05:18:07 +00002645 if (LocaleCompare("mean",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002646 {
2647 double
2648 mean,
2649 standard_deviation;
2650
cristy68064d72012-06-13 20:56:14 +00002651 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00002652 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002653 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002654 break;
2655 }
anthony2cfa1a12012-05-12 05:18:07 +00002656 if (LocaleCompare("min",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002657 {
2658 double
2659 maximum,
2660 minimum;
2661
cristyc82a27b2011-10-21 01:07:16 +00002662 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002663 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002664 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002665 break;
2666 }
2667 break;
2668 }
cristy3ed852e2009-09-05 21:47:34 +00002669 case 'o':
2670 {
anthony2cfa1a12012-05-12 05:18:07 +00002671 if (LocaleCompare("opaque",property) == 0)
cristyb0094252011-03-26 12:59:45 +00002672 {
2673 MagickBooleanType
2674 opaque;
2675
cristyc82a27b2011-10-21 01:07:16 +00002676 opaque=IsImageOpaque(image,exception);
anthony2fbb5952012-05-05 12:35:30 +00002677 (void) CopyMagickString(value,IfMagickTrue(opaque)?"true":"false",
2678 MaxTextExtent);
cristyb0094252011-03-26 12:59:45 +00002679 break;
2680 }
anthony2cfa1a12012-05-12 05:18:07 +00002681 if (LocaleCompare("orientation",property) == 0)
cristy42aa06c2012-04-01 14:05:10 +00002682 {
anthony2cfa1a12012-05-12 05:18:07 +00002683 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
2684 image->orientation);
cristy42aa06c2012-04-01 14:05:10 +00002685 break;
2686 }
cristy68064d72012-06-13 20:56:14 +00002687 if ((image_info != (ImageInfo *) NULL) &&
2688 (LocaleCompare("output",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002689 {
2690 (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
2691 break;
2692 }
2693 break;
2694 }
2695 case 'p':
2696 {
anthony2cfa1a12012-05-12 05:18:07 +00002697 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002698 {
cristyb51dff52011-05-19 16:55:47 +00002699 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyad785752011-07-27 23:13:03 +00002700 GetImageIndexInList(image)+1);
cristy3ed852e2009-09-05 21:47:34 +00002701 break;
2702 }
2703 break;
2704 }
cristy946b1032012-04-01 14:19:07 +00002705 case 'r':
2706 {
anthony2eb9a422012-05-15 02:28:02 +00002707 /* This matches %[fx:resolution.x] */
anthony2cfa1a12012-05-12 05:18:07 +00002708 if (LocaleCompare("resolution.x",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002709 {
2710 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2711 image->resolution.x);
2712 break;
2713 }
anthony2eb9a422012-05-15 02:28:02 +00002714 /* This matches %[fx:resolution.y] */
anthony2cfa1a12012-05-12 05:18:07 +00002715 if (LocaleCompare("resolution.y",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002716 {
2717 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2718 image->resolution.y);
2719 break;
2720 }
2721 break;
2722 }
cristy3ed852e2009-09-05 21:47:34 +00002723 case 's':
2724 {
anthony2cfa1a12012-05-12 05:18:07 +00002725 if (LocaleCompare("scene",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002726 {
cristy68064d72012-06-13 20:56:14 +00002727 if ((image_info != (ImageInfo *) NULL) &&
2728 (image_info->number_scenes != 0))
cristyb51dff52011-05-19 16:55:47 +00002729 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002730 image_info->scene);
anthony90ebc0d2012-04-28 08:10:02 +00002731 else
2732 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002733 image->scene);
cristy3ed852e2009-09-05 21:47:34 +00002734 break;
2735 }
anthony2cfa1a12012-05-12 05:18:07 +00002736 if (LocaleCompare("scenes",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002737 {
2738 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2739 GetImageListLength(image));
2740 break;
2741 }
anthony2cfa1a12012-05-12 05:18:07 +00002742 if (LocaleCompare("size",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002743 {
2744 char
2745 format[MaxTextExtent];
2746
2747 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
2748 (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
2749 break;
2750 }
anthony2cfa1a12012-05-12 05:18:07 +00002751 if (LocaleCompare("skewness",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002752 {
2753 double
2754 kurtosis,
2755 skewness;
2756
cristyc82a27b2011-10-21 01:07:16 +00002757 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002758 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002759 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002760 break;
2761 }
anthony2cfa1a12012-05-12 05:18:07 +00002762 if (LocaleCompare("standard-deviation",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002763 {
2764 double
2765 mean,
2766 standard_deviation;
2767
cristyd42d9952011-07-08 14:21:50 +00002768 (void) GetImageMean(image,&mean,&standard_deviation,
cristyc82a27b2011-10-21 01:07:16 +00002769 exception);
cristyb51dff52011-05-19 16:55:47 +00002770 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002771 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00002772 break;
2773 }
2774 break;
2775 }
cristy623aaec2012-06-21 00:49:08 +00002776 case 't':
2777 {
2778 if (LocaleCompare("type",property) == 0)
2779 {
2780 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
2781 GetImageType(image,exception));
2782 break;
2783 }
2784 break;
2785 }
cristy3ed852e2009-09-05 21:47:34 +00002786 case 'u':
2787 {
cristy68064d72012-06-13 20:56:14 +00002788 if ((image_info != (ImageInfo *) NULL) &&
2789 (LocaleCompare("unique",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002790 {
anthony2cfa1a12012-05-12 05:18:07 +00002791 string=image_info->unique;
cristy3ed852e2009-09-05 21:47:34 +00002792 break;
2793 }
2794 break;
2795 }
cristy63054742010-09-20 12:42:28 +00002796 case 'v':
2797 {
anthony2cfa1a12012-05-12 05:18:07 +00002798 if (LocaleCompare("version",property) == 0)
cristy63054742010-09-20 12:42:28 +00002799 {
anthony2cfa1a12012-05-12 05:18:07 +00002800 string=GetMagickVersion((size_t *) NULL);
cristy63054742010-09-20 12:42:28 +00002801 break;
2802 }
2803 break;
2804 }
cristy3ed852e2009-09-05 21:47:34 +00002805 case 'w':
2806 {
anthony2cfa1a12012-05-12 05:18:07 +00002807 if (LocaleCompare("width",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002808 {
cristyb51dff52011-05-19 16:55:47 +00002809 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002810 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00002811 break;
2812 }
2813 break;
2814 }
anthony2eb9a422012-05-15 02:28:02 +00002815 case 'x': /* FUTURE: Obsolete X resolution */
anthony90ebc0d2012-04-28 08:10:02 +00002816 {
anthony2cfa1a12012-05-12 05:18:07 +00002817 if ((LocaleCompare("xresolution",property) == 0) ||
2818 (LocaleCompare("x-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00002819 {
anthonyc69008c2012-05-07 11:45:32 +00002820 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00002821 image->resolution.x);
anthony90ebc0d2012-04-28 08:10:02 +00002822 break;
2823 }
2824 break;
2825 }
anthony2eb9a422012-05-15 02:28:02 +00002826 case 'y': /* FUTURE: Obsolete Y resolution */
anthony90ebc0d2012-04-28 08:10:02 +00002827 {
anthony2cfa1a12012-05-12 05:18:07 +00002828 if ((LocaleCompare("yresolution",property) == 0) ||
2829 (LocaleCompare("y-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00002830 {
anthonyc69008c2012-05-07 11:45:32 +00002831 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00002832 image->resolution.y);
anthony90ebc0d2012-04-28 08:10:02 +00002833 break;
2834 }
2835 break;
2836 }
cristy3ed852e2009-09-05 21:47:34 +00002837 case 'z':
2838 {
cristy68064d72012-06-13 20:56:14 +00002839 if ((image_info != (ImageInfo *) NULL) &&
2840 (LocaleCompare("zero",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002841 {
anthony2cfa1a12012-05-12 05:18:07 +00002842 string=image_info->zero;
cristy3ed852e2009-09-05 21:47:34 +00002843 break;
2844 }
2845 break;
2846 }
2847 }
anthony2cfa1a12012-05-12 05:18:07 +00002848 if (*value != '\0')
2849 string=value;
cristy68064d72012-06-13 20:56:14 +00002850 if (string != (char *)NULL)
2851 {
2852 (void) SetImageArtifact(image,"get-property", string);
2853 return(GetImageArtifact(image,"get-property"));
2854 }
anthony2cfa1a12012-05-12 05:18:07 +00002855 return((char *)NULL);
cristy3ed852e2009-09-05 21:47:34 +00002856}
2857
2858/*
2859%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2860% %
2861% %
2862% %
2863% G e t N e x t I m a g e P r o p e r t y %
2864% %
2865% %
2866% %
2867%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2868%
cristy97fc9f32012-06-13 21:04:44 +00002869% GetNextImageProperty() gets the next free-form string property name.
cristy3ed852e2009-09-05 21:47:34 +00002870%
2871% The format of the GetNextImageProperty method is:
2872%
2873% char *GetNextImageProperty(const Image *image)
2874%
2875% A description of each parameter follows:
2876%
2877% o image: the image.
2878%
2879*/
2880MagickExport char *GetNextImageProperty(const Image *image)
2881{
2882 assert(image != (Image *) NULL);
2883 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00002884 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00002885 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2886 image->filename);
2887 if (image->properties == (void *) NULL)
2888 return((char *) NULL);
2889 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
2890}
2891
2892/*
2893%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2894% %
2895% %
2896% %
2897% I n t e r p r e t I m a g e P r o p e r t i e s %
2898% %
2899% %
2900% %
2901%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2902%
2903% InterpretImageProperties() replaces any embedded formatting characters with
anthony06762232012-04-29 11:45:40 +00002904% the appropriate image property and returns the interpreted text.
2905%
2906% This searches for and replaces
anthony003f8992012-05-11 12:50:07 +00002907% \n \r \% replaced by newline, return, and percent resp.
2908% &lt; &gt; &amp; replaced by '<', '>', '&' resp.
2909% %% replaced by percent
anthony06762232012-04-29 11:45:40 +00002910%
anthonyd2f39e02012-08-20 12:29:28 +00002911% %x %[x] where 'x' is a single letter properity, case sensitive).
2912% %[type:name] where 'type' a is special and known prefix.
anthony003f8992012-05-11 12:50:07 +00002913% %[name] where 'name' is a specifically known attribute, calculated
cristy97fc9f32012-06-13 21:04:44 +00002914% value, or a per-image property string name, or a per-image
anthonyd2f39e02012-08-20 12:29:28 +00002915% 'artifact' (as generated from a global option).
2916% It may contain ':' as long as the prefix is not special.
anthony06762232012-04-29 11:45:40 +00002917%
anthonyd2f39e02012-08-20 12:29:28 +00002918% Single letter % substitutions will only happen if the character before the
2919% percent is NOT a number. But braced substitutions will always be performed.
2920% This prevents the typical usage of percent in a interpreted geometry
2921% argument from being substituted when the percent is a geometry flag.
anthony003f8992012-05-11 12:50:07 +00002922%
2923% If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
2924% used as a search pattern to print multiple lines of "name=value\n" pairs of
2925% the associacted set of properities.
2926%
anthonyd2f39e02012-08-20 12:29:28 +00002927% The returned string must be freed using DestoryString() by the caller.
anthonyf68116b2012-04-14 12:54:41 +00002928%
cristy3ed852e2009-09-05 21:47:34 +00002929% The format of the InterpretImageProperties method is:
2930%
anthony003f8992012-05-11 12:50:07 +00002931% char *InterpretImageProperties(const ImageInfo *image_info,
2932% Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002933%
2934% A description of each parameter follows:
2935%
2936% o image_info: the image info.
2937%
2938% o image: the image.
2939%
2940% o embed_text: the address of a character string containing the embedded
2941% formatting characters.
2942%
cristy018f07f2011-09-04 21:15:19 +00002943% o exception: return any errors or warnings in this structure.
2944%
cristy3ed852e2009-09-05 21:47:34 +00002945*/
2946MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00002947 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002948{
2949 char
anthony003f8992012-05-11 12:50:07 +00002950 *interpret_text;
cristy3ed852e2009-09-05 21:47:34 +00002951
cristy3ed852e2009-09-05 21:47:34 +00002952 register char
2953 *q;
2954
2955 register const char
2956 *p;
2957
cristy3ed852e2009-09-05 21:47:34 +00002958 size_t
2959 extent,
2960 length;
2961
anthony003f8992012-05-11 12:50:07 +00002962 MagickBooleanType
2963 number;
2964
cristy3ed852e2009-09-05 21:47:34 +00002965 assert(image != (Image *) NULL);
2966 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00002967 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00002968 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthony2fbb5952012-05-05 12:35:30 +00002969
anthony964d28e2012-05-17 23:39:46 +00002970 if ((embed_text == (const char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002971 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00002972 p=embed_text;
anthony2fbb5952012-05-05 12:35:30 +00002973
anthony964d28e2012-05-17 23:39:46 +00002974 if (*p == '\0')
2975 return(ConstantString(""));
2976
anthony2fbb5952012-05-05 12:35:30 +00002977 /* handle a '@' replace string from file */
anthony003f8992012-05-11 12:50:07 +00002978 if (*p == '@') {
2979 p++;
2980 if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
2981 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00002982 OptionError,"UnableToAccessPath","%s",p);
anthony003f8992012-05-11 12:50:07 +00002983 return((char *) NULL);
2984 }
2985 return(FileToString(p,~0,exception));
2986 }
2987
cristy3ed852e2009-09-05 21:47:34 +00002988 /*
2989 Translate any embedded format characters.
2990 */
anthony003f8992012-05-11 12:50:07 +00002991 interpret_text=AcquireString(embed_text); /* new string with extra space */
2992 extent=MaxTextExtent; /* how many extra space */
2993 number=MagickFalse; /* is last char a number? */
2994 for (q=interpret_text; *p!='\0'; number=IsMagickTrue(isdigit(*p)),p++)
cristy3ed852e2009-09-05 21:47:34 +00002995 {
2996 *q='\0';
2997 if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
2998 {
2999 extent+=MaxTextExtent;
3000 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
3001 MaxTextExtent+1,sizeof(*interpret_text));
3002 if (interpret_text == (char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003003 return((char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003004 q=interpret_text+strlen(interpret_text);
3005 }
3006 /*
anthonyd2f39e02012-08-20 12:29:28 +00003007 Look for the various escapes, (and handle other specials)
cristy3ed852e2009-09-05 21:47:34 +00003008 */
anthony003f8992012-05-11 12:50:07 +00003009 switch (*p) {
3010 case '\\':
3011 switch (*(p+1)) {
3012 case '\0':
3013 continue;
3014 case 'r': /* convert to RETURN */
3015 *q++='\r';
3016 p++;
3017 continue;
3018 case 'n': /* convert to NEWLINE */
3019 *q++='\n';
3020 p++;
3021 continue;
3022 case '\n': /* EOL removal UNIX,MacOSX */
3023 p++;
3024 continue;
3025 case '\r': /* EOL removal DOS,Windows */
3026 p++;
3027 if (*p == '\n') /* return-newline EOL */
3028 p++;
3029 continue;
3030 default:
3031 p++;
3032 *q++=(*p);
3033 continue;
3034 }
3035 continue; /* never reached! */
3036 case '&':
anthony104f8932012-05-13 01:54:53 +00003037 if (LocaleNCompare("&lt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003038 *q++='<', p+=3;
anthony104f8932012-05-13 01:54:53 +00003039 else if (LocaleNCompare("&gt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003040 *q++='>', p+=3;
anthony104f8932012-05-13 01:54:53 +00003041 else if (LocaleNCompare("&amp;",p,5) == 0)
anthony003f8992012-05-11 12:50:07 +00003042 *q++='&', p+=4;
3043 else
3044 *q++=(*p);
cristy3ed852e2009-09-05 21:47:34 +00003045 continue;
anthony003f8992012-05-11 12:50:07 +00003046 case '%':
3047 break; /* continue to next set of handlers */
3048 default:
3049 *q++=(*p); /* any thing else is 'as normal' */
cristy3ed852e2009-09-05 21:47:34 +00003050 continue;
anthony003f8992012-05-11 12:50:07 +00003051 }
anthony104f8932012-05-13 01:54:53 +00003052 p++; /* advance beyond the percent */
anthony2fbb5952012-05-05 12:35:30 +00003053
anthony003f8992012-05-11 12:50:07 +00003054 /*
anthony2ec9bd92012-05-20 05:43:24 +00003055 Doubled Percent - or percent at end of string
anthony003f8992012-05-11 12:50:07 +00003056 */
anthony2ec9bd92012-05-20 05:43:24 +00003057 if ( *p == '\0' )
3058 p--;
anthony104f8932012-05-13 01:54:53 +00003059 if ( *p == '%' ) {
anthony89075d02012-05-18 03:40:40 +00003060 *q++='%';
anthony104f8932012-05-13 01:54:53 +00003061 continue;
3062 }
anthony003f8992012-05-11 12:50:07 +00003063
3064 /*
anthonyd2f39e02012-08-20 12:29:28 +00003065 Single letter escapes %c
anthony003f8992012-05-11 12:50:07 +00003066 */
anthony104f8932012-05-13 01:54:53 +00003067 if ( *p != '[' ) {
anthony003f8992012-05-11 12:50:07 +00003068 const char
3069 *value;
3070
3071 /* But only if not preceeded by a number! */
3072 if ( IfMagickTrue(number) ) {
anthony104f8932012-05-13 01:54:53 +00003073 *q++='%'; /* do NOT substitute the percent */
3074 p--; /* back up one */
anthony003f8992012-05-11 12:50:07 +00003075 continue;
3076 }
anthony2fbb5952012-05-05 12:35:30 +00003077 value=GetMagickPropertyLetter(image_info,image,*p, exception);
3078 if (value != (char *) NULL) {
3079 length=strlen(value);
3080 if ((size_t) (q-interpret_text+length+1) >= extent)
3081 {
3082 extent+=length;
3083 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
3084 extent+MaxTextExtent,sizeof(*interpret_text));
anthony104f8932012-05-13 01:54:53 +00003085 if (interpret_text == (char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003086 return((char *)NULL);
anthony2fbb5952012-05-05 12:35:30 +00003087 q=interpret_text+strlen(interpret_text);
3088 }
3089 (void) CopyMagickString(q,value,extent);
3090 q+=length;
anthony2cfa1a12012-05-12 05:18:07 +00003091 continue;
anthony2fbb5952012-05-05 12:35:30 +00003092 }
anthony2cfa1a12012-05-12 05:18:07 +00003093 (void) ThrowMagickException(exception,GetMagickModule(),
3094 OptionWarning,"UnknownImageProperty","\"%%%c\"",*p);
anthony003f8992012-05-11 12:50:07 +00003095 continue;
anthony2fbb5952012-05-05 12:35:30 +00003096 }
anthony2fbb5952012-05-05 12:35:30 +00003097
anthony003f8992012-05-11 12:50:07 +00003098 /*
anthonyd2f39e02012-08-20 12:29:28 +00003099 Braced Percent Escape %[...]
anthony003f8992012-05-11 12:50:07 +00003100 */
3101 {
3102 char
3103 pattern[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00003104
anthony003f8992012-05-11 12:50:07 +00003105 const char
3106 *key,
3107 *value;
cristy3ed852e2009-09-05 21:47:34 +00003108
anthony003f8992012-05-11 12:50:07 +00003109 register ssize_t
3110 len;
cristy3ed852e2009-09-05 21:47:34 +00003111
anthony003f8992012-05-11 12:50:07 +00003112 ssize_t
3113 depth;
3114
3115 /* get the string framed by the %[...] */
anthony104f8932012-05-13 01:54:53 +00003116 p++; /* advance p to just inside the opening brace */
anthony003f8992012-05-11 12:50:07 +00003117 depth=1;
3118 if ( *p == ']' ) {
3119 (void) ThrowMagickException(exception,GetMagickModule(),
3120 OptionWarning,"UnknownImageProperty","\"%%[]\"");
3121 break;
3122 }
3123 for (len=0; len<(MaxTextExtent-1L) && (*p != '\0');)
3124 {
3125 /* skip escaped braces within braced pattern */
3126 if ( (*p == '\\') && (*(p+1) != '\0') ) {
3127 pattern[len++]=(*p++);
3128 pattern[len++]=(*p++);
3129 continue;
3130 }
3131 if (*p == '[')
3132 depth++;
3133 if (*p == ']')
3134 depth--;
3135 if (depth <= 0)
3136 break;
3137 pattern[len++]=(*p++);
3138 }
3139 pattern[len]='\0';
3140 /* Check for unmatched final ']' for "%[...]" */
3141 if ( depth != 0 ) {
3142 if (len >= 64) { /* truncate string for error message */
3143 pattern[61] = '.';
3144 pattern[62] = '.';
3145 pattern[63] = '.';
3146 pattern[64] = '\0';
3147 }
3148 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003149 OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
3150 interpret_text=DestroyString(interpret_text);
3151 return((char *)NULL);
anthony003f8992012-05-11 12:50:07 +00003152 }
3153
3154 /*
anthony104f8932012-05-13 01:54:53 +00003155 Special Property Prefixes
anthony003f8992012-05-11 12:50:07 +00003156 such as: %[exif:...] %[fx:...] %[pixel:...]
cristy97fc9f32012-06-13 21:04:44 +00003157 Otherwise a free-form property string
anthony003f8992012-05-11 12:50:07 +00003158 */
3159 value=GetImageProperty(image,pattern,exception);
3160 if (value != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003161 {
anthony003f8992012-05-11 12:50:07 +00003162 length=strlen(value);
3163 if ((size_t) (q-interpret_text+length+1) >= extent)
cristy3ed852e2009-09-05 21:47:34 +00003164 {
anthony003f8992012-05-11 12:50:07 +00003165 extent+=length;
3166 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
3167 extent+MaxTextExtent,sizeof(*interpret_text));
3168 if (interpret_text == (char *) NULL)
3169 return((char *)NULL);
3170 q=interpret_text+strlen(interpret_text);
cristy3ed852e2009-09-05 21:47:34 +00003171 }
anthony003f8992012-05-11 12:50:07 +00003172 (void) CopyMagickString(q,value,extent);
3173 q+=length;
3174 continue;
3175 }
3176 /*
cristy97fc9f32012-06-13 21:04:44 +00003177 Handle property 'glob' patterns
anthony003f8992012-05-11 12:50:07 +00003178 Such as: %[*] %[user:array_??] %[filename:e*]
3179 */
3180 if( IfMagickTrue(IsGlob(pattern)) )
3181 {
3182 ResetImagePropertyIterator(image);
3183 key=GetNextImageProperty(image);
3184 while (key != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003185 {
anthony003f8992012-05-11 12:50:07 +00003186 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
cristy3ed852e2009-09-05 21:47:34 +00003187 {
anthony003f8992012-05-11 12:50:07 +00003188 value=GetImageProperty(image,key,exception);
3189 if (value != (const char *) NULL)
3190 {
3191 length=strlen(key)+strlen(value)+2;
3192 if ((size_t) (q-interpret_text+length+1) >= extent)
3193 {
3194 extent+=length;
3195 interpret_text=(char *) ResizeQuantumMemory(
3196 interpret_text,extent+MaxTextExtent,
3197 sizeof(*interpret_text));
3198 if (interpret_text == (char *) NULL)
3199 return((char *)NULL);
3200 q=interpret_text+strlen(interpret_text);
3201 }
3202 q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
3203 }
cristy3ed852e2009-09-05 21:47:34 +00003204 }
anthony003f8992012-05-11 12:50:07 +00003205 key=GetNextImageProperty(image);
cristy3ed852e2009-09-05 21:47:34 +00003206 }
anthony003f8992012-05-11 12:50:07 +00003207 continue;
3208 }
3209 /*
3210 Look for a known property or image attribute
3211 Such as %[basename] %[denisty] %[delay]
3212 Also does wrapped single letters: %[b] %[G] %[g]
3213 */
3214 value=GetMagickProperty(image_info,image,pattern,exception);
3215 if (value != (const char *) NULL)
3216 {
3217 length=strlen(value);
3218 if ((size_t) (q-interpret_text+length+1) >= extent)
3219 {
3220 extent+=length;
3221 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
3222 extent+MaxTextExtent,sizeof(*interpret_text));
anthony104f8932012-05-13 01:54:53 +00003223 if (interpret_text == (char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003224 return((char *)NULL);
anthony003f8992012-05-11 12:50:07 +00003225 q=interpret_text+strlen(interpret_text);
3226 }
3227 (void) CopyMagickString(q,value,extent);
3228 q+=length;
anthony003f8992012-05-11 12:50:07 +00003229 continue;
3230 }
3231 /*
anthony2cfa1a12012-05-12 05:18:07 +00003232 Look for a per-image Artifact (user option, post-interpreted)
anthony003f8992012-05-11 12:50:07 +00003233 */
3234 value=GetImageArtifact(image,pattern);
3235 if (value != (char *) NULL)
3236 {
3237 length=strlen(value);
3238 if ((size_t) (q-interpret_text+length+1) >= extent)
3239 {
3240 extent+=length;
3241 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
3242 extent+MaxTextExtent,sizeof(*interpret_text));
3243 if (interpret_text == (char *) NULL)
3244 return((char *)NULL);
3245 q=interpret_text+strlen(interpret_text);
3246 }
3247 (void) CopyMagickString(q,value,extent);
3248 q+=length;
3249 continue;
3250 }
3251 /*
anthony2cfa1a12012-05-12 05:18:07 +00003252 Look for user option of this name (should never match in CLI usage)
anthony003f8992012-05-11 12:50:07 +00003253 */
3254 if (image_info != (ImageInfo *) NULL) {
cristy3ed852e2009-09-05 21:47:34 +00003255 value=GetImageOption(image_info,pattern);
3256 if (value != (char *) NULL)
3257 {
3258 length=strlen(value);
3259 if ((size_t) (q-interpret_text+length+1) >= extent)
3260 {
3261 extent+=length;
3262 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
3263 extent+MaxTextExtent,sizeof(*interpret_text));
3264 if (interpret_text == (char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003265 return((char *)NULL);
cristy3ed852e2009-09-05 21:47:34 +00003266 q=interpret_text+strlen(interpret_text);
3267 }
3268 (void) CopyMagickString(q,value,extent);
3269 q+=length;
anthony003f8992012-05-11 12:50:07 +00003270 continue;
cristy3ed852e2009-09-05 21:47:34 +00003271 }
anthony003f8992012-05-11 12:50:07 +00003272 }
3273 /*
3274 Failed to find any match anywhere!
3275 */
3276 if (len >= 64) { /* truncate string for error message */
3277 pattern[61] = '.';
3278 pattern[62] = '.';
3279 pattern[63] = '.';
3280 pattern[64] = '\0';
3281 }
3282 (void) ThrowMagickException(exception,GetMagickModule(),
3283 OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
3284 /* continue */
3285 } /* Braced Percent Escape */
3286
3287 } /* for each char in 'embed_text' */
cristy3ed852e2009-09-05 21:47:34 +00003288 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003289 return(interpret_text);
3290}
3291
3292/*
3293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3294% %
3295% %
3296% %
3297% R e m o v e I m a g e P r o p e r t y %
3298% %
3299% %
3300% %
3301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3302%
3303% RemoveImageProperty() removes a property from the image and returns its
3304% value.
3305%
anthonyd2f39e02012-08-20 12:29:28 +00003306% In this case the ConstantString() value returned should be freed by the
3307% caller when finished.
3308%
cristy3ed852e2009-09-05 21:47:34 +00003309% The format of the RemoveImageProperty method is:
3310%
3311% char *RemoveImageProperty(Image *image,const char *property)
3312%
3313% A description of each parameter follows:
3314%
3315% o image: the image.
3316%
3317% o property: the image property.
3318%
3319*/
3320MagickExport char *RemoveImageProperty(Image *image,
3321 const char *property)
3322{
3323 char
3324 *value;
3325
3326 assert(image != (Image *) NULL);
3327 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003328 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003329 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3330 image->filename);
3331 if (image->properties == (void *) NULL)
3332 return((char *) NULL);
3333 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3334 property);
3335 return(value);
3336}
3337
3338/*
3339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3340% %
3341% %
3342% %
3343% 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 %
3344% %
3345% %
3346% %
3347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3348%
3349% ResetImagePropertyIterator() resets the image properties iterator. Use it
3350% in conjunction with GetNextImageProperty() to iterate over all the values
3351% associated with an image property.
3352%
3353% The format of the ResetImagePropertyIterator method is:
3354%
3355% ResetImagePropertyIterator(Image *image)
3356%
3357% A description of each parameter follows:
3358%
3359% o image: the image.
3360%
3361*/
3362MagickExport void ResetImagePropertyIterator(const Image *image)
3363{
3364 assert(image != (Image *) NULL);
3365 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003366 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003367 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3368 image->filename);
3369 if (image->properties == (void *) NULL)
3370 return;
3371 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3372}
3373
3374/*
3375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3376% %
3377% %
3378% %
3379% S e t I m a g e P r o p e r t y %
3380% %
3381% %
3382% %
3383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3384%
anthony104f8932012-05-13 01:54:53 +00003385% SetImageProperty() saves the given string value either to specific known
cristy97fc9f32012-06-13 21:04:44 +00003386% attribute or to a freeform property string.
anthony6e2e0732012-05-06 12:22:43 +00003387%
cristy97fc9f32012-06-13 21:04:44 +00003388% Attempting to set a property that is normally calculated will produce
anthony6e2e0732012-05-06 12:22:43 +00003389% an exception.
cristy3ed852e2009-09-05 21:47:34 +00003390%
3391% The format of the SetImageProperty method is:
3392%
3393% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003394% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003395%
3396% A description of each parameter follows:
3397%
3398% o image: the image.
3399%
3400% o property: the image property.
3401%
3402% o values: the image property values.
3403%
cristyd15e6592011-10-15 00:13:06 +00003404% o exception: return any errors or warnings in this structure.
3405%
cristy3ed852e2009-09-05 21:47:34 +00003406*/
3407MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003408 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003409{
3410 MagickBooleanType
3411 status;
3412
3413 MagickStatusType
3414 flags;
3415
3416 assert(image != (Image *) NULL);
3417 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003418 if( IfMagickTrue(image->debug) )
anthony6e2e0732012-05-06 12:22:43 +00003419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3420
anthony7bb7aee2012-05-15 00:09:16 +00003421 /* Create splay-tree */
cristy3ed852e2009-09-05 21:47:34 +00003422 if (image->properties == (void *) NULL)
3423 image->properties=NewSplayTree(CompareSplayTreeString,
3424 RelinquishMagickMemory,RelinquishMagickMemory);
anthony7bb7aee2012-05-15 00:09:16 +00003425
cristy97fc9f32012-06-13 21:04:44 +00003426 /* Delete property if NULL -- empty string values are valid! */
anthony7bb7aee2012-05-15 00:09:16 +00003427 if ((value == (const char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003428 return(DeleteImageProperty(image,property));
3429 status=MagickTrue;
anthony6e2e0732012-05-06 12:22:43 +00003430
anthony7bb7aee2012-05-15 00:09:16 +00003431 /* Do not 'set' single letter properties - read only shorthand */
anthony6e2e0732012-05-06 12:22:43 +00003432 if (strlen(property) <= 1)
3433 {
3434 (void) ThrowMagickException(exception,GetMagickModule(),
3435 OptionError,"SetReadOnlyProperty","'%s'",property);
3436 return(MagickFalse);
3437 }
anthony7bb7aee2012-05-15 00:09:16 +00003438
anthonyd2f39e02012-08-20 12:29:28 +00003439 /* FUTURE: These should produce a 'illegal settings' error
3440 + binary chars in property key
anthony7bb7aee2012-05-15 00:09:16 +00003441 + single letter property keys (read only)
anthonyd2f39e02012-08-20 12:29:28 +00003442 + known special prefixes (read only, they don't get saved!)
anthony6e2e0732012-05-06 12:22:43 +00003443 */
3444
cristy3ed852e2009-09-05 21:47:34 +00003445 switch (*property)
3446 {
3447 case 'B':
3448 case 'b':
3449 {
anthony2f7f9ca2012-05-14 06:33:53 +00003450 if (LocaleCompare("background",property) == 0)
cristyc6c08ab2010-07-24 23:50:09 +00003451 {
cristy9950d572011-10-01 18:22:35 +00003452 (void) QueryColorCompliance(value,AllCompliance,
3453 &image->background_color,exception);
cristyc6c08ab2010-07-24 23:50:09 +00003454 break;
3455 }
cristy3ed852e2009-09-05 21:47:34 +00003456 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3457 ConstantString(property),ConstantString(value));
anthony6e2e0732012-05-06 12:22:43 +00003458 /* FUTURE: error if status is bad? */
cristy3ed852e2009-09-05 21:47:34 +00003459 break;
3460 }
3461 case 'C':
3462 case 'c':
3463 {
anthony2f7f9ca2012-05-14 06:33:53 +00003464 if (LocaleCompare("channels",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003465 {
3466 (void) ThrowMagickException(exception,GetMagickModule(),
3467 OptionError,"SetReadOnlyProperty","'%s'",property);
3468 status=MagickFalse;
3469 break;
3470 }
anthony2f7f9ca2012-05-14 06:33:53 +00003471 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003472 {
cristybb503372010-05-27 20:51:26 +00003473 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003474 colorspace;
3475
cristy042ee782011-04-22 18:48:30 +00003476 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003477 value);
3478 if (colorspace < 0)
3479 break;
cristy8e7bc182012-06-01 16:19:45 +00003480 image->colorspace=(ColorspaceType) colorspace;
cristyd228c032012-06-03 15:01:15 +00003481 image->rendering_intent=UndefinedIntent;
3482 image->gamma=1.000f;
3483 ResetMagickMemory(&image->chromaticity,0,sizeof(image->chromaticity));
3484 if (IssRGBColorspace(image->colorspace) != MagickFalse)
3485 {
3486 image->rendering_intent=PerceptualIntent;
3487 image->gamma=1.000f/2.200f;
3488 image->chromaticity.red_primary.x=0.6400f;
3489 image->chromaticity.red_primary.y=0.3300f;
3490 image->chromaticity.red_primary.z=0.0300f;
3491 image->chromaticity.green_primary.x=0.3000f;
3492 image->chromaticity.green_primary.y=0.6000f;
3493 image->chromaticity.green_primary.z=0.1000f;
3494 image->chromaticity.blue_primary.x=0.1500f;
3495 image->chromaticity.blue_primary.y=0.0600f;
3496 image->chromaticity.blue_primary.z=0.7900f;
3497 image->chromaticity.white_point.x=0.3127f;
3498 image->chromaticity.white_point.y=0.3290f;
3499 image->chromaticity.white_point.z=0.3583f;
3500 }
cristy3ed852e2009-09-05 21:47:34 +00003501 break;
3502 }
anthony2f7f9ca2012-05-14 06:33:53 +00003503 if (LocaleCompare("compose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003504 {
cristybb503372010-05-27 20:51:26 +00003505 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003506 compose;
3507
cristy042ee782011-04-22 18:48:30 +00003508 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003509 if (compose < 0)
3510 break;
3511 image->compose=(CompositeOperator) compose;
3512 break;
3513 }
anthony2f7f9ca2012-05-14 06:33:53 +00003514 if (LocaleCompare("compress",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003515 {
cristybb503372010-05-27 20:51:26 +00003516 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003517 compression;
3518
cristy042ee782011-04-22 18:48:30 +00003519 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003520 value);
3521 if (compression < 0)
3522 break;
3523 image->compression=(CompressionType) compression;
3524 break;
3525 }
anthony2f7f9ca2012-05-14 06:33:53 +00003526 if (LocaleCompare("copyright",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003527 {
3528 (void) ThrowMagickException(exception,GetMagickModule(),
3529 OptionError,"SetReadOnlyProperty","'%s'",property);
3530 status=MagickFalse;
3531 break;
3532 }
cristy3ed852e2009-09-05 21:47:34 +00003533 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3534 ConstantString(property),ConstantString(value));
3535 break;
3536 }
3537 case 'D':
3538 case 'd':
3539 {
anthony2f7f9ca2012-05-14 06:33:53 +00003540 if (LocaleCompare("delay",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003541 {
3542 GeometryInfo
3543 geometry_info;
3544
3545 flags=ParseGeometry(value,&geometry_info);
3546 if ((flags & GreaterValue) != 0)
3547 {
cristybb503372010-05-27 20:51:26 +00003548 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3549 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003550 }
3551 else
3552 if ((flags & LessValue) != 0)
3553 {
cristybb503372010-05-27 20:51:26 +00003554 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
cristyc6c08ab2010-07-24 23:50:09 +00003555 image->ticks_per_second=(ssize_t)
3556 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003557 }
3558 else
cristybb503372010-05-27 20:51:26 +00003559 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003560 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003561 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003562 break;
3563 }
anthony2f7f9ca2012-05-14 06:33:53 +00003564 if (LocaleCompare("density",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003565 {
3566 GeometryInfo
3567 geometry_info;
3568
3569 flags=ParseGeometry(value,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +00003570 image->resolution.x=geometry_info.rho;
3571 image->resolution.y=geometry_info.sigma;
cristyfbb56842010-08-02 11:26:33 +00003572 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +00003573 image->resolution.y=image->resolution.x;
cristyfbb56842010-08-02 11:26:33 +00003574 }
anthony2f7f9ca2012-05-14 06:33:53 +00003575 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003576 {
cristye27293e2009-12-18 02:53:20 +00003577 image->depth=StringToUnsignedLong(value);
cristy3ed852e2009-09-05 21:47:34 +00003578 break;
3579 }
anthony2f7f9ca2012-05-14 06:33:53 +00003580 if (LocaleCompare("dispose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003581 {
cristybb503372010-05-27 20:51:26 +00003582 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003583 dispose;
3584
cristy042ee782011-04-22 18:48:30 +00003585 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003586 if (dispose < 0)
3587 break;
3588 image->dispose=(DisposeType) dispose;
3589 break;
3590 }
3591 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3592 ConstantString(property),ConstantString(value));
3593 break;
3594 }
3595 case 'G':
3596 case 'g':
3597 {
anthony2f7f9ca2012-05-14 06:33:53 +00003598 if (LocaleCompare("gamma",property) == 0)
3599 {
3600 image->gamma=StringToDouble(value,(char **) NULL);
3601 break;
3602 }
3603 if (LocaleCompare("gravity",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003604 {
cristybb503372010-05-27 20:51:26 +00003605 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003606 gravity;
3607
cristy042ee782011-04-22 18:48:30 +00003608 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003609 if (gravity < 0)
3610 break;
3611 image->gravity=(GravityType) gravity;
3612 break;
3613 }
3614 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3615 ConstantString(property),ConstantString(value));
3616 break;
3617 }
anthony6e2e0732012-05-06 12:22:43 +00003618 case 'H':
3619 case 'h':
anthony2f7f9ca2012-05-14 06:33:53 +00003620 if (LocaleCompare("height",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003621 {
3622 (void) ThrowMagickException(exception,GetMagickModule(),
3623 OptionError,"SetReadOnlyProperty","'%s'",property);
3624 status=MagickFalse;
3625 break;
3626 }
cristy3ed852e2009-09-05 21:47:34 +00003627 case 'I':
3628 case 'i':
3629 {
anthony2f7f9ca2012-05-14 06:33:53 +00003630 if (LocaleCompare("intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003631 {
cristybb503372010-05-27 20:51:26 +00003632 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003633 rendering_intent;
3634
cristy042ee782011-04-22 18:48:30 +00003635 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003636 value);
3637 if (rendering_intent < 0)
3638 break;
3639 image->rendering_intent=(RenderingIntent) rendering_intent;
3640 break;
3641 }
anthony2f7f9ca2012-05-14 06:33:53 +00003642 if (LocaleCompare("interpolate",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003643 {
cristybb503372010-05-27 20:51:26 +00003644 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003645 interpolate;
3646
cristy042ee782011-04-22 18:48:30 +00003647 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003648 value);
3649 if (interpolate < 0)
3650 break;
cristy5c4e2582011-09-11 19:21:03 +00003651 image->interpolate=(PixelInterpolateMethod) interpolate;
cristy3ed852e2009-09-05 21:47:34 +00003652 break;
3653 }
3654 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3655 ConstantString(property),ConstantString(value));
3656 break;
3657 }
anthony6e2e0732012-05-06 12:22:43 +00003658 case 'K':
3659 case 'k':
anthony2f7f9ca2012-05-14 06:33:53 +00003660 if (LocaleCompare("kurtosis",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003661 {
3662 (void) ThrowMagickException(exception,GetMagickModule(),
3663 OptionError,"SetReadOnlyProperty","'%s'",property);
3664 status=MagickFalse;
3665 break;
3666 }
cristy3ed852e2009-09-05 21:47:34 +00003667 case 'L':
3668 case 'l':
3669 {
anthony2f7f9ca2012-05-14 06:33:53 +00003670 if (LocaleCompare("loop",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003671 {
cristye27293e2009-12-18 02:53:20 +00003672 image->iterations=StringToUnsignedLong(value);
cristy3ed852e2009-09-05 21:47:34 +00003673 break;
3674 }
3675 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3676 ConstantString(property),ConstantString(value));
3677 break;
3678 }
anthony6e2e0732012-05-06 12:22:43 +00003679 case 'M':
3680 case 'm':
anthony2f7f9ca2012-05-14 06:33:53 +00003681 if ( (LocaleCompare("magick",property) == 0) ||
3682 (LocaleCompare("max",property) == 0) ||
3683 (LocaleCompare("mean",property) == 0) ||
3684 (LocaleCompare("min",property) == 0) ||
3685 (LocaleCompare("min",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00003686 {
cristy5048d302012-08-07 01:05:16 +00003687 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3688 "SetReadOnlyProperty","'%s'",property);
anthony6e2e0732012-05-06 12:22:43 +00003689 status=MagickFalse;
3690 break;
3691 }
3692 case 'O':
3693 case 'o':
anthony2f7f9ca2012-05-14 06:33:53 +00003694 if (LocaleCompare("opaque",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003695 {
3696 (void) ThrowMagickException(exception,GetMagickModule(),
3697 OptionError,"SetReadOnlyProperty","'%s'",property);
3698 status=MagickFalse;
3699 break;
3700 }
cristy3ed852e2009-09-05 21:47:34 +00003701 case 'P':
3702 case 'p':
3703 {
anthony2f7f9ca2012-05-14 06:33:53 +00003704 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003705 {
3706 char
3707 *geometry;
3708
3709 geometry=GetPageGeometry(value);
3710 flags=ParseAbsoluteGeometry(geometry,&image->page);
3711 geometry=DestroyString(geometry);
3712 break;
3713 }
anthony2f7f9ca2012-05-14 06:33:53 +00003714 if (LocaleCompare("profile",property) == 0)
cristy071dd7b2010-04-09 13:04:54 +00003715 {
3716 ImageInfo
3717 *image_info;
3718
3719 StringInfo
3720 *profile;
3721
3722 image_info=AcquireImageInfo();
3723 (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
cristyc6c08ab2010-07-24 23:50:09 +00003724 (void) SetImageInfo(image_info,1,exception);
3725 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00003726 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003727 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00003728 image_info=DestroyImageInfo(image_info);
3729 break;
3730 }
cristy3ed852e2009-09-05 21:47:34 +00003731 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3732 ConstantString(property),ConstantString(value));
3733 break;
3734 }
3735 case 'R':
3736 case 'r':
3737 {
anthony2f7f9ca2012-05-14 06:33:53 +00003738 if (LocaleCompare("rendering-intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003739 {
cristybb503372010-05-27 20:51:26 +00003740 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003741 rendering_intent;
3742
cristy042ee782011-04-22 18:48:30 +00003743 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003744 value);
3745 if (rendering_intent < 0)
anthony6e2e0732012-05-06 12:22:43 +00003746 status=MagickFalse;
3747 else
3748 image->rendering_intent=(RenderingIntent) rendering_intent;
cristy3ed852e2009-09-05 21:47:34 +00003749 break;
3750 }
3751 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3752 ConstantString(property),ConstantString(value));
3753 break;
3754 }
anthony6e2e0732012-05-06 12:22:43 +00003755 case 'S':
3756 case 's':
anthony2f7f9ca2012-05-14 06:33:53 +00003757 if ( (LocaleCompare("size",property) == 0) ||
3758 (LocaleCompare("skewness",property) == 0) ||
3759 (LocaleCompare("scenes",property) == 0) ||
3760 (LocaleCompare("standard-deviation",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00003761 {
3762 (void) ThrowMagickException(exception,GetMagickModule(),
3763 OptionError,"SetReadOnlyProperty","'%s'",property);
3764 status=MagickFalse;
3765 break;
3766 }
cristy3ed852e2009-09-05 21:47:34 +00003767 case 'T':
3768 case 't':
3769 {
anthony2f7f9ca2012-05-14 06:33:53 +00003770 if (LocaleCompare("tile-offset",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003771 {
3772 char
3773 *geometry;
3774
3775 geometry=GetPageGeometry(value);
3776 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
3777 geometry=DestroyString(geometry);
3778 break;
3779 }
3780 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3781 ConstantString(property),ConstantString(value));
3782 break;
3783 }
cristyfbb56842010-08-02 11:26:33 +00003784 case 'U':
3785 case 'u':
3786 {
anthony2f7f9ca2012-05-14 06:33:53 +00003787 if (LocaleCompare("units",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003788 {
3789 ssize_t
3790 units;
3791
cristy042ee782011-04-22 18:48:30 +00003792 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00003793 if (units < 0)
3794 break;
3795 image->units=(ResolutionType) units;
3796 break;
3797 }
3798 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3799 ConstantString(property),ConstantString(value));
3800 break;
3801 }
anthony6e2e0732012-05-06 12:22:43 +00003802 case 'V':
3803 case 'v':
anthony2f7f9ca2012-05-14 06:33:53 +00003804 if (LocaleCompare("version",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003805 {
3806 (void) ThrowMagickException(exception,GetMagickModule(),
3807 OptionError,"SetReadOnlyProperty","'%s'",property);
3808 status=MagickFalse;
3809 break;
3810 }
3811 case 'W':
3812 case 'w':
anthony2f7f9ca2012-05-14 06:33:53 +00003813 if (LocaleCompare("width",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003814 {
3815 (void) ThrowMagickException(exception,GetMagickModule(),
3816 OptionError,"SetReadOnlyProperty","'%s'",property);
3817 status=MagickFalse;
3818 break;
3819 }
cristy3ed852e2009-09-05 21:47:34 +00003820 default:
3821 {
3822 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3823 ConstantString(property),ConstantString(value));
3824 break;
3825 }
3826 }
3827 return(status);
3828}