blob: be1bfa3932329847d43ab6b3c51e2dae25460798 [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%
anthony643c6132012-11-07 14:50:28 +0000279% DestroyImageProperties() destroys all properties and associated memory
anthonyd2f39e02012-08-20 12:29:28 +0000280% 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" },
cristy6e617e62012-09-29 14:12:23 +00001098 { 0x00000, (const char *) NULL }
cristy3ed852e2009-09-05 21:47:34 +00001099 };
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 }
cristy90e72a02012-09-29 01:08:57 +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
cristy6e617e62012-09-29 14:12:23 +00001428 *key;
cristy3ed852e2009-09-05 21:47:34 +00001429
1430 register const char
1431 *p;
1432
cristy6e617e62012-09-29 14:12:23 +00001433 key=AcquireString(property);
1434 if (level == 2)
1435 (void) SubstituteString(&key,"exif:","exif:thumbnail:");
cristy3ed852e2009-09-05 21:47:34 +00001436 switch (all)
1437 {
1438 case 1:
1439 {
1440 const char
1441 *description;
1442
cristybb503372010-05-27 20:51:26 +00001443 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001444 i;
1445
1446 description="unknown";
1447 for (i=0; ; i++)
1448 {
1449 if (EXIFTag[i].tag == 0)
1450 break;
cristybb503372010-05-27 20:51:26 +00001451 if ((ssize_t) EXIFTag[i].tag == tag_value)
cristy3ed852e2009-09-05 21:47:34 +00001452 {
1453 description=EXIFTag[i].description;
1454 break;
1455 }
1456 }
cristy96ea4862011-11-22 00:45:47 +00001457 (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
cristy3ed852e2009-09-05 21:47:34 +00001458 break;
1459 }
1460 case 2:
1461 {
1462 if (tag_value < 0x10000)
cristyb51dff52011-05-19 16:55:47 +00001463 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001464 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001465 else
1466 if (tag_value < 0x20000)
cristyb51dff52011-05-19 16:55:47 +00001467 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001468 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001469 else
cristyb51dff52011-05-19 16:55:47 +00001470 (void) FormatLocaleString(key,MaxTextExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001471 break;
1472 }
1473 }
1474 p=(const char *) NULL;
1475 if (image->properties != (void *) NULL)
1476 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1477 image->properties,key);
1478 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001479 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001480 value=DestroyString(value);
cristy6e617e62012-09-29 14:12:23 +00001481 key=DestroyString(key);
cristy929ea322011-02-21 15:21:35 +00001482 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001483 }
1484 }
1485 if ((tag_value == TAG_EXIF_OFFSET) ||
cristy4a8b0172012-02-03 16:39:53 +00001486 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
cristy3ed852e2009-09-05 21:47:34 +00001487 {
cristy3d8d1f12012-02-29 01:59:28 +00001488 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001489 offset;
1490
cristy3d8d1f12012-02-29 01:59:28 +00001491 offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
1492 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00001493 {
cristy3d8d1f12012-02-29 01:59:28 +00001494 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001495 tag_offset1;
1496
cristy3d8d1f12012-02-29 01:59:28 +00001497 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1498 0);
cristy3ed852e2009-09-05 21:47:34 +00001499 directory_stack[level].directory=directory;
1500 entry++;
1501 directory_stack[level].entry=entry;
1502 directory_stack[level].offset=tag_offset;
1503 level++;
1504 directory_stack[level].directory=exif+offset;
1505 directory_stack[level].offset=tag_offset1;
1506 directory_stack[level].entry=0;
1507 level++;
1508 if ((directory+2+(12*number_entries)) > (exif+length))
1509 break;
cristy3d8d1f12012-02-29 01:59:28 +00001510 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
cristy4a8b0172012-02-03 16:39:53 +00001511 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00001512 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00001513 (level < (MaxDirectoryStack-2)))
1514 {
1515 directory_stack[level].directory=exif+offset;
1516 directory_stack[level].entry=0;
1517 directory_stack[level].offset=tag_offset1;
1518 level++;
1519 }
1520 }
1521 break;
1522 }
1523 }
cristy142ab012012-02-02 02:26:18 +00001524 } while (level > 0);
1525 exif_resources=DestroySplayTree(exif_resources);
cristy929ea322011-02-21 15:21:35 +00001526 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001527}
1528
cristy13adad02011-08-16 19:22:15 +00001529static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001530{
1531 char
1532 *xmp_profile;
1533
1534 const StringInfo
1535 *profile;
1536
1537 ExceptionInfo
1538 *exception;
1539
1540 MagickBooleanType
1541 status;
1542
1543 register const char
1544 *p;
1545
1546 XMLTreeInfo
1547 *child,
1548 *description,
1549 *node,
1550 *rdf,
1551 *xmp;
1552
1553 profile=GetImageProfile(image,"xmp");
1554 if (profile == (StringInfo *) NULL)
1555 return(MagickFalse);
1556 if ((property == (const char *) NULL) || (*property == '\0'))
1557 return(MagickFalse);
1558 xmp_profile=StringInfoToString(profile);
1559 if (xmp_profile == (char *) NULL)
1560 return(MagickFalse);
1561 for (p=xmp_profile; *p != '\0'; p++)
1562 if ((*p == '<') && (*(p+1) == 'x'))
1563 break;
1564 exception=AcquireExceptionInfo();
1565 xmp=NewXMLTree((char *) p,exception);
1566 xmp_profile=DestroyString(xmp_profile);
1567 exception=DestroyExceptionInfo(exception);
1568 if (xmp == (XMLTreeInfo *) NULL)
1569 return(MagickFalse);
1570 status=MagickFalse;
1571 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1572 if (rdf != (XMLTreeInfo *) NULL)
1573 {
1574 if (image->properties == (void *) NULL)
1575 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1576 RelinquishMagickMemory,RelinquishMagickMemory);
1577 description=GetXMLTreeChild(rdf,"rdf:Description");
1578 while (description != (XMLTreeInfo *) NULL)
1579 {
1580 node=GetXMLTreeChild(description,(const char *) NULL);
1581 while (node != (XMLTreeInfo *) NULL)
1582 {
1583 child=GetXMLTreeChild(node,(const char *) NULL);
1584 if (child == (XMLTreeInfo *) NULL)
1585 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1586 ConstantString(GetXMLTreeTag(node)),
1587 ConstantString(GetXMLTreeContent(node)));
1588 while (child != (XMLTreeInfo *) NULL)
1589 {
1590 if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
1591 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1592 ConstantString(GetXMLTreeTag(child)),
1593 ConstantString(GetXMLTreeContent(child)));
1594 child=GetXMLTreeSibling(child);
1595 }
1596 node=GetXMLTreeSibling(node);
1597 }
1598 description=GetNextXMLTreeTag(description);
1599 }
1600 }
1601 xmp=DestroyXMLTree(xmp);
1602 return(status);
1603}
1604
1605static char *TracePSClippath(const unsigned char *blob,size_t length,
cristyf5d0a1a2012-02-03 12:33:10 +00001606 const size_t magick_unused(columns),const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001607{
1608 char
1609 *path,
1610 *message;
1611
cristy3ed852e2009-09-05 21:47:34 +00001612 MagickBooleanType
1613 in_subpath;
1614
1615 PointInfo
1616 first[3],
1617 last[3],
1618 point[3];
1619
cristybb503372010-05-27 20:51:26 +00001620 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001621 i,
1622 x;
1623
cristy9d314ff2011-03-09 01:30:28 +00001624 ssize_t
1625 knot_count,
1626 selector,
1627 y;
1628
cristy3ed852e2009-09-05 21:47:34 +00001629 path=AcquireString((char *) NULL);
1630 if (path == (char *) NULL)
1631 return((char *) NULL);
1632 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001633 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001634 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001635 (void) FormatLocaleString(message,MaxTextExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001636 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001637 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001638 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001639 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001640 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001641 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001642 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001643 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001644 " /v {currentpoint 6 2 roll 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 " /y {2 copy curveto} bind def\n");
1648 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001649 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001650 " /z {closepath} bind def\n");
1651 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001652 (void) FormatLocaleString(message,MaxTextExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001653 (void) ConcatenateString(&path,message);
1654 /*
1655 The clipping path format is defined in "Adobe Photoshop File
1656 Formats Specification" version 6.0 downloadable from adobe.com.
1657 */
1658 (void) ResetMagickMemory(point,0,sizeof(point));
1659 (void) ResetMagickMemory(first,0,sizeof(first));
1660 (void) ResetMagickMemory(last,0,sizeof(last));
1661 knot_count=0;
1662 in_subpath=MagickFalse;
1663 while (length > 0)
1664 {
cristy4a8b0172012-02-03 16:39:53 +00001665 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001666 switch (selector)
1667 {
1668 case 0:
1669 case 3:
1670 {
1671 if (knot_count != 0)
1672 {
1673 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001674 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001675 break;
1676 }
1677 /*
1678 Expected subpath length record.
1679 */
cristy4a8b0172012-02-03 16:39:53 +00001680 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001681 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001682 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001683 break;
1684 }
1685 case 1:
1686 case 2:
1687 case 4:
1688 case 5:
1689 {
1690 if (knot_count == 0)
1691 {
1692 /*
1693 Unexpected subpath knot
1694 */
1695 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001696 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001697 break;
1698 }
1699 /*
1700 Add sub-path knot
1701 */
1702 for (i=0; i < 3; i++)
1703 {
cristy13b9a2e2010-11-10 14:03:51 +00001704 size_t
cristy97433202009-10-27 02:05:08 +00001705 xx,
1706 yy;
1707
cristy4a8b0172012-02-03 16:39:53 +00001708 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1709 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001710 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001711 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001712 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001713 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001714 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001715 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001716 point[i].x=(double) x/4096/4096;
1717 point[i].y=1.0-(double) y/4096/4096;
1718 }
anthony2fbb5952012-05-05 12:35:30 +00001719 if( IfMagickFalse(in_subpath) )
cristy3ed852e2009-09-05 21:47:34 +00001720 {
cristyb51dff52011-05-19 16:55:47 +00001721 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001722 point[1].x,point[1].y);
1723 for (i=0; i < 3; i++)
1724 {
1725 first[i]=point[i];
1726 last[i]=point[i];
1727 }
1728 }
1729 else
1730 {
1731 /*
1732 Handle special cases when Bezier curves are used to describe
1733 corners and straight lines.
1734 */
1735 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1736 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001737 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001738 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001739 else
1740 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001741 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001742 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001743 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001744 else
1745 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001746 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001747 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001748 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001749 else
cristyb51dff52011-05-19 16:55:47 +00001750 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001751 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001752 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001753 for (i=0; i < 3; i++)
1754 last[i]=point[i];
1755 }
1756 (void) ConcatenateString(&path,message);
1757 in_subpath=MagickTrue;
1758 knot_count--;
1759 /*
1760 Close the subpath if there are no more knots.
1761 */
1762 if (knot_count == 0)
1763 {
1764 /*
1765 Same special handling as above except we compare to the
1766 first point in the path and close the path.
1767 */
1768 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1769 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001770 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001771 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001772 else
1773 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001774 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001775 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001776 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001777 else
1778 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001779 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001780 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001781 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001782 else
cristyb51dff52011-05-19 16:55:47 +00001783 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001784 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001785 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001786 (void) ConcatenateString(&path,message);
1787 in_subpath=MagickFalse;
1788 }
1789 break;
1790 }
1791 case 6:
1792 case 7:
1793 case 8:
1794 default:
1795 {
1796 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001797 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001798 break;
1799 }
1800 }
1801 }
1802 /*
1803 Returns an empty PS path if the path has no knots.
1804 */
cristyb51dff52011-05-19 16:55:47 +00001805 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001806 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001807 (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001808 (void) ConcatenateString(&path,message);
1809 message=DestroyString(message);
1810 return(path);
1811}
1812
1813static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001814 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001815{
1816 char
1817 *path,
1818 *message;
1819
cristy3ed852e2009-09-05 21:47:34 +00001820 MagickBooleanType
1821 in_subpath;
1822
1823 PointInfo
1824 first[3],
1825 last[3],
1826 point[3];
1827
cristybb503372010-05-27 20:51:26 +00001828 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001829 i;
1830
cristy9d314ff2011-03-09 01:30:28 +00001831 ssize_t
1832 knot_count,
1833 selector,
1834 x,
1835 y;
1836
cristy3ed852e2009-09-05 21:47:34 +00001837 path=AcquireString((char *) NULL);
1838 if (path == (char *) NULL)
1839 return((char *) NULL);
1840 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001841 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001842 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
1843 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001844 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001845 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001846 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001847 (void) FormatLocaleString(message,MaxTextExtent,"<g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001848 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001849 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001850 "<path style=\"fill:#00000000;stroke:#00000000;");
1851 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001852 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001853 "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
1854 (void) ConcatenateString(&path,message);
1855 (void) ResetMagickMemory(point,0,sizeof(point));
1856 (void) ResetMagickMemory(first,0,sizeof(first));
1857 (void) ResetMagickMemory(last,0,sizeof(last));
1858 knot_count=0;
1859 in_subpath=MagickFalse;
1860 while (length != 0)
1861 {
cristy4a8b0172012-02-03 16:39:53 +00001862 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001863 switch (selector)
1864 {
1865 case 0:
1866 case 3:
1867 {
1868 if (knot_count != 0)
1869 {
1870 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001871 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001872 break;
1873 }
1874 /*
1875 Expected subpath length record.
1876 */
cristy4a8b0172012-02-03 16:39:53 +00001877 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
cristy3ed852e2009-09-05 21:47:34 +00001878 blob+=22;
cristy4a8b0172012-02-03 16:39:53 +00001879 length-=MagickMin(22,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001880 break;
1881 }
1882 case 1:
1883 case 2:
1884 case 4:
1885 case 5:
1886 {
1887 if (knot_count == 0)
1888 {
1889 /*
1890 Unexpected subpath knot.
1891 */
1892 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001893 length-=MagickMin(24,(ssize_t) length);
cristy97433202009-10-27 02:05:08 +00001894 break;
1895 }
1896 /*
1897 Add sub-path knot
1898 */
1899 for (i=0; i < 3; i++)
1900 {
cristyd4982b42011-03-21 14:24:23 +00001901 size_t
cristy97433202009-10-27 02:05:08 +00001902 xx,
1903 yy;
1904
cristy4a8b0172012-02-03 16:39:53 +00001905 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
1906 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
cristybb503372010-05-27 20:51:26 +00001907 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001908 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001909 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001910 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001911 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001912 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00001913 point[i].x=(double) x*columns/4096/4096;
1914 point[i].y=(double) y*rows/4096/4096;
1915 }
anthony2fbb5952012-05-05 12:35:30 +00001916 if( IfMagickFalse(in_subpath) )
cristy97433202009-10-27 02:05:08 +00001917 {
cristyb51dff52011-05-19 16:55:47 +00001918 (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001919 point[1].x,point[1].y);
1920 for (i=0; i < 3; i++)
1921 {
1922 first[i]=point[i];
1923 last[i]=point[i];
1924 }
cristy3ed852e2009-09-05 21:47:34 +00001925 }
1926 else
1927 {
cristy97433202009-10-27 02:05:08 +00001928 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1929 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001930 (void) FormatLocaleString(message,MaxTextExtent,"L %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001931 point[1].x,point[1].y);
1932 else
cristyb51dff52011-05-19 16:55:47 +00001933 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001934 "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
cristy97433202009-10-27 02:05:08 +00001935 point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001936 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00001937 last[i]=point[i];
1938 }
1939 (void) ConcatenateString(&path,message);
1940 in_subpath=MagickTrue;
1941 knot_count--;
1942 /*
1943 Close the subpath if there are no more knots.
1944 */
1945 if (knot_count == 0)
1946 {
1947 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1948 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001949 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001950 "L %g,%g Z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001951 else
1952 {
cristyb51dff52011-05-19 16:55:47 +00001953 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001954 "C %g,%g %g,%g %g,%g Z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001955 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy97433202009-10-27 02:05:08 +00001956 (void) ConcatenateString(&path,message);
cristy3ed852e2009-09-05 21:47:34 +00001957 }
cristy97433202009-10-27 02:05:08 +00001958 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001959 }
cristy97433202009-10-27 02:05:08 +00001960 break;
cristy3ed852e2009-09-05 21:47:34 +00001961 }
1962 case 6:
1963 case 7:
1964 case 8:
1965 default:
1966 {
1967 blob+=24;
cristy4a8b0172012-02-03 16:39:53 +00001968 length-=MagickMin(24,(ssize_t) length);
cristy3ed852e2009-09-05 21:47:34 +00001969 break;
1970 }
1971 }
1972 }
1973 /*
1974 Return an empty SVG image if the path does not have knots.
1975 */
cristyb51dff52011-05-19 16:55:47 +00001976 (void) FormatLocaleString(message,MaxTextExtent,"\"/>\n");
cristy3ed852e2009-09-05 21:47:34 +00001977 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001978 (void) FormatLocaleString(message,MaxTextExtent,"</g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001979 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001980 (void) FormatLocaleString(message,MaxTextExtent,"</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00001981 (void) ConcatenateString(&path,message);
1982 message=DestroyString(message);
1983 return(path);
1984}
1985
1986MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00001987 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001988{
cristy3ed852e2009-09-05 21:47:34 +00001989 FxInfo
1990 *fx_info;
1991
cristya19f1d72012-08-07 18:24:38 +00001992 double
cristy3ed852e2009-09-05 21:47:34 +00001993 alpha;
1994
1995 MagickStatusType
1996 status;
1997
1998 register const char
1999 *p;
2000
2001 assert(image != (Image *) NULL);
2002 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00002003 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00002004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2005 p=(const char *) NULL;
cristy27f7af22010-06-21 12:23:21 +00002006 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002007 {
cristy5eb9fc82011-02-21 15:05:41 +00002008 if (property == (const char *) NULL)
2009 {
2010 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
2011 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
2012 image->properties);
2013 return(p);
2014 }
anthony643c6132012-11-07 14:50:28 +00002015 if (LocaleNCompare("fx:",property,3) != 0) /* if NOT %[fx:..] !!!! */
cristyd4982b42011-03-21 14:24:23 +00002016 {
2017 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2018 image->properties,property);
2019 if (p != (const char *) NULL)
2020 return(p);
2021 }
cristy3ed852e2009-09-05 21:47:34 +00002022 }
cristy5eb9fc82011-02-21 15:05:41 +00002023 if ((property == (const char *) NULL) ||
2024 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002025 return(p);
cristy3ed852e2009-09-05 21:47:34 +00002026 switch (*property)
2027 {
2028 case '8':
2029 {
2030 if (LocaleNCompare("8bim:",property,5) == 0)
2031 {
anthony2fbb5952012-05-05 12:35:30 +00002032 if( IfMagickTrue(Get8BIMProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002033 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002034 {
2035 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2036 image->properties,property);
2037 return(p);
2038 }
2039 }
2040 break;
2041 }
2042 case 'E':
2043 case 'e':
2044 {
2045 if (LocaleNCompare("exif:",property,5) == 0)
2046 {
anthony2fbb5952012-05-05 12:35:30 +00002047 if( IfMagickTrue(GetEXIFProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002048 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002049 {
2050 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2051 image->properties,property);
2052 return(p);
2053 }
2054 }
2055 break;
2056 }
2057 case 'F':
2058 case 'f':
2059 {
cristy27f7af22010-06-21 12:23:21 +00002060 if (LocaleNCompare("fx:",property,3) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002061 {
cristydb070952012-04-20 14:33:00 +00002062 fx_info=AcquireFxInfo(image,property+3,exception);
cristy2bddff82011-07-25 18:39:12 +00002063 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
cristy0568ffc2011-07-25 16:54:14 +00002064 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002065 fx_info=DestroyFxInfo(fx_info);
anthony2fbb5952012-05-05 12:35:30 +00002066 if( IfMagickTrue(status) )
cristy3ed852e2009-09-05 21:47:34 +00002067 {
2068 char
2069 value[MaxTextExtent];
2070
cristyb51dff52011-05-19 16:55:47 +00002071 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002072 GetMagickPrecision(),(double) alpha);
cristyd15e6592011-10-15 00:13:06 +00002073 (void) SetImageProperty((Image *) image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00002074 }
cristy5eb9fc82011-02-21 15:05:41 +00002075 if (image->properties != (void *) NULL)
2076 {
2077 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2078 image->properties,property);
2079 return(p);
2080 }
cristy3ed852e2009-09-05 21:47:34 +00002081 }
2082 break;
2083 }
2084 case 'I':
2085 case 'i':
2086 {
2087 if (LocaleNCompare("iptc:",property,5) == 0)
2088 {
anthony2fbb5952012-05-05 12:35:30 +00002089 if( IfMagickTrue(GetIPTCProperty(image,property,exception)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002090 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002091 {
2092 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2093 image->properties,property);
2094 return(p);
2095 }
2096 }
2097 break;
2098 }
2099 case 'P':
2100 case 'p':
2101 {
2102 if (LocaleNCompare("pixel:",property,6) == 0)
2103 {
cristy4c08aed2011-07-01 19:47:50 +00002104 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002105 pixel;
2106
cristy4c08aed2011-07-01 19:47:50 +00002107 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +00002108 fx_info=AcquireFxInfo(image,property+6,exception);
cristy0568ffc2011-07-25 16:54:14 +00002109 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
2110 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002111 pixel.red=(double) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002112 status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
2113 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002114 pixel.green=(double) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002115 status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
2116 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002117 pixel.blue=(double) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002118 if (image->colorspace == CMYKColorspace)
2119 {
cristy0568ffc2011-07-25 16:54:14 +00002120 status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristy3ed852e2009-09-05 21:47:34 +00002121 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002122 pixel.black=(double) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002123 }
cristy0568ffc2011-07-25 16:54:14 +00002124 status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
2125 &alpha,exception);
cristya19f1d72012-08-07 18:24:38 +00002126 pixel.alpha=(double) QuantumRange*(1.0-alpha);
cristy3ed852e2009-09-05 21:47:34 +00002127 fx_info=DestroyFxInfo(fx_info);
anthony2fbb5952012-05-05 12:35:30 +00002128 if( IfMagickTrue(status) )
cristy3ed852e2009-09-05 21:47:34 +00002129 {
2130 char
2131 name[MaxTextExtent];
2132
cristy269c9412011-10-13 23:41:15 +00002133 (void) QueryColorname(image,&pixel,SVGCompliance,name,
cristy3ed852e2009-09-05 21:47:34 +00002134 exception);
cristyd15e6592011-10-15 00:13:06 +00002135 (void) SetImageProperty((Image *) image,property,name,exception);
2136 return(GetImageProperty(image,property,exception));
cristy3ed852e2009-09-05 21:47:34 +00002137 }
2138 }
2139 break;
2140 }
2141 case 'X':
2142 case 'x':
2143 {
2144 if (LocaleNCompare("xmp:",property,4) == 0)
2145 {
anthony2fbb5952012-05-05 12:35:30 +00002146 if( IfMagickTrue(GetXMPProperty(image,property)) &&
cristy5eb9fc82011-02-21 15:05:41 +00002147 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002148 {
2149 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2150 image->properties,property);
2151 return(p);
2152 }
2153 }
2154 break;
2155 }
cristy5eb9fc82011-02-21 15:05:41 +00002156 default:
2157 break;
cristy3ed852e2009-09-05 21:47:34 +00002158 }
2159 return(p);
2160}
2161
2162/*
2163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2164% %
2165% %
2166% %
2167+ G e t M a g i c k P r o p e r t y %
2168% %
2169% %
2170% %
2171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2172%
anthony06762232012-04-29 11:45:40 +00002173% GetMagickProperty() gets attributes or calculated values that is associated
cristy97fc9f32012-06-13 21:04:44 +00002174% with a fixed known property name, or single letter property.
anthony06762232012-04-29 11:45:40 +00002175%
cristy97fc9f32012-06-13 21:04:44 +00002176% This does not return, special profile or property expressions. Nor does it
2177% return free-form property strings, unless referenced by a single letter
2178% property name.
anthony06762232012-04-29 11:45:40 +00002179%
anthony2cfa1a12012-05-12 05:18:07 +00002180% The returned string is stored as the image artifact 'get-property' (not as
2181% another property), and as such should not be freed. Later calls however
2182% will overwrite this value so if needed for a longer period a copy should be
2183% made. This artifact can be deleted when no longer required.
cristy3ed852e2009-09-05 21:47:34 +00002184%
2185% The format of the GetMagickProperty method is:
2186%
cristyad785752011-07-27 23:13:03 +00002187% const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
cristy97fc9f32012-06-13 21:04:44 +00002188% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002189%
2190% A description of each parameter follows:
2191%
2192% o image_info: the image info.
2193%
2194% o image: the image.
2195%
2196% o key: the key.
2197%
cristyd15e6592011-10-15 00:13:06 +00002198% o exception: return any errors or warnings in this structure.
2199%
cristy3ed852e2009-09-05 21:47:34 +00002200*/
anthony2fbb5952012-05-05 12:35:30 +00002201static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
2202 Image *image,const char letter,ExceptionInfo *exception)
2203{
2204 char
anthony2cfa1a12012-05-12 05:18:07 +00002205 value[MaxTextExtent];
anthony2fbb5952012-05-05 12:35:30 +00002206
anthony2cfa1a12012-05-12 05:18:07 +00002207 const char
2208 *string;
2209
2210 if (image != (Image *) NULL && IfMagickTrue(image->debug))
cristy68064d72012-06-13 20:56:14 +00002211 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2212 *value='\0'; /* formatted string */
2213 string=(char *) NULL; /* constant string reference */
anthony2fbb5952012-05-05 12:35:30 +00002214 switch (letter)
2215 {
anthony2fbb5952012-05-05 12:35:30 +00002216 case 'b': /* image size read in - in bytes */
2217 {
2218 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2219 ((MagickOffsetType) image->extent));
2220 if (image->extent != (MagickSizeType) ((size_t) image->extent))
2221 (void) FormatMagickSize(image->extent,MagickFalse,value);
2222 ConcatenateMagickString(value,"B",MaxTextExtent);
2223 break;
2224 }
anthony7bb7aee2012-05-15 00:09:16 +00002225 case 'c': /* image comment property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002226 {
anthony2cfa1a12012-05-12 05:18:07 +00002227 string=GetImageProperty(image,"comment",exception);
cristyec6934f2012-08-14 18:38:40 +00002228 if (string == (const char *) NULL)
anthony7bb7aee2012-05-15 00:09:16 +00002229 string="";
anthony2fbb5952012-05-05 12:35:30 +00002230 break;
2231 }
2232 case 'd': /* Directory component of filename */
2233 {
2234 GetPathComponent(image->magick_filename,HeadPath,value);
2235 break;
2236 }
2237 case 'e': /* Filename extension (suffix) of image file */
2238 {
2239 GetPathComponent(image->magick_filename,ExtensionPath,value);
2240 break;
2241 }
2242 case 'f': /* Filename without directory component */
2243 {
2244 GetPathComponent(image->magick_filename,TailPath,value);
2245 break;
2246 }
2247 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */
2248 {
2249 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002250 (double) image->page.width,(double) image->page.height,
2251 (double) image->page.x,(double) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002252 break;
2253 }
2254 case 'h': /* Image height (current) */
2255 {
cristyec6934f2012-08-14 18:38:40 +00002256 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2257 (image->rows != 0 ? image->rows : image->magick_rows));
anthony2fbb5952012-05-05 12:35:30 +00002258 break;
2259 }
anthonyc69008c2012-05-07 11:45:32 +00002260 case 'i': /* Filename last used for image (read or write) */
anthony2fbb5952012-05-05 12:35:30 +00002261 {
anthony2cfa1a12012-05-12 05:18:07 +00002262 string=image->filename;
anthony2fbb5952012-05-05 12:35:30 +00002263 break;
2264 }
2265 case 'k': /* Number of unique colors */
2266 {
cristyec6934f2012-08-14 18:38:40 +00002267 /*
2268 FUTURE: ensure this does not generate the formatted comment!
2269 */
anthony2fbb5952012-05-05 12:35:30 +00002270 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002271 GetNumberColors(image,(FILE *) NULL,exception));
anthony2fbb5952012-05-05 12:35:30 +00002272 break;
2273 }
cristy97fc9f32012-06-13 21:04:44 +00002274 case 'l': /* Image label property - empty string by default */
anthony2fbb5952012-05-05 12:35:30 +00002275 {
anthony2cfa1a12012-05-12 05:18:07 +00002276 string=GetImageProperty(image,"label",exception);
anthony7bb7aee2012-05-15 00:09:16 +00002277 if ( string == (const char *)NULL)
2278 string="";
anthony2fbb5952012-05-05 12:35:30 +00002279 break;
2280 }
2281 case 'm': /* Image format (file magick) */
2282 {
anthony2cfa1a12012-05-12 05:18:07 +00002283 string=image->magick;
anthony2fbb5952012-05-05 12:35:30 +00002284 break;
2285 }
2286 case 'n': /* Number of images in the list. */
2287 {
2288 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002289 GetImageListLength(image));
anthony2fbb5952012-05-05 12:35:30 +00002290 break;
2291 }
2292 case 'o': /* Output Filename - for delegate use only */
2293 {
anthony2cfa1a12012-05-12 05:18:07 +00002294 string=image_info->filename;
anthony2fbb5952012-05-05 12:35:30 +00002295 break;
2296 }
2297 case 'p': /* Image index in current image list -- As 'n' OBSOLETE */
2298 {
2299 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002300 GetImageIndexInList(image));
anthony2fbb5952012-05-05 12:35:30 +00002301 break;
2302 }
2303 case 'q': /* Quantum depth of image in memory */
2304 {
2305 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2306 MAGICKCORE_QUANTUM_DEPTH);
2307 break;
2308 }
2309 case 'r': /* Image storage class and colorspace. */
2310 {
2311 ColorspaceType
2312 colorspace;
2313
2314 colorspace=image->colorspace;
2315 if (IfMagickTrue(IsImageGray(image,exception)))
2316 colorspace=GRAYColorspace;
2317 (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s",
cristy68064d72012-06-13 20:56:14 +00002318 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
anthony2fbb5952012-05-05 12:35:30 +00002319 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
cristy8a46d822012-08-28 23:32:39 +00002320 image->alpha_trait == BlendPixelTrait ? "Matte" : "");
anthony2fbb5952012-05-05 12:35:30 +00002321 break;
2322 }
2323 case 's': /* Image scene number */
2324 {
2325 if (image_info->number_scenes != 0)
2326 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002327 image_info->scene);
anthony2fbb5952012-05-05 12:35:30 +00002328 else
2329 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002330 image->scene);
anthony2fbb5952012-05-05 12:35:30 +00002331 break;
2332 }
2333 case 't': /* Base filename without directory or extention */
2334 {
2335 GetPathComponent(image->magick_filename,BasePath,value);
2336 break;
2337 }
2338 case 'u': /* Unique filename */
2339 {
anthony2cfa1a12012-05-12 05:18:07 +00002340 string=image_info->unique;
anthony2fbb5952012-05-05 12:35:30 +00002341 break;
2342 }
2343 case 'w': /* Image width (current) */
2344 {
2345 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002346 (image->columns != 0 ? image->columns : image->magick_columns));
anthony2fbb5952012-05-05 12:35:30 +00002347 break;
2348 }
anthonyc69008c2012-05-07 11:45:32 +00002349 case 'x': /* Image horizontal resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002350 {
anthonyc69008c2012-05-07 11:45:32 +00002351 (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
cristy68064d72012-06-13 20:56:14 +00002352 image->resolution.x,CommandOptionToMnemonic(
2353 MagickResolutionOptions,(ssize_t)image->units));
anthony2fbb5952012-05-05 12:35:30 +00002354 break;
2355 }
anthonyc69008c2012-05-07 11:45:32 +00002356 case 'y': /* Image vertical resolution (with units) */
anthony2fbb5952012-05-05 12:35:30 +00002357 {
anthonyc69008c2012-05-07 11:45:32 +00002358 (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
cristy68064d72012-06-13 20:56:14 +00002359 image->resolution.y,CommandOptionToMnemonic(MagickResolutionOptions,
2360 (ssize_t) image->units));
anthony2fbb5952012-05-05 12:35:30 +00002361 break;
2362 }
2363 case 'z': /* Image depth as read in */
2364 {
2365 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002366 image->depth);
anthony2fbb5952012-05-05 12:35:30 +00002367 break;
2368 }
2369 case 'A': /* Image alpha channel */
2370 {
2371 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy8a46d822012-08-28 23:32:39 +00002372 CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t) image->alpha_trait));
anthony2fbb5952012-05-05 12:35:30 +00002373 break;
2374 }
2375 case 'C': /* Image compression method. */
2376 {
2377 (void) FormatLocaleString(value,MaxTextExtent,"%s",
2378 CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
2379 image->compression));
2380 break;
2381 }
2382 case 'D': /* Image dispose method. */
2383 {
2384 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy68064d72012-06-13 20:56:14 +00002385 CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) image->dispose));
anthony2fbb5952012-05-05 12:35:30 +00002386 break;
2387 }
2388 case 'G': /* Image size as geometry = "%wx%h" */
2389 {
2390 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002391 (double)image->magick_columns,(double) image->magick_rows);
anthony2fbb5952012-05-05 12:35:30 +00002392 break;
2393 }
2394 case 'H': /* layer canvas height */
2395 {
2396 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002397 image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002398 break;
2399 }
2400 case 'M': /* Magick filename - filename given incl. coder & read mods */
2401 {
anthony2cfa1a12012-05-12 05:18:07 +00002402 string=image->magick_filename;
anthony2fbb5952012-05-05 12:35:30 +00002403 break;
2404 }
2405 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2406 {
cristy68064d72012-06-13 20:56:14 +00002407 (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long)
2408 image->page.x,(long) image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002409 break;
2410 }
2411 case 'P': /* layer canvas page size = "%Wx%H" */
2412 {
2413 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
cristy68064d72012-06-13 20:56:14 +00002414 (double) image->page.width,(double) image->page.height);
anthony2fbb5952012-05-05 12:35:30 +00002415 break;
2416 }
2417 case 'Q': /* image compression quality */
2418 {
2419 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002420 image->quality);
anthony2fbb5952012-05-05 12:35:30 +00002421 break;
2422 }
2423 case 'S': /* Image scenes ???? */
2424 {
2425 if (image_info->number_scenes == 0)
anthony2cfa1a12012-05-12 05:18:07 +00002426 string="2147483647";
anthony2fbb5952012-05-05 12:35:30 +00002427 else
2428 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002429 image_info->scene+image_info->number_scenes);
anthony2fbb5952012-05-05 12:35:30 +00002430 break;
2431 }
2432 case 'T': /* image time delay for animations */
2433 {
2434 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002435 image->delay);
anthony2fbb5952012-05-05 12:35:30 +00002436 break;
2437 }
2438 case 'W': /* layer canvas width */
2439 {
2440 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002441 image->page.width);
anthony2fbb5952012-05-05 12:35:30 +00002442 break;
2443 }
2444 case 'X': /* layer canvas X offset */
2445 {
cristy68064d72012-06-13 20:56:14 +00002446 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2447 image->page.x);
anthony2fbb5952012-05-05 12:35:30 +00002448 break;
2449 }
2450 case 'Y': /* layer canvas Y offset */
2451 {
cristy68064d72012-06-13 20:56:14 +00002452 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
2453 image->page.y);
anthony2fbb5952012-05-05 12:35:30 +00002454 break;
2455 }
anthony6e2e0732012-05-06 12:22:43 +00002456 case 'Z': /* Zero filename ??? */
anthony2fbb5952012-05-05 12:35:30 +00002457 {
anthony2cfa1a12012-05-12 05:18:07 +00002458 string=image_info->zero;
anthony2fbb5952012-05-05 12:35:30 +00002459 break;
2460 }
2461 case '@': /* Trim bounding box, without Trimming! */
2462 {
2463 RectangleInfo
2464 page;
2465
2466 page=GetImageBoundingBox(image,exception);
2467 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristy68064d72012-06-13 20:56:14 +00002468 (double) page.width,(double) page.height,(double) page.x,(double)
2469 page.y);
anthony2fbb5952012-05-05 12:35:30 +00002470 break;
2471 }
2472 case '#': /* Image signature */
2473 {
2474 (void) SignatureImage(image,exception);
anthony2cfa1a12012-05-12 05:18:07 +00002475 string=GetImageProperty(image,"signature",exception);
anthony2fbb5952012-05-05 12:35:30 +00002476 break;
2477 }
2478 case '%': /* percent escaped */
2479 {
anthony2cfa1a12012-05-12 05:18:07 +00002480 string="%";
anthony2fbb5952012-05-05 12:35:30 +00002481 break;
2482 }
2483 }
2484 if (*value != '\0')
anthony2cfa1a12012-05-12 05:18:07 +00002485 string=value;
cristy68064d72012-06-13 20:56:14 +00002486 if (string != (char *) NULL)
2487 {
cristyec6934f2012-08-14 18:38:40 +00002488 (void) SetImageArtifact(image,"get-property",string);
2489 return(GetImageArtifact(image,"get-property"));
cristy68064d72012-06-13 20:56:14 +00002490 }
anthony2cfa1a12012-05-12 05:18:07 +00002491 return((char *)NULL);
anthony2fbb5952012-05-05 12:35:30 +00002492}
2493
cristy3ed852e2009-09-05 21:47:34 +00002494MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002495 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002496{
2497 char
anthony003f8992012-05-11 12:50:07 +00002498 value[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00002499
anthony2cfa1a12012-05-12 05:18:07 +00002500 const char
2501 *string;
2502
anthony2fbb5952012-05-05 12:35:30 +00002503 assert(property[0] != '\0');
cristy68064d72012-06-13 20:56:14 +00002504 if (property[1] == '\0') /* single letter property request */
2505 return(GetMagickPropertyLetter(image_info,image,*property,exception));
2506 if ((image != (Image *) NULL) && IfMagickTrue(image->debug))
2507 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2508 *value='\0'; /* formated string */
2509 string=(char *) NULL; /* constant string reference */
cristyec6897c2010-05-12 00:43:15 +00002510 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002511 {
2512 case 'b':
2513 {
anthony2cfa1a12012-05-12 05:18:07 +00002514 if ((LocaleCompare("base",property) == 0) ||
2515 (LocaleCompare("basename",property) == 0) )
cristy3ed852e2009-09-05 21:47:34 +00002516 {
anthony003f8992012-05-11 12:50:07 +00002517 GetPathComponent(image->magick_filename,BasePath,value);
cristy3ed852e2009-09-05 21:47:34 +00002518 break;
2519 }
2520 break;
2521 }
2522 case 'c':
2523 {
anthony2cfa1a12012-05-12 05:18:07 +00002524 if (LocaleCompare("channels",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002525 {
anthony7bb7aee2012-05-15 00:09:16 +00002526 /* FUTURE: return actual image channels */
cristyb51dff52011-05-19 16:55:47 +00002527 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002528 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002529 image->colorspace));
2530 LocaleLower(value);
cristy8a46d822012-08-28 23:32:39 +00002531 if( image->alpha_trait == BlendPixelTrait )
cristy3ed852e2009-09-05 21:47:34 +00002532 (void) ConcatenateMagickString(value,"a",MaxTextExtent);
2533 break;
2534 }
anthony2cfa1a12012-05-12 05:18:07 +00002535 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002536 {
2537 ColorspaceType
2538 colorspace;
2539
anthony7bb7aee2012-05-15 00:09:16 +00002540 /* FUTURE: return actual colorspace - no 'gray' stuff */
cristy3ed852e2009-09-05 21:47:34 +00002541 colorspace=image->colorspace;
anthony2fbb5952012-05-05 12:35:30 +00002542 if( IfMagickTrue(IsImageGray(image,exception)) )
cristy3ed852e2009-09-05 21:47:34 +00002543 colorspace=GRAYColorspace;
anthony2cfa1a12012-05-12 05:18:07 +00002544 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
2545 colorspace);
cristy3ed852e2009-09-05 21:47:34 +00002546 break;
2547 }
anthony2cfa1a12012-05-12 05:18:07 +00002548 if (LocaleCompare("copyright",property) == 0)
cristy63054742010-09-20 12:42:28 +00002549 {
2550 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
2551 break;
2552 }
cristy3ed852e2009-09-05 21:47:34 +00002553 break;
2554 }
2555 case 'd':
2556 {
anthony2cfa1a12012-05-12 05:18:07 +00002557 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002558 {
cristyb51dff52011-05-19 16:55:47 +00002559 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002560 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002561 break;
2562 }
anthony2cfa1a12012-05-12 05:18:07 +00002563 if (LocaleCompare("directory",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002564 {
anthony003f8992012-05-11 12:50:07 +00002565 GetPathComponent(image->magick_filename,HeadPath,value);
cristy3ed852e2009-09-05 21:47:34 +00002566 break;
2567 }
2568 break;
2569 }
2570 case 'e':
2571 {
anthony2cfa1a12012-05-12 05:18:07 +00002572 if (LocaleCompare("extension",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002573 {
anthony003f8992012-05-11 12:50:07 +00002574 GetPathComponent(image->magick_filename,ExtensionPath,value);
cristy3ed852e2009-09-05 21:47:34 +00002575 break;
2576 }
2577 break;
2578 }
2579 case 'g':
2580 {
anthony2f7f9ca2012-05-14 06:33:53 +00002581 if (LocaleCompare("gamma",property) == 0)
2582 {
2583 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
2584 GetMagickPrecision(),image->gamma);
2585 break;
2586 }
anthony003f8992012-05-11 12:50:07 +00002587 if ( (image_info != (ImageInfo *) NULL) &&
anthony2cfa1a12012-05-12 05:18:07 +00002588 (LocaleCompare("group",property) == 0) )
cristy3ed852e2009-09-05 21:47:34 +00002589 {
cristy68064d72012-06-13 20:56:14 +00002590 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
2591 image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002592 break;
2593 }
2594 break;
2595 }
2596 case 'h':
2597 {
anthony2cfa1a12012-05-12 05:18:07 +00002598 if (LocaleCompare("height",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002599 {
cristyb51dff52011-05-19 16:55:47 +00002600 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002601 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002602 break;
2603 }
2604 break;
2605 }
2606 case 'i':
2607 {
anthony2cfa1a12012-05-12 05:18:07 +00002608 if (LocaleCompare("input",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002609 {
anthony2cfa1a12012-05-12 05:18:07 +00002610 string=image->filename;
cristy3ed852e2009-09-05 21:47:34 +00002611 break;
2612 }
2613 break;
2614 }
2615 case 'k':
2616 {
anthony2cfa1a12012-05-12 05:18:07 +00002617 if (LocaleCompare("kurtosis",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002618 {
2619 double
2620 kurtosis,
2621 skewness;
2622
cristyc82a27b2011-10-21 01:07:16 +00002623 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002624 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002625 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002626 break;
2627 }
2628 break;
2629 }
2630 case 'm':
2631 {
anthony2cfa1a12012-05-12 05:18:07 +00002632 if (LocaleCompare("magick",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002633 {
anthony2cfa1a12012-05-12 05:18:07 +00002634 string=image->magick;
cristy3ed852e2009-09-05 21:47:34 +00002635 break;
2636 }
anthony2cfa1a12012-05-12 05:18:07 +00002637 if (LocaleCompare("max",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002638 {
2639 double
2640 maximum,
2641 minimum;
2642
cristyc82a27b2011-10-21 01:07:16 +00002643 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002644 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002645 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002646 break;
2647 }
anthony2cfa1a12012-05-12 05:18:07 +00002648 if (LocaleCompare("mean",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002649 {
2650 double
2651 mean,
2652 standard_deviation;
2653
cristy68064d72012-06-13 20:56:14 +00002654 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00002655 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002656 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002657 break;
2658 }
anthony2cfa1a12012-05-12 05:18:07 +00002659 if (LocaleCompare("min",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002660 {
2661 double
2662 maximum,
2663 minimum;
2664
cristyc82a27b2011-10-21 01:07:16 +00002665 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002666 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002667 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002668 break;
2669 }
2670 break;
2671 }
cristy3ed852e2009-09-05 21:47:34 +00002672 case 'o':
2673 {
anthony2cfa1a12012-05-12 05:18:07 +00002674 if (LocaleCompare("opaque",property) == 0)
cristyb0094252011-03-26 12:59:45 +00002675 {
2676 MagickBooleanType
2677 opaque;
2678
cristyc82a27b2011-10-21 01:07:16 +00002679 opaque=IsImageOpaque(image,exception);
cristy14fed162012-09-26 10:13:43 +00002680 (void) CopyMagickString(value,IfMagickTrue(opaque) ? "true" : "false",
2681 MaxTextExtent);
cristyb0094252011-03-26 12:59:45 +00002682 break;
2683 }
anthony2cfa1a12012-05-12 05:18:07 +00002684 if (LocaleCompare("orientation",property) == 0)
cristy42aa06c2012-04-01 14:05:10 +00002685 {
anthony2cfa1a12012-05-12 05:18:07 +00002686 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
2687 image->orientation);
cristy42aa06c2012-04-01 14:05:10 +00002688 break;
2689 }
cristy68064d72012-06-13 20:56:14 +00002690 if ((image_info != (ImageInfo *) NULL) &&
2691 (LocaleCompare("output",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002692 {
2693 (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
2694 break;
2695 }
2696 break;
2697 }
2698 case 'p':
2699 {
anthony2cfa1a12012-05-12 05:18:07 +00002700 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002701 {
cristyb51dff52011-05-19 16:55:47 +00002702 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyad785752011-07-27 23:13:03 +00002703 GetImageIndexInList(image)+1);
cristy3ed852e2009-09-05 21:47:34 +00002704 break;
2705 }
2706 break;
2707 }
cristy946b1032012-04-01 14:19:07 +00002708 case 'r':
2709 {
anthony2eb9a422012-05-15 02:28:02 +00002710 /* This matches %[fx:resolution.x] */
anthony2cfa1a12012-05-12 05:18:07 +00002711 if (LocaleCompare("resolution.x",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002712 {
2713 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2714 image->resolution.x);
2715 break;
2716 }
anthony2eb9a422012-05-15 02:28:02 +00002717 /* This matches %[fx:resolution.y] */
anthony2cfa1a12012-05-12 05:18:07 +00002718 if (LocaleCompare("resolution.y",property) == 0)
cristy946b1032012-04-01 14:19:07 +00002719 {
2720 (void) FormatLocaleString(value,MaxTextExtent,"%g",
2721 image->resolution.y);
2722 break;
2723 }
2724 break;
2725 }
cristy3ed852e2009-09-05 21:47:34 +00002726 case 's':
2727 {
anthony2cfa1a12012-05-12 05:18:07 +00002728 if (LocaleCompare("scene",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002729 {
cristy68064d72012-06-13 20:56:14 +00002730 if ((image_info != (ImageInfo *) NULL) &&
2731 (image_info->number_scenes != 0))
cristyb51dff52011-05-19 16:55:47 +00002732 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002733 image_info->scene);
anthony90ebc0d2012-04-28 08:10:02 +00002734 else
2735 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy68064d72012-06-13 20:56:14 +00002736 image->scene);
cristy3ed852e2009-09-05 21:47:34 +00002737 break;
2738 }
anthony2cfa1a12012-05-12 05:18:07 +00002739 if (LocaleCompare("scenes",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002740 {
2741 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
2742 GetImageListLength(image));
2743 break;
2744 }
anthony2cfa1a12012-05-12 05:18:07 +00002745 if (LocaleCompare("size",property) == 0)
anthonyc69008c2012-05-07 11:45:32 +00002746 {
2747 char
2748 format[MaxTextExtent];
2749
2750 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
2751 (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
2752 break;
2753 }
anthony2cfa1a12012-05-12 05:18:07 +00002754 if (LocaleCompare("skewness",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002755 {
2756 double
2757 kurtosis,
2758 skewness;
2759
cristyc82a27b2011-10-21 01:07:16 +00002760 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002761 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002762 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002763 break;
2764 }
anthony2cfa1a12012-05-12 05:18:07 +00002765 if (LocaleCompare("standard-deviation",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002766 {
2767 double
2768 mean,
2769 standard_deviation;
2770
cristy14fed162012-09-26 10:13:43 +00002771 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00002772 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002773 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00002774 break;
2775 }
2776 break;
2777 }
cristy623aaec2012-06-21 00:49:08 +00002778 case 't':
2779 {
2780 if (LocaleCompare("type",property) == 0)
2781 {
2782 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
2783 GetImageType(image,exception));
2784 break;
2785 }
2786 break;
2787 }
cristy3ed852e2009-09-05 21:47:34 +00002788 case 'u':
2789 {
cristy68064d72012-06-13 20:56:14 +00002790 if ((image_info != (ImageInfo *) NULL) &&
2791 (LocaleCompare("unique",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002792 {
anthony2cfa1a12012-05-12 05:18:07 +00002793 string=image_info->unique;
cristy3ed852e2009-09-05 21:47:34 +00002794 break;
2795 }
2796 break;
2797 }
cristy63054742010-09-20 12:42:28 +00002798 case 'v':
2799 {
anthony2cfa1a12012-05-12 05:18:07 +00002800 if (LocaleCompare("version",property) == 0)
cristy63054742010-09-20 12:42:28 +00002801 {
anthony2cfa1a12012-05-12 05:18:07 +00002802 string=GetMagickVersion((size_t *) NULL);
cristy63054742010-09-20 12:42:28 +00002803 break;
2804 }
2805 break;
2806 }
cristy3ed852e2009-09-05 21:47:34 +00002807 case 'w':
2808 {
anthony2cfa1a12012-05-12 05:18:07 +00002809 if (LocaleCompare("width",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002810 {
cristyb51dff52011-05-19 16:55:47 +00002811 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002812 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00002813 break;
2814 }
2815 break;
2816 }
anthony2eb9a422012-05-15 02:28:02 +00002817 case 'x': /* FUTURE: Obsolete X resolution */
anthony90ebc0d2012-04-28 08:10:02 +00002818 {
anthony2cfa1a12012-05-12 05:18:07 +00002819 if ((LocaleCompare("xresolution",property) == 0) ||
2820 (LocaleCompare("x-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00002821 {
anthonyc69008c2012-05-07 11:45:32 +00002822 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00002823 image->resolution.x);
anthony90ebc0d2012-04-28 08:10:02 +00002824 break;
2825 }
2826 break;
2827 }
anthony2eb9a422012-05-15 02:28:02 +00002828 case 'y': /* FUTURE: Obsolete Y resolution */
anthony90ebc0d2012-04-28 08:10:02 +00002829 {
anthony2cfa1a12012-05-12 05:18:07 +00002830 if ((LocaleCompare("yresolution",property) == 0) ||
2831 (LocaleCompare("y-resolution",property) == 0) )
anthony90ebc0d2012-04-28 08:10:02 +00002832 {
anthonyc69008c2012-05-07 11:45:32 +00002833 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristy68064d72012-06-13 20:56:14 +00002834 image->resolution.y);
anthony90ebc0d2012-04-28 08:10:02 +00002835 break;
2836 }
2837 break;
2838 }
cristy3ed852e2009-09-05 21:47:34 +00002839 case 'z':
2840 {
cristy68064d72012-06-13 20:56:14 +00002841 if ((image_info != (ImageInfo *) NULL) &&
2842 (LocaleCompare("zero",property) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002843 {
anthony2cfa1a12012-05-12 05:18:07 +00002844 string=image_info->zero;
cristy3ed852e2009-09-05 21:47:34 +00002845 break;
2846 }
2847 break;
2848 }
2849 }
anthony2cfa1a12012-05-12 05:18:07 +00002850 if (*value != '\0')
2851 string=value;
cristy68064d72012-06-13 20:56:14 +00002852 if (string != (char *)NULL)
2853 {
2854 (void) SetImageArtifact(image,"get-property", string);
2855 return(GetImageArtifact(image,"get-property"));
2856 }
anthony2cfa1a12012-05-12 05:18:07 +00002857 return((char *)NULL);
cristy3ed852e2009-09-05 21:47:34 +00002858}
2859
2860/*
2861%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2862% %
2863% %
2864% %
2865% G e t N e x t I m a g e P r o p e r t y %
2866% %
2867% %
2868% %
2869%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2870%
cristy97fc9f32012-06-13 21:04:44 +00002871% GetNextImageProperty() gets the next free-form string property name.
cristy3ed852e2009-09-05 21:47:34 +00002872%
2873% The format of the GetNextImageProperty method is:
2874%
2875% char *GetNextImageProperty(const Image *image)
2876%
2877% A description of each parameter follows:
2878%
2879% o image: the image.
2880%
2881*/
2882MagickExport char *GetNextImageProperty(const Image *image)
2883{
2884 assert(image != (Image *) NULL);
2885 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00002886 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00002887 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2888 image->filename);
2889 if (image->properties == (void *) NULL)
2890 return((char *) NULL);
2891 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
2892}
2893
2894/*
2895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2896% %
2897% %
2898% %
2899% I n t e r p r e t I m a g e P r o p e r t i e s %
2900% %
2901% %
2902% %
2903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2904%
2905% InterpretImageProperties() replaces any embedded formatting characters with
anthony06762232012-04-29 11:45:40 +00002906% the appropriate image property and returns the interpreted text.
2907%
2908% This searches for and replaces
anthony003f8992012-05-11 12:50:07 +00002909% \n \r \% replaced by newline, return, and percent resp.
2910% &lt; &gt; &amp; replaced by '<', '>', '&' resp.
2911% %% replaced by percent
anthony06762232012-04-29 11:45:40 +00002912%
anthonyd2f39e02012-08-20 12:29:28 +00002913% %x %[x] where 'x' is a single letter properity, case sensitive).
2914% %[type:name] where 'type' a is special and known prefix.
anthony003f8992012-05-11 12:50:07 +00002915% %[name] where 'name' is a specifically known attribute, calculated
cristy97fc9f32012-06-13 21:04:44 +00002916% value, or a per-image property string name, or a per-image
anthonyd2f39e02012-08-20 12:29:28 +00002917% 'artifact' (as generated from a global option).
2918% It may contain ':' as long as the prefix is not special.
anthony06762232012-04-29 11:45:40 +00002919%
anthonyd2f39e02012-08-20 12:29:28 +00002920% Single letter % substitutions will only happen if the character before the
2921% percent is NOT a number. But braced substitutions will always be performed.
2922% This prevents the typical usage of percent in a interpreted geometry
2923% argument from being substituted when the percent is a geometry flag.
anthony003f8992012-05-11 12:50:07 +00002924%
2925% If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
2926% used as a search pattern to print multiple lines of "name=value\n" pairs of
anthony643c6132012-11-07 14:50:28 +00002927% the associacted set of properties.
anthony003f8992012-05-11 12:50:07 +00002928%
anthonyd2f39e02012-08-20 12:29:28 +00002929% The returned string must be freed using DestoryString() by the caller.
anthonyf68116b2012-04-14 12:54:41 +00002930%
cristy3ed852e2009-09-05 21:47:34 +00002931% The format of the InterpretImageProperties method is:
2932%
anthony003f8992012-05-11 12:50:07 +00002933% char *InterpretImageProperties(const ImageInfo *image_info,
2934% Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002935%
2936% A description of each parameter follows:
2937%
2938% o image_info: the image info.
2939%
2940% o image: the image.
2941%
2942% o embed_text: the address of a character string containing the embedded
2943% formatting characters.
2944%
cristy018f07f2011-09-04 21:15:19 +00002945% o exception: return any errors or warnings in this structure.
2946%
cristy3ed852e2009-09-05 21:47:34 +00002947*/
anthony643c6132012-11-07 14:50:28 +00002948
2949/* common inline code to expand the interpreted text string */
2950#define ExtendInterpretText(string_length) do { \
2951 size_t length=(string_length); \
2952 if ((size_t) (q-interpret_text+length+1) >= extent) \
2953 { extent+=length; \
2954 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
2955 extent+MaxTextExtent,sizeof(*interpret_text)); \
2956 if (interpret_text == (char *) NULL) \
2957 return((char *)NULL); \
2958 q=interpret_text+strlen(interpret_text); \
2959 } } while (0) /* no trailing ; */
2960
2961/* same but append the given string */
2962#define AppendString2Text(string) do { \
2963 size_t length=strlen((string)); \
2964 if ((size_t) (q-interpret_text+length+1) >= extent) \
2965 { extent+=length; \
2966 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
2967 extent+MaxTextExtent,sizeof(*interpret_text)); \
2968 if (interpret_text == (char *) NULL) \
2969 return((char *)NULL); \
2970 q=interpret_text+strlen(interpret_text); \
2971 } \
2972 (void) CopyMagickString(q,(string),extent); \
2973 q+=length; \
2974 } while (0) /* no trailing ; */
2975
2976/* same but append a 'key' and 'value' pair */
2977#define AppendKeyValue2Text(key,value) do { \
2978 size_t length=strlen(key)+strlen(value)+2; \
2979 if ((size_t) (q-interpret_text+length+1) >= extent) \
2980 { extent+=length; \
2981 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \
2982 extent+MaxTextExtent,sizeof(*interpret_text)); \
2983 if (interpret_text == (char *) NULL) \
2984 return((char *)NULL); \
2985 q=interpret_text+strlen(interpret_text); \
2986 } \
2987 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(value)); \
2988 } while (0) /* no trailing ; */
2989
cristy3ed852e2009-09-05 21:47:34 +00002990MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00002991 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002992{
2993 char
anthony003f8992012-05-11 12:50:07 +00002994 *interpret_text;
cristy3ed852e2009-09-05 21:47:34 +00002995
cristy3ed852e2009-09-05 21:47:34 +00002996 register char
anthony643c6132012-11-07 14:50:28 +00002997 *q; /* current position in interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00002998
2999 register const char
anthony643c6132012-11-07 14:50:28 +00003000 *p; /* position in embed_text string being expanded */
cristy3ed852e2009-09-05 21:47:34 +00003001
cristy3ed852e2009-09-05 21:47:34 +00003002 size_t
anthony643c6132012-11-07 14:50:28 +00003003 extent; /* allocated length of interpret_text */
cristy3ed852e2009-09-05 21:47:34 +00003004
anthony003f8992012-05-11 12:50:07 +00003005 MagickBooleanType
3006 number;
3007
cristy3ed852e2009-09-05 21:47:34 +00003008 assert(image != (Image *) NULL);
3009 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003010 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003011 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthony2fbb5952012-05-05 12:35:30 +00003012
anthony964d28e2012-05-17 23:39:46 +00003013 if ((embed_text == (const char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003014 return((char *) NULL);
anthony003f8992012-05-11 12:50:07 +00003015 p=embed_text;
anthony2fbb5952012-05-05 12:35:30 +00003016
anthony964d28e2012-05-17 23:39:46 +00003017 if (*p == '\0')
3018 return(ConstantString(""));
3019
anthony2fbb5952012-05-05 12:35:30 +00003020 /* handle a '@' replace string from file */
anthony003f8992012-05-11 12:50:07 +00003021 if (*p == '@') {
3022 p++;
3023 if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
3024 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003025 OptionError,"UnableToAccessPath","%s",p);
anthony003f8992012-05-11 12:50:07 +00003026 return((char *) NULL);
3027 }
3028 return(FileToString(p,~0,exception));
3029 }
3030
cristy3ed852e2009-09-05 21:47:34 +00003031 /*
3032 Translate any embedded format characters.
3033 */
anthony003f8992012-05-11 12:50:07 +00003034 interpret_text=AcquireString(embed_text); /* new string with extra space */
anthony643c6132012-11-07 14:50:28 +00003035 extent=MaxTextExtent; /* allocated space in string */
anthony003f8992012-05-11 12:50:07 +00003036 number=MagickFalse; /* is last char a number? */
3037 for (q=interpret_text; *p!='\0'; number=IsMagickTrue(isdigit(*p)),p++)
cristy3ed852e2009-09-05 21:47:34 +00003038 {
3039 *q='\0';
anthony643c6132012-11-07 14:50:28 +00003040 ExtendInterpretText(MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003041 /*
anthonyd2f39e02012-08-20 12:29:28 +00003042 Look for the various escapes, (and handle other specials)
cristy3ed852e2009-09-05 21:47:34 +00003043 */
anthony003f8992012-05-11 12:50:07 +00003044 switch (*p) {
3045 case '\\':
3046 switch (*(p+1)) {
3047 case '\0':
3048 continue;
3049 case 'r': /* convert to RETURN */
3050 *q++='\r';
3051 p++;
3052 continue;
3053 case 'n': /* convert to NEWLINE */
3054 *q++='\n';
3055 p++;
3056 continue;
3057 case '\n': /* EOL removal UNIX,MacOSX */
3058 p++;
3059 continue;
3060 case '\r': /* EOL removal DOS,Windows */
3061 p++;
3062 if (*p == '\n') /* return-newline EOL */
3063 p++;
3064 continue;
3065 default:
3066 p++;
3067 *q++=(*p);
3068 continue;
3069 }
3070 continue; /* never reached! */
3071 case '&':
anthony104f8932012-05-13 01:54:53 +00003072 if (LocaleNCompare("&lt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003073 *q++='<', p+=3;
anthony104f8932012-05-13 01:54:53 +00003074 else if (LocaleNCompare("&gt;",p,4) == 0)
anthony003f8992012-05-11 12:50:07 +00003075 *q++='>', p+=3;
anthony104f8932012-05-13 01:54:53 +00003076 else if (LocaleNCompare("&amp;",p,5) == 0)
anthony003f8992012-05-11 12:50:07 +00003077 *q++='&', p+=4;
3078 else
3079 *q++=(*p);
cristy3ed852e2009-09-05 21:47:34 +00003080 continue;
anthony003f8992012-05-11 12:50:07 +00003081 case '%':
3082 break; /* continue to next set of handlers */
3083 default:
3084 *q++=(*p); /* any thing else is 'as normal' */
cristy3ed852e2009-09-05 21:47:34 +00003085 continue;
anthony003f8992012-05-11 12:50:07 +00003086 }
anthony104f8932012-05-13 01:54:53 +00003087 p++; /* advance beyond the percent */
anthony2fbb5952012-05-05 12:35:30 +00003088
anthony003f8992012-05-11 12:50:07 +00003089 /*
anthony2ec9bd92012-05-20 05:43:24 +00003090 Doubled Percent - or percent at end of string
anthony003f8992012-05-11 12:50:07 +00003091 */
anthony2ec9bd92012-05-20 05:43:24 +00003092 if ( *p == '\0' )
3093 p--;
anthony104f8932012-05-13 01:54:53 +00003094 if ( *p == '%' ) {
anthony89075d02012-05-18 03:40:40 +00003095 *q++='%';
anthony104f8932012-05-13 01:54:53 +00003096 continue;
3097 }
anthony003f8992012-05-11 12:50:07 +00003098
3099 /*
anthonyd2f39e02012-08-20 12:29:28 +00003100 Single letter escapes %c
anthony003f8992012-05-11 12:50:07 +00003101 */
anthony104f8932012-05-13 01:54:53 +00003102 if ( *p != '[' ) {
anthony003f8992012-05-11 12:50:07 +00003103 const char
3104 *value;
3105
3106 /* But only if not preceeded by a number! */
3107 if ( IfMagickTrue(number) ) {
anthony104f8932012-05-13 01:54:53 +00003108 *q++='%'; /* do NOT substitute the percent */
3109 p--; /* back up one */
anthony003f8992012-05-11 12:50:07 +00003110 continue;
3111 }
anthony2fbb5952012-05-05 12:35:30 +00003112 value=GetMagickPropertyLetter(image_info,image,*p, exception);
3113 if (value != (char *) NULL) {
anthony643c6132012-11-07 14:50:28 +00003114 AppendString2Text(value);
anthony2cfa1a12012-05-12 05:18:07 +00003115 continue;
anthony2fbb5952012-05-05 12:35:30 +00003116 }
anthony2cfa1a12012-05-12 05:18:07 +00003117 (void) ThrowMagickException(exception,GetMagickModule(),
3118 OptionWarning,"UnknownImageProperty","\"%%%c\"",*p);
anthony003f8992012-05-11 12:50:07 +00003119 continue;
anthony2fbb5952012-05-05 12:35:30 +00003120 }
anthony2fbb5952012-05-05 12:35:30 +00003121
anthony003f8992012-05-11 12:50:07 +00003122 /*
anthonyd2f39e02012-08-20 12:29:28 +00003123 Braced Percent Escape %[...]
anthony003f8992012-05-11 12:50:07 +00003124 */
3125 {
3126 char
3127 pattern[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00003128
anthony003f8992012-05-11 12:50:07 +00003129 const char
3130 *key,
3131 *value;
cristy3ed852e2009-09-05 21:47:34 +00003132
anthony003f8992012-05-11 12:50:07 +00003133 register ssize_t
3134 len;
cristy3ed852e2009-09-05 21:47:34 +00003135
anthony003f8992012-05-11 12:50:07 +00003136 ssize_t
3137 depth;
3138
3139 /* get the string framed by the %[...] */
anthony104f8932012-05-13 01:54:53 +00003140 p++; /* advance p to just inside the opening brace */
anthony003f8992012-05-11 12:50:07 +00003141 depth=1;
3142 if ( *p == ']' ) {
3143 (void) ThrowMagickException(exception,GetMagickModule(),
3144 OptionWarning,"UnknownImageProperty","\"%%[]\"");
3145 break;
3146 }
3147 for (len=0; len<(MaxTextExtent-1L) && (*p != '\0');)
3148 {
3149 /* skip escaped braces within braced pattern */
3150 if ( (*p == '\\') && (*(p+1) != '\0') ) {
3151 pattern[len++]=(*p++);
3152 pattern[len++]=(*p++);
3153 continue;
3154 }
3155 if (*p == '[')
3156 depth++;
3157 if (*p == ']')
3158 depth--;
3159 if (depth <= 0)
3160 break;
3161 pattern[len++]=(*p++);
3162 }
3163 pattern[len]='\0';
3164 /* Check for unmatched final ']' for "%[...]" */
3165 if ( depth != 0 ) {
3166 if (len >= 64) { /* truncate string for error message */
3167 pattern[61] = '.';
3168 pattern[62] = '.';
3169 pattern[63] = '.';
3170 pattern[64] = '\0';
3171 }
3172 (void) ThrowMagickException(exception,GetMagickModule(),
anthony104f8932012-05-13 01:54:53 +00003173 OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
3174 interpret_text=DestroyString(interpret_text);
3175 return((char *)NULL);
anthony003f8992012-05-11 12:50:07 +00003176 }
3177
3178 /*
anthony643c6132012-11-07 14:50:28 +00003179 Special Lookup Prefixes %[prefix:...]
anthony003f8992012-05-11 12:50:07 +00003180 */
anthony643c6132012-11-07 14:50:28 +00003181 /* option - direct global option lookup (with globbing) */
3182 if (LocaleNCompare("option:",pattern,7) == 0)
3183 {
3184 if (image_info == (ImageInfo *) NULL)
3185 continue; /* no global options available */
3186 if( IfMagickTrue(IsGlob(pattern+7)) )
3187 {
3188 ResetImageOptionIterator(image_info);
3189 while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
3190 if( IfMagickTrue(GlobExpression(key,pattern+7,MagickTrue)) )
3191 {
3192 value=GetImageOption(image_info,key);
3193 if (value != (const char *) NULL)
3194 AppendKeyValue2Text(key,value);
3195 /* else - assertion failure? key but no value! */
3196 }
3197 continue;
3198 }
3199 value=GetImageOption(image_info,pattern+7);
3200 if (value != (char *) NULL)
3201 AppendString2Text(value);
3202 /* else - no global option of this specifc name */
3203 continue;
3204 }
3205 /* artifact - direct image artifact lookup (with glob) */
anthonyc7994672012-11-17 05:33:27 +00003206 if (LocaleNCompare("artifact:",pattern,9) == 0)
anthony643c6132012-11-07 14:50:28 +00003207 {
3208 if (image == (Image *) NULL)
anthonyc7994672012-11-17 05:33:27 +00003209 continue; /* else no image to retrieve artifact */
anthony643c6132012-11-07 14:50:28 +00003210 if( IfMagickTrue(IsGlob(pattern+9)) )
3211 {
3212 ResetImageArtifactIterator(image);
3213 while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
3214 if( IfMagickTrue(GlobExpression(key,pattern+9,MagickTrue)) )
3215 {
3216 value=GetImageArtifact(image,key);
3217 if (value != (const char *) NULL)
3218 AppendKeyValue2Text(key,value);
3219 /* else - assertion failure? key but no value! */
3220 }
3221 continue;
3222 }
3223 value=GetImageArtifact(image,pattern+9);
3224 if (value != (char *) NULL)
3225 AppendString2Text(value);
anthonyc7994672012-11-17 05:33:27 +00003226 /* else - no artifact of this specifc name */
3227 continue;
anthony643c6132012-11-07 14:50:28 +00003228 }
3229 /* FUTURE: handle %[property:...] prefix - abort other lookups */
3230
3231 /* handle special image properties */
3232 /* For example: %[exif:...] %[fx:...] %[pixel:...] */
anthony003f8992012-05-11 12:50:07 +00003233 value=GetImageProperty(image,pattern,exception);
3234 if (value != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003235 {
anthony643c6132012-11-07 14:50:28 +00003236 AppendString2Text(value);
anthony003f8992012-05-11 12:50:07 +00003237 continue;
3238 }
3239 /*
cristy97fc9f32012-06-13 21:04:44 +00003240 Handle property 'glob' patterns
anthony003f8992012-05-11 12:50:07 +00003241 Such as: %[*] %[user:array_??] %[filename:e*]
3242 */
3243 if( IfMagickTrue(IsGlob(pattern)) )
3244 {
3245 ResetImagePropertyIterator(image);
anthony643c6132012-11-07 14:50:28 +00003246 while ((key=GetNextImageProperty(image)) != (const char *) NULL)
anthony003f8992012-05-11 12:50:07 +00003247 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
cristy3ed852e2009-09-05 21:47:34 +00003248 {
anthony003f8992012-05-11 12:50:07 +00003249 value=GetImageProperty(image,key,exception);
3250 if (value != (const char *) NULL)
anthony643c6132012-11-07 14:50:28 +00003251 AppendKeyValue2Text(key,value);
3252 /* else - assertion failure? */
cristy3ed852e2009-09-05 21:47:34 +00003253 }
anthony003f8992012-05-11 12:50:07 +00003254 continue;
3255 }
3256 /*
3257 Look for a known property or image attribute
3258 Such as %[basename] %[denisty] %[delay]
anthony643c6132012-11-07 14:50:28 +00003259 Also handles a braced single letter: %[b] %[G] %[g]
anthony003f8992012-05-11 12:50:07 +00003260 */
3261 value=GetMagickProperty(image_info,image,pattern,exception);
3262 if (value != (const char *) NULL)
3263 {
anthony643c6132012-11-07 14:50:28 +00003264 AppendString2Text(value);
anthony003f8992012-05-11 12:50:07 +00003265 continue;
3266 }
3267 /*
anthony2cfa1a12012-05-12 05:18:07 +00003268 Look for a per-image Artifact (user option, post-interpreted)
anthony003f8992012-05-11 12:50:07 +00003269 */
3270 value=GetImageArtifact(image,pattern);
3271 if (value != (char *) NULL)
3272 {
anthony643c6132012-11-07 14:50:28 +00003273 AppendString2Text(value);
anthony003f8992012-05-11 12:50:07 +00003274 continue;
3275 }
3276 /*
anthony2cfa1a12012-05-12 05:18:07 +00003277 Look for user option of this name (should never match in CLI usage)
anthony003f8992012-05-11 12:50:07 +00003278 */
3279 if (image_info != (ImageInfo *) NULL) {
cristy3ed852e2009-09-05 21:47:34 +00003280 value=GetImageOption(image_info,pattern);
3281 if (value != (char *) NULL)
3282 {
anthony643c6132012-11-07 14:50:28 +00003283 AppendString2Text(value);
anthony003f8992012-05-11 12:50:07 +00003284 continue;
cristy3ed852e2009-09-05 21:47:34 +00003285 }
anthony003f8992012-05-11 12:50:07 +00003286 }
3287 /*
3288 Failed to find any match anywhere!
3289 */
3290 if (len >= 64) { /* truncate string for error message */
3291 pattern[61] = '.';
3292 pattern[62] = '.';
3293 pattern[63] = '.';
3294 pattern[64] = '\0';
3295 }
3296 (void) ThrowMagickException(exception,GetMagickModule(),
3297 OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
3298 /* continue */
3299 } /* Braced Percent Escape */
3300
3301 } /* for each char in 'embed_text' */
cristy3ed852e2009-09-05 21:47:34 +00003302 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003303 return(interpret_text);
3304}
3305
3306/*
3307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3308% %
3309% %
3310% %
3311% R e m o v e I m a g e P r o p e r t y %
3312% %
3313% %
3314% %
3315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3316%
3317% RemoveImageProperty() removes a property from the image and returns its
3318% value.
3319%
anthonyd2f39e02012-08-20 12:29:28 +00003320% In this case the ConstantString() value returned should be freed by the
3321% caller when finished.
3322%
cristy3ed852e2009-09-05 21:47:34 +00003323% The format of the RemoveImageProperty method is:
3324%
3325% char *RemoveImageProperty(Image *image,const char *property)
3326%
3327% A description of each parameter follows:
3328%
3329% o image: the image.
3330%
3331% o property: the image property.
3332%
3333*/
3334MagickExport char *RemoveImageProperty(Image *image,
3335 const char *property)
3336{
3337 char
3338 *value;
3339
3340 assert(image != (Image *) NULL);
3341 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003342 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003343 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3344 image->filename);
3345 if (image->properties == (void *) NULL)
3346 return((char *) NULL);
3347 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3348 property);
3349 return(value);
3350}
3351
3352/*
3353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3354% %
3355% %
3356% %
3357% 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 %
3358% %
3359% %
3360% %
3361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3362%
3363% ResetImagePropertyIterator() resets the image properties iterator. Use it
3364% in conjunction with GetNextImageProperty() to iterate over all the values
3365% associated with an image property.
3366%
3367% The format of the ResetImagePropertyIterator method is:
3368%
3369% ResetImagePropertyIterator(Image *image)
3370%
3371% A description of each parameter follows:
3372%
3373% o image: the image.
3374%
3375*/
3376MagickExport void ResetImagePropertyIterator(const Image *image)
3377{
3378 assert(image != (Image *) NULL);
3379 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003380 if( IfMagickTrue(image->debug) )
cristy3ed852e2009-09-05 21:47:34 +00003381 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3382 image->filename);
3383 if (image->properties == (void *) NULL)
3384 return;
3385 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3386}
3387
3388/*
3389%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3390% %
3391% %
3392% %
3393% S e t I m a g e P r o p e r t y %
3394% %
3395% %
3396% %
3397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3398%
anthony104f8932012-05-13 01:54:53 +00003399% SetImageProperty() saves the given string value either to specific known
cristy97fc9f32012-06-13 21:04:44 +00003400% attribute or to a freeform property string.
anthony6e2e0732012-05-06 12:22:43 +00003401%
cristy97fc9f32012-06-13 21:04:44 +00003402% Attempting to set a property that is normally calculated will produce
anthony6e2e0732012-05-06 12:22:43 +00003403% an exception.
cristy3ed852e2009-09-05 21:47:34 +00003404%
3405% The format of the SetImageProperty method is:
3406%
3407% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003408% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003409%
3410% A description of each parameter follows:
3411%
3412% o image: the image.
3413%
3414% o property: the image property.
3415%
3416% o values: the image property values.
3417%
cristyd15e6592011-10-15 00:13:06 +00003418% o exception: return any errors or warnings in this structure.
3419%
cristy3ed852e2009-09-05 21:47:34 +00003420*/
3421MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003422 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003423{
3424 MagickBooleanType
3425 status;
3426
3427 MagickStatusType
3428 flags;
3429
3430 assert(image != (Image *) NULL);
3431 assert(image->signature == MagickSignature);
anthony2fbb5952012-05-05 12:35:30 +00003432 if( IfMagickTrue(image->debug) )
anthony6e2e0732012-05-06 12:22:43 +00003433 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3434
anthony7bb7aee2012-05-15 00:09:16 +00003435 /* Create splay-tree */
cristy3ed852e2009-09-05 21:47:34 +00003436 if (image->properties == (void *) NULL)
3437 image->properties=NewSplayTree(CompareSplayTreeString,
3438 RelinquishMagickMemory,RelinquishMagickMemory);
anthony7bb7aee2012-05-15 00:09:16 +00003439
cristy97fc9f32012-06-13 21:04:44 +00003440 /* Delete property if NULL -- empty string values are valid! */
anthony7bb7aee2012-05-15 00:09:16 +00003441 if ((value == (const char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003442 return(DeleteImageProperty(image,property));
3443 status=MagickTrue;
anthony6e2e0732012-05-06 12:22:43 +00003444
anthony7bb7aee2012-05-15 00:09:16 +00003445 /* Do not 'set' single letter properties - read only shorthand */
anthony6e2e0732012-05-06 12:22:43 +00003446 if (strlen(property) <= 1)
3447 {
3448 (void) ThrowMagickException(exception,GetMagickModule(),
3449 OptionError,"SetReadOnlyProperty","'%s'",property);
3450 return(MagickFalse);
3451 }
anthony7bb7aee2012-05-15 00:09:16 +00003452
anthony643c6132012-11-07 14:50:28 +00003453 /* FUTURE: binary chars or quotes in key should produce a error
anthony6e2e0732012-05-06 12:22:43 +00003454 */
3455
cristy3ed852e2009-09-05 21:47:34 +00003456 switch (*property)
3457 {
anthonyc99875a2012-11-14 05:22:17 +00003458#if 0 /* the percent escape sets prefix:... propertys!
3459 This causes it to fail */
anthony643c6132012-11-07 14:50:28 +00003460 case '8':
3461 {
3462 if (LocaleNCompare("8bim:",property,5) == 0)
3463 {
3464 (void) ThrowMagickException(exception,GetMagickModule(),
3465 OptionError,"SetReadOnlyProperty","'%s'",property);
3466 return(MagickFalse);
3467 }
3468 break;
3469 }
anthonyc99875a2012-11-14 05:22:17 +00003470#endif
cristy3ed852e2009-09-05 21:47:34 +00003471 case 'B':
3472 case 'b':
3473 {
anthony2f7f9ca2012-05-14 06:33:53 +00003474 if (LocaleCompare("background",property) == 0)
cristyc6c08ab2010-07-24 23:50:09 +00003475 {
cristy9950d572011-10-01 18:22:35 +00003476 (void) QueryColorCompliance(value,AllCompliance,
anthony643c6132012-11-07 14:50:28 +00003477 &image->background_color,exception);
3478 /* check for value exception? */
3479 /* also add user input to splay tree */
cristyc6c08ab2010-07-24 23:50:09 +00003480 }
anthony643c6132012-11-07 14:50:28 +00003481 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003482 }
3483 case 'C':
3484 case 'c':
3485 {
anthony2f7f9ca2012-05-14 06:33:53 +00003486 if (LocaleCompare("channels",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003487 {
3488 (void) ThrowMagickException(exception,GetMagickModule(),
3489 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003490 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003491 }
anthony2f7f9ca2012-05-14 06:33:53 +00003492 if (LocaleCompare("colorspace",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003493 {
cristybb503372010-05-27 20:51:26 +00003494 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003495 colorspace;
3496
cristy042ee782011-04-22 18:48:30 +00003497 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003498 value);
3499 if (colorspace < 0)
anthony643c6132012-11-07 14:50:28 +00003500 return(MagickFalse); /* value exception? */
cristy8e7bc182012-06-01 16:19:45 +00003501 image->colorspace=(ColorspaceType) colorspace;
cristyd228c032012-06-03 15:01:15 +00003502 image->rendering_intent=UndefinedIntent;
3503 image->gamma=1.000f;
3504 ResetMagickMemory(&image->chromaticity,0,sizeof(image->chromaticity));
3505 if (IssRGBColorspace(image->colorspace) != MagickFalse)
3506 {
3507 image->rendering_intent=PerceptualIntent;
3508 image->gamma=1.000f/2.200f;
3509 image->chromaticity.red_primary.x=0.6400f;
3510 image->chromaticity.red_primary.y=0.3300f;
3511 image->chromaticity.red_primary.z=0.0300f;
3512 image->chromaticity.green_primary.x=0.3000f;
3513 image->chromaticity.green_primary.y=0.6000f;
3514 image->chromaticity.green_primary.z=0.1000f;
3515 image->chromaticity.blue_primary.x=0.1500f;
3516 image->chromaticity.blue_primary.y=0.0600f;
3517 image->chromaticity.blue_primary.z=0.7900f;
3518 image->chromaticity.white_point.x=0.3127f;
3519 image->chromaticity.white_point.y=0.3290f;
3520 image->chromaticity.white_point.z=0.3583f;
3521 }
anthony643c6132012-11-07 14:50:28 +00003522 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003523 }
anthony2f7f9ca2012-05-14 06:33:53 +00003524 if (LocaleCompare("compose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003525 {
cristybb503372010-05-27 20:51:26 +00003526 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003527 compose;
3528
cristy042ee782011-04-22 18:48:30 +00003529 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003530 if (compose < 0)
anthony643c6132012-11-07 14:50:28 +00003531 return(MagickFalse); /* value exception? */
cristy3ed852e2009-09-05 21:47:34 +00003532 image->compose=(CompositeOperator) compose;
anthony643c6132012-11-07 14:50:28 +00003533 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003534 }
anthony2f7f9ca2012-05-14 06:33:53 +00003535 if (LocaleCompare("compress",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003536 {
cristybb503372010-05-27 20:51:26 +00003537 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003538 compression;
3539
cristy042ee782011-04-22 18:48:30 +00003540 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003541 value);
3542 if (compression < 0)
anthony643c6132012-11-07 14:50:28 +00003543 return(MagickFalse); /* value exception? */
cristy3ed852e2009-09-05 21:47:34 +00003544 image->compression=(CompressionType) compression;
anthony643c6132012-11-07 14:50:28 +00003545 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003546 }
anthony2f7f9ca2012-05-14 06:33:53 +00003547 if (LocaleCompare("copyright",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003548 {
3549 (void) ThrowMagickException(exception,GetMagickModule(),
3550 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003551 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003552 }
anthony643c6132012-11-07 14:50:28 +00003553 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003554 }
3555 case 'D':
3556 case 'd':
3557 {
anthony2f7f9ca2012-05-14 06:33:53 +00003558 if (LocaleCompare("delay",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003559 {
3560 GeometryInfo
3561 geometry_info;
3562
3563 flags=ParseGeometry(value,&geometry_info);
3564 if ((flags & GreaterValue) != 0)
3565 {
cristybb503372010-05-27 20:51:26 +00003566 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3567 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003568 }
3569 else
3570 if ((flags & LessValue) != 0)
3571 {
cristybb503372010-05-27 20:51:26 +00003572 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
anthony643c6132012-11-07 14:50:28 +00003573 image->delay=(ssize_t)
cristyc6c08ab2010-07-24 23:50:09 +00003574 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003575 }
3576 else
cristybb503372010-05-27 20:51:26 +00003577 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003578 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003579 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
anthony643c6132012-11-07 14:50:28 +00003580 return(MagickTrue);
3581 }
3582 if (LocaleCompare("delay_units",property) == 0)
3583 {
3584 (void) ThrowMagickException(exception,GetMagickModule(),
3585 OptionError,"SetReadOnlyProperty","'%s'",property);
3586 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00003587 }
anthony2f7f9ca2012-05-14 06:33:53 +00003588 if (LocaleCompare("density",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003589 {
3590 GeometryInfo
3591 geometry_info;
3592
3593 flags=ParseGeometry(value,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +00003594 image->resolution.x=geometry_info.rho;
3595 image->resolution.y=geometry_info.sigma;
cristyfbb56842010-08-02 11:26:33 +00003596 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +00003597 image->resolution.y=image->resolution.x;
anthony643c6132012-11-07 14:50:28 +00003598 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00003599 }
anthony2f7f9ca2012-05-14 06:33:53 +00003600 if (LocaleCompare("depth",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003601 {
cristye27293e2009-12-18 02:53:20 +00003602 image->depth=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00003603 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003604 }
anthony2f7f9ca2012-05-14 06:33:53 +00003605 if (LocaleCompare("dispose",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003606 {
cristybb503372010-05-27 20:51:26 +00003607 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003608 dispose;
3609
cristy042ee782011-04-22 18:48:30 +00003610 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003611 if (dispose < 0)
anthony643c6132012-11-07 14:50:28 +00003612 return(MagickFalse); /* value exception? */
cristy3ed852e2009-09-05 21:47:34 +00003613 image->dispose=(DisposeType) dispose;
anthony643c6132012-11-07 14:50:28 +00003614 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003615 }
anthony643c6132012-11-07 14:50:28 +00003616 break; /* add to properties splay tree */
3617 }
anthonyc99875a2012-11-14 05:22:17 +00003618#if 0 /* the percent escape sets prefix:... propertys!
3619 This causes it to fail */
anthony643c6132012-11-07 14:50:28 +00003620 case 'E':
3621 case 'e':
3622 {
3623 if (LocaleNCompare("exif:",property,5) == 0)
3624 {
3625 (void) ThrowMagickException(exception,GetMagickModule(),
3626 OptionError,"SetReadOnlyProperty","'%s'",property);
3627 return(MagickFalse);
3628 }
3629 break; /* add to properties splay tree */
3630 }
3631 case 'F':
3632 case 'f':
3633 {
3634 if (LocaleNCompare("fx:",property,3) == 0)
3635 {
3636 (void) ThrowMagickException(exception,GetMagickModule(),
3637 OptionError,"SetReadOnlyProperty","'%s'",property);
3638 return(MagickFalse);
3639 }
3640 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003641 }
anthonyc99875a2012-11-14 05:22:17 +00003642#endif
cristy3ed852e2009-09-05 21:47:34 +00003643 case 'G':
3644 case 'g':
3645 {
anthony2f7f9ca2012-05-14 06:33:53 +00003646 if (LocaleCompare("gamma",property) == 0)
3647 {
3648 image->gamma=StringToDouble(value,(char **) NULL);
anthony643c6132012-11-07 14:50:28 +00003649 return(MagickTrue);
anthony2f7f9ca2012-05-14 06:33:53 +00003650 }
3651 if (LocaleCompare("gravity",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003652 {
cristybb503372010-05-27 20:51:26 +00003653 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003654 gravity;
3655
cristy042ee782011-04-22 18:48:30 +00003656 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003657 if (gravity < 0)
anthony643c6132012-11-07 14:50:28 +00003658 return(MagickFalse); /* value exception? */
cristy3ed852e2009-09-05 21:47:34 +00003659 image->gravity=(GravityType) gravity;
anthony643c6132012-11-07 14:50:28 +00003660 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003661 }
anthony643c6132012-11-07 14:50:28 +00003662 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003663 }
anthony6e2e0732012-05-06 12:22:43 +00003664 case 'H':
3665 case 'h':
anthony643c6132012-11-07 14:50:28 +00003666 {
anthony2f7f9ca2012-05-14 06:33:53 +00003667 if (LocaleCompare("height",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003668 {
3669 (void) ThrowMagickException(exception,GetMagickModule(),
3670 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003671 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003672 }
anthony643c6132012-11-07 14:50:28 +00003673 break; /* add to properties splay tree */
3674 }
cristy3ed852e2009-09-05 21:47:34 +00003675 case 'I':
3676 case 'i':
3677 {
anthony2f7f9ca2012-05-14 06:33:53 +00003678 if (LocaleCompare("intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003679 {
cristybb503372010-05-27 20:51:26 +00003680 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003681 rendering_intent;
3682
cristy042ee782011-04-22 18:48:30 +00003683 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003684 value);
3685 if (rendering_intent < 0)
anthony643c6132012-11-07 14:50:28 +00003686 return(MagickFalse); /* value exception? */
cristy3ed852e2009-09-05 21:47:34 +00003687 image->rendering_intent=(RenderingIntent) rendering_intent;
anthony643c6132012-11-07 14:50:28 +00003688 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003689 }
anthony2f7f9ca2012-05-14 06:33:53 +00003690 if (LocaleCompare("interpolate",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003691 {
cristybb503372010-05-27 20:51:26 +00003692 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003693 interpolate;
3694
cristy042ee782011-04-22 18:48:30 +00003695 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003696 value);
3697 if (interpolate < 0)
anthony643c6132012-11-07 14:50:28 +00003698 return(MagickFalse); /* value exception? */
cristy5c4e2582011-09-11 19:21:03 +00003699 image->interpolate=(PixelInterpolateMethod) interpolate;
anthony643c6132012-11-07 14:50:28 +00003700 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003701 }
anthonyc99875a2012-11-14 05:22:17 +00003702#if 0 /* the percent escape sets prefix:... propertys!
3703 This causes it to fail */
anthony643c6132012-11-07 14:50:28 +00003704 if (LocaleNCompare("iptc:",property,5) == 0)
3705 {
3706 (void) ThrowMagickException(exception,GetMagickModule(),
3707 OptionError,"SetReadOnlyProperty","'%s'",property);
3708 return(MagickFalse);
3709 }
anthonyc99875a2012-11-14 05:22:17 +00003710#endif
anthony643c6132012-11-07 14:50:28 +00003711 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003712 }
anthony6e2e0732012-05-06 12:22:43 +00003713 case 'K':
3714 case 'k':
anthony2f7f9ca2012-05-14 06:33:53 +00003715 if (LocaleCompare("kurtosis",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003716 {
3717 (void) ThrowMagickException(exception,GetMagickModule(),
3718 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003719 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003720 }
anthony643c6132012-11-07 14:50:28 +00003721 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003722 case 'L':
3723 case 'l':
3724 {
anthony2f7f9ca2012-05-14 06:33:53 +00003725 if (LocaleCompare("loop",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003726 {
cristye27293e2009-12-18 02:53:20 +00003727 image->iterations=StringToUnsignedLong(value);
anthony643c6132012-11-07 14:50:28 +00003728 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003729 }
anthony643c6132012-11-07 14:50:28 +00003730 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003731 }
anthony6e2e0732012-05-06 12:22:43 +00003732 case 'M':
3733 case 'm':
anthony2f7f9ca2012-05-14 06:33:53 +00003734 if ( (LocaleCompare("magick",property) == 0) ||
3735 (LocaleCompare("max",property) == 0) ||
3736 (LocaleCompare("mean",property) == 0) ||
3737 (LocaleCompare("min",property) == 0) ||
3738 (LocaleCompare("min",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00003739 {
cristy5048d302012-08-07 01:05:16 +00003740 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
3741 "SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003742 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003743 }
anthony643c6132012-11-07 14:50:28 +00003744 break; /* add to properties splay tree */
anthony6e2e0732012-05-06 12:22:43 +00003745 case 'O':
3746 case 'o':
anthony2f7f9ca2012-05-14 06:33:53 +00003747 if (LocaleCompare("opaque",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003748 {
3749 (void) ThrowMagickException(exception,GetMagickModule(),
3750 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003751 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003752 }
anthony643c6132012-11-07 14:50:28 +00003753 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003754 case 'P':
3755 case 'p':
3756 {
anthony2f7f9ca2012-05-14 06:33:53 +00003757 if (LocaleCompare("page",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003758 {
3759 char
3760 *geometry;
3761
3762 geometry=GetPageGeometry(value);
3763 flags=ParseAbsoluteGeometry(geometry,&image->page);
3764 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00003765 return(MagickTrue);
3766 }
anthonyc99875a2012-11-14 05:22:17 +00003767#if 0 /* the percent escape sets prefix:... propertys!
3768 This causes it to fail */
anthony643c6132012-11-07 14:50:28 +00003769 if (LocaleNCompare("pixel:",property,6) == 0)
3770 {
3771 (void) ThrowMagickException(exception,GetMagickModule(),
3772 OptionError,"SetReadOnlyProperty","'%s'",property);
3773 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00003774 }
anthonyc99875a2012-11-14 05:22:17 +00003775#endif
anthony2f7f9ca2012-05-14 06:33:53 +00003776 if (LocaleCompare("profile",property) == 0)
cristy071dd7b2010-04-09 13:04:54 +00003777 {
3778 ImageInfo
3779 *image_info;
3780
3781 StringInfo
3782 *profile;
3783
3784 image_info=AcquireImageInfo();
3785 (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
cristyc6c08ab2010-07-24 23:50:09 +00003786 (void) SetImageInfo(image_info,1,exception);
3787 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00003788 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003789 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00003790 image_info=DestroyImageInfo(image_info);
anthony643c6132012-11-07 14:50:28 +00003791 return(MagickTrue);
cristy071dd7b2010-04-09 13:04:54 +00003792 }
anthony643c6132012-11-07 14:50:28 +00003793 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003794 }
3795 case 'R':
3796 case 'r':
3797 {
anthony2f7f9ca2012-05-14 06:33:53 +00003798 if (LocaleCompare("rendering-intent",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003799 {
cristybb503372010-05-27 20:51:26 +00003800 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003801 rendering_intent;
3802
cristy042ee782011-04-22 18:48:30 +00003803 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003804 value);
3805 if (rendering_intent < 0)
anthony643c6132012-11-07 14:50:28 +00003806 return(MagickFalse); /* value exception? */
3807 image->rendering_intent=(RenderingIntent) rendering_intent;
3808 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003809 }
anthony643c6132012-11-07 14:50:28 +00003810 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003811 }
anthony6e2e0732012-05-06 12:22:43 +00003812 case 'S':
3813 case 's':
anthony2f7f9ca2012-05-14 06:33:53 +00003814 if ( (LocaleCompare("size",property) == 0) ||
3815 (LocaleCompare("skewness",property) == 0) ||
3816 (LocaleCompare("scenes",property) == 0) ||
3817 (LocaleCompare("standard-deviation",property) == 0) )
anthony6e2e0732012-05-06 12:22:43 +00003818 {
3819 (void) ThrowMagickException(exception,GetMagickModule(),
3820 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003821 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003822 }
anthony643c6132012-11-07 14:50:28 +00003823 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003824 case 'T':
3825 case 't':
3826 {
anthony2f7f9ca2012-05-14 06:33:53 +00003827 if (LocaleCompare("tile-offset",property) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003828 {
3829 char
3830 *geometry;
3831
3832 geometry=GetPageGeometry(value);
3833 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
3834 geometry=DestroyString(geometry);
anthony643c6132012-11-07 14:50:28 +00003835 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00003836 }
anthony643c6132012-11-07 14:50:28 +00003837 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003838 }
cristyfbb56842010-08-02 11:26:33 +00003839 case 'U':
3840 case 'u':
3841 {
anthony2f7f9ca2012-05-14 06:33:53 +00003842 if (LocaleCompare("units",property) == 0)
cristyfbb56842010-08-02 11:26:33 +00003843 {
3844 ssize_t
3845 units;
3846
cristy042ee782011-04-22 18:48:30 +00003847 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00003848 if (units < 0)
anthony643c6132012-11-07 14:50:28 +00003849 return(MagickFalse); /* value exception? */
cristyfbb56842010-08-02 11:26:33 +00003850 image->units=(ResolutionType) units;
anthony643c6132012-11-07 14:50:28 +00003851 return(MagickTrue);
cristyfbb56842010-08-02 11:26:33 +00003852 }
anthony643c6132012-11-07 14:50:28 +00003853 break; /* add to properties splay tree */
cristyfbb56842010-08-02 11:26:33 +00003854 }
anthony6e2e0732012-05-06 12:22:43 +00003855 case 'V':
3856 case 'v':
anthony643c6132012-11-07 14:50:28 +00003857 {
anthony2f7f9ca2012-05-14 06:33:53 +00003858 if (LocaleCompare("version",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003859 {
3860 (void) ThrowMagickException(exception,GetMagickModule(),
3861 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003862 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003863 }
anthony643c6132012-11-07 14:50:28 +00003864 break; /* add to properties splay tree */
3865 }
anthony6e2e0732012-05-06 12:22:43 +00003866 case 'W':
3867 case 'w':
anthony643c6132012-11-07 14:50:28 +00003868 {
anthony2f7f9ca2012-05-14 06:33:53 +00003869 if (LocaleCompare("width",property) == 0)
anthony6e2e0732012-05-06 12:22:43 +00003870 {
3871 (void) ThrowMagickException(exception,GetMagickModule(),
3872 OptionError,"SetReadOnlyProperty","'%s'",property);
anthony643c6132012-11-07 14:50:28 +00003873 return(MagickFalse);
anthony6e2e0732012-05-06 12:22:43 +00003874 }
anthony643c6132012-11-07 14:50:28 +00003875 break; /* add to properties splay tree */
3876 }
anthonyc99875a2012-11-14 05:22:17 +00003877#if 0 /* the percent escape sets prefix:... propertys!
3878 This causes it to fail */
anthony643c6132012-11-07 14:50:28 +00003879 case 'X':
3880 case 'x':
cristy3ed852e2009-09-05 21:47:34 +00003881 {
anthony643c6132012-11-07 14:50:28 +00003882 if (LocaleNCompare("xmp:",property,4) == 0)
3883 {
3884 (void) ThrowMagickException(exception,GetMagickModule(),
3885 OptionError,"SetReadOnlyProperty","'%s'",property);
3886 return(MagickFalse);
3887 }
3888 break; /* add to properties splay tree */
cristy3ed852e2009-09-05 21:47:34 +00003889 }
anthonyc99875a2012-11-14 05:22:17 +00003890#endif
cristy3ed852e2009-09-05 21:47:34 +00003891 }
anthonyc99875a2012-11-14 05:22:17 +00003892 /* Default: add to properties splay tree */
anthony643c6132012-11-07 14:50:28 +00003893 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3894 ConstantString(property),ConstantString(value));
3895 /* FUTURE: error if status is bad? */
cristy3ed852e2009-09-05 21:47:34 +00003896 return(status);
3897}