blob: 54fc599e83259e42ca3d08633d88cf1151a1fe86 [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"
44#include "MagickCore/attribute.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/color.h"
47#include "MagickCore/compare.h"
48#include "MagickCore/constitute.h"
49#include "MagickCore/draw.h"
50#include "MagickCore/effect.h"
51#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
53#include "MagickCore/fx.h"
54#include "MagickCore/fx-private.h"
55#include "MagickCore/gem.h"
56#include "MagickCore/geometry.h"
57#include "MagickCore/histogram.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image.h"
60#include "MagickCore/layer.h"
cristy7832dc22011-09-05 01:21:53 +000061#include "MagickCore/locale-private.h"
cristy4c08aed2011-07-01 19:47:50 +000062#include "MagickCore/list.h"
63#include "MagickCore/magick.h"
64#include "MagickCore/memory_.h"
65#include "MagickCore/monitor.h"
66#include "MagickCore/montage.h"
67#include "MagickCore/option.h"
68#include "MagickCore/profile.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resource_.h"
72#include "MagickCore/splay-tree.h"
cristy7832dc22011-09-05 01:21:53 +000073#include "MagickCore/signature.h"
cristy4c08aed2011-07-01 19:47:50 +000074#include "MagickCore/statistic.h"
75#include "MagickCore/string_.h"
76#include "MagickCore/string-private.h"
77#include "MagickCore/token.h"
cristy7832dc22011-09-05 01:21:53 +000078#include "MagickCore/token-private.h"
cristy4c08aed2011-07-01 19:47:50 +000079#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000080#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000081#include "MagickCore/version.h"
82#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000083#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000084
85/*
86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87% %
88% %
89% %
90% C l o n e I m a g e P r o p e r t i e s %
91% %
92% %
93% %
94%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95%
96% CloneImageProperties() clones one or more image properties.
97%
98% The format of the CloneImageProperties method is:
99%
100% MagickBooleanType CloneImageProperties(Image *image,
101% const Image *clone_image)
102%
103% A description of each parameter follows:
104%
105% o image: the image.
106%
107% o clone_image: the clone image.
108%
109*/
110MagickExport MagickBooleanType CloneImageProperties(Image *image,
111 const Image *clone_image)
112{
113 assert(image != (Image *) NULL);
114 assert(image->signature == MagickSignature);
115 if (image->debug != MagickFalse)
116 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
117 assert(clone_image != (const Image *) NULL);
118 assert(clone_image->signature == MagickSignature);
119 if (clone_image->debug != MagickFalse)
120 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
121 clone_image->filename);
122 (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
123 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
124 MaxTextExtent);
125 image->compression=clone_image->compression;
126 image->quality=clone_image->quality;
127 image->depth=clone_image->depth;
128 image->background_color=clone_image->background_color;
129 image->border_color=clone_image->border_color;
130 image->matte_color=clone_image->matte_color;
131 image->transparent_color=clone_image->transparent_color;
132 image->gamma=clone_image->gamma;
133 image->chromaticity=clone_image->chromaticity;
134 image->rendering_intent=clone_image->rendering_intent;
135 image->black_point_compensation=clone_image->black_point_compensation;
136 image->units=clone_image->units;
137 image->montage=(char *) NULL;
138 image->directory=(char *) NULL;
139 (void) CloneString(&image->geometry,clone_image->geometry);
140 image->offset=clone_image->offset;
cristy2a11bef2011-10-28 18:33:11 +0000141 image->resolution.x=clone_image->resolution.x;
142 image->resolution.y=clone_image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +0000143 image->page=clone_image->page;
144 image->tile_offset=clone_image->tile_offset;
145 image->extract_info=clone_image->extract_info;
146 image->bias=clone_image->bias;
147 image->filter=clone_image->filter;
148 image->blur=clone_image->blur;
149 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%
190% DefineImageProperty() associates a key/value pair with an image property.
191%
192% The format of the DefineImageProperty method is:
193%
194% MagickBooleanType DefineImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000195% const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000196%
197% A description of each parameter follows:
198%
199% o image: the image.
200%
201% o property: the image property.
202%
cristyd15e6592011-10-15 00:13:06 +0000203% o exception: return any errors or warnings in this structure.
204%
cristy3ed852e2009-09-05 21:47:34 +0000205*/
206MagickExport MagickBooleanType DefineImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000207 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000208{
209 char
210 key[MaxTextExtent],
211 value[MaxTextExtent];
212
213 register char
214 *p;
215
216 assert(image != (Image *) NULL);
217 assert(property != (const char *) NULL);
218 (void) CopyMagickString(key,property,MaxTextExtent-1);
219 for (p=key; *p != '\0'; p++)
220 if (*p == '=')
221 break;
222 *value='\0';
223 if (*p == '=')
224 (void) CopyMagickString(value,p+1,MaxTextExtent);
225 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000226 return(SetImageProperty(image,key,value,exception));
cristy3ed852e2009-09-05 21:47:34 +0000227}
228
229/*
230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231% %
232% %
233% %
234% D e l e t e I m a g e P r o p e r t y %
235% %
236% %
237% %
238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239%
240% DeleteImageProperty() deletes an image property.
241%
242% The format of the DeleteImageProperty method is:
243%
244% MagickBooleanType DeleteImageProperty(Image *image,const char *property)
245%
246% A description of each parameter follows:
247%
248% o image: the image.
249%
250% o property: the image property.
251%
252*/
253MagickExport MagickBooleanType DeleteImageProperty(Image *image,
254 const char *property)
255{
256 assert(image != (Image *) NULL);
257 assert(image->signature == MagickSignature);
258 if (image->debug != MagickFalse)
259 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
260 image->filename);
261 if (image->properties == (void *) NULL)
262 return(MagickFalse);
263 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
264}
265
266/*
267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
268% %
269% %
270% %
271% D e s t r o y I m a g e P r o p e r t i e s %
272% %
273% %
274% %
275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
276%
277% DestroyImageProperties() releases memory associated with image property
278% values.
279%
280% The format of the DestroyDefines method is:
281%
282% void DestroyImageProperties(Image *image)
283%
284% A description of each parameter follows:
285%
286% o image: the image.
287%
288*/
289MagickExport void DestroyImageProperties(Image *image)
290{
291 assert(image != (Image *) NULL);
292 assert(image->signature == MagickSignature);
293 if (image->debug != MagickFalse)
294 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
295 image->filename);
296 if (image->properties != (void *) NULL)
297 image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
298 image->properties);
299}
300
301/*
302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
303% %
304% %
305% %
306% F o r m a t I m a g e P r o p e r t y %
307% %
308% %
309% %
310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
311%
312% FormatImageProperty() permits formatted property/value pairs to be saved as
glennrp2cc891a2010-12-24 13:44:32 +0000313% an image property.
cristy3ed852e2009-09-05 21:47:34 +0000314%
315% The format of the FormatImageProperty method is:
316%
317% MagickBooleanType FormatImageProperty(Image *image,const char *property,
318% const char *format,...)
319%
320% A description of each parameter follows.
321%
322% o image: The image.
323%
324% o property: The attribute property.
325%
326% o format: A string describing the format to use to write the remaining
327% arguments.
328%
329*/
cristydb584ae2011-05-20 14:50:53 +0000330MagickExport MagickBooleanType FormatImageProperty(Image *image,
331 const char *property,const char *format,...)
cristy3ed852e2009-09-05 21:47:34 +0000332{
333 char
334 value[MaxTextExtent];
335
cristyc82a27b2011-10-21 01:07:16 +0000336 ExceptionInfo
337 *exception;
338
339 MagickBooleanType
340 status;
341
cristy20ec7592011-05-29 01:28:05 +0000342 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000343 n;
344
cristy3ed852e2009-09-05 21:47:34 +0000345 va_list
346 operands;
347
348 va_start(operands,format);
cristydb584ae2011-05-20 14:50:53 +0000349 n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
350 (void) n;
cristy3ed852e2009-09-05 21:47:34 +0000351 va_end(operands);
cristyc82a27b2011-10-21 01:07:16 +0000352 exception=AcquireExceptionInfo();
353 status=SetImageProperty(image,property,value,exception);
354 exception=DestroyExceptionInfo(exception);
355 return(status);
cristy3ed852e2009-09-05 21:47:34 +0000356}
357
358/*
359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360% %
361% %
362% %
363% G e t I m a g e P r o p e r t y %
364% %
365% %
366% %
367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368%
369% GetImageProperty() gets a value associated with an image property.
370%
371% The format of the GetImageProperty method is:
372%
cristyd15e6592011-10-15 00:13:06 +0000373% const char *GetImageProperty(const Image *image,const char *key,
374% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000375%
376% A description of each parameter follows:
377%
378% o image: the image.
379%
380% o key: the key.
381%
cristyd15e6592011-10-15 00:13:06 +0000382% o exception: return any errors or warnings in this structure.
383%
cristy3ed852e2009-09-05 21:47:34 +0000384*/
385
386static char
cristybb503372010-05-27 20:51:26 +0000387 *TracePSClippath(const unsigned char *,size_t,const size_t,
388 const size_t),
389 *TraceSVGClippath(const unsigned char *,size_t,const size_t,
390 const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000391
cristyd15e6592011-10-15 00:13:06 +0000392static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
393 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000394{
395 char
396 *attribute,
397 *message;
398
399 const StringInfo
400 *profile;
401
cristycee97112010-05-28 00:44:52 +0000402 long
cristy3ed852e2009-09-05 21:47:34 +0000403 count,
404 dataset,
405 record;
406
cristybb503372010-05-27 20:51:26 +0000407 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000408 i;
409
410 size_t
411 length;
412
413 profile=GetImageProfile(image,"iptc");
414 if (profile == (StringInfo *) NULL)
415 profile=GetImageProfile(image,"8bim");
416 if (profile == (StringInfo *) NULL)
417 return(MagickFalse);
418 count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
419 if (count != 2)
420 return(MagickFalse);
421 attribute=(char *) NULL;
cristybb503372010-05-27 20:51:26 +0000422 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
cristy3ed852e2009-09-05 21:47:34 +0000423 {
424 length=1;
cristybb503372010-05-27 20:51:26 +0000425 if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
cristy3ed852e2009-09-05 21:47:34 +0000426 continue;
427 length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
428 length|=GetStringInfoDatum(profile)[i+4];
cristycee97112010-05-28 00:44:52 +0000429 if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
430 ((long) GetStringInfoDatum(profile)[i+2] == record))
cristy3ed852e2009-09-05 21:47:34 +0000431 {
432 message=(char *) NULL;
433 if (~length >= 1)
434 message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
435 if (message != (char *) NULL)
436 {
437 (void) CopyMagickString(message,(char *) GetStringInfoDatum(
438 profile)+i+5,length+1);
439 (void) ConcatenateString(&attribute,message);
440 (void) ConcatenateString(&attribute,";");
441 message=DestroyString(message);
442 }
443 }
444 i+=5;
445 }
446 if ((attribute == (char *) NULL) || (*attribute == ';'))
447 {
448 if (attribute != (char *) NULL)
449 attribute=DestroyString(attribute);
450 return(MagickFalse);
451 }
452 attribute[strlen(attribute)-1]='\0';
cristyd15e6592011-10-15 00:13:06 +0000453 (void) SetImageProperty((Image *) image,key,(const char *) attribute,
454 exception);
cristy3ed852e2009-09-05 21:47:34 +0000455 attribute=DestroyString(attribute);
456 return(MagickTrue);
457}
458
cristybb503372010-05-27 20:51:26 +0000459static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000460{
461 if (x > y)
462 return(x);
463 return(y);
464}
465
466static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
467{
468 int
469 c;
470
471 if (*length < 1)
472 return(EOF);
473 c=(int) (*(*p)++);
474 (*length)--;
475 return(c);
476}
477
cristybb503372010-05-27 20:51:26 +0000478static inline size_t ReadPropertyMSBLong(const unsigned char **p,
cristy3ed852e2009-09-05 21:47:34 +0000479 size_t *length)
480{
481 int
482 c;
483
cristybb503372010-05-27 20:51:26 +0000484 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000485 i;
486
487 unsigned char
488 buffer[4];
489
cristybb503372010-05-27 20:51:26 +0000490 size_t
cristy3ed852e2009-09-05 21:47:34 +0000491 value;
492
493 if (*length < 4)
494 return(~0UL);
495 for (i=0; i < 4; i++)
496 {
497 c=(int) (*(*p)++);
498 (*length)--;
499 buffer[i]=(unsigned char) c;
500 }
cristybb503372010-05-27 20:51:26 +0000501 value=(size_t) (buffer[0] << 24);
cristy3ed852e2009-09-05 21:47:34 +0000502 value|=buffer[1] << 16;
503 value|=buffer[2] << 8;
504 value|=buffer[3];
505 return(value & 0xffffffff);
506}
507
508static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
509 size_t *length)
510{
511 int
512 c;
513
cristybb503372010-05-27 20:51:26 +0000514 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000515 i;
516
517 unsigned char
518 buffer[2];
519
520 unsigned short
521 value;
522
523 if (*length < 2)
524 return((unsigned short) ~0U);
525 for (i=0; i < 2; i++)
526 {
527 c=(int) (*(*p)++);
528 (*length)--;
529 buffer[i]=(unsigned char) c;
530 }
531 value=(unsigned short) (buffer[0] << 8);
532 value|=buffer[1];
533 return((unsigned short) (value & 0xffff));
534}
535
cristyd15e6592011-10-15 00:13:06 +0000536static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
537 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000538{
539 char
540 *attribute,
541 format[MaxTextExtent],
542 name[MaxTextExtent],
543 *resource;
544
545 const StringInfo
546 *profile;
547
548 const unsigned char
549 *info;
550
cristycee97112010-05-28 00:44:52 +0000551 long
cristy3ed852e2009-09-05 21:47:34 +0000552 start,
cristycee97112010-05-28 00:44:52 +0000553 stop;
cristy3ed852e2009-09-05 21:47:34 +0000554
555 MagickBooleanType
556 status;
557
cristybb503372010-05-27 20:51:26 +0000558 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000559 i;
560
561 ssize_t
cristycee97112010-05-28 00:44:52 +0000562 count,
563 id,
564 sub_number;
cristy3ed852e2009-09-05 21:47:34 +0000565
566 size_t
567 length;
568
569 /*
glennrp2cc891a2010-12-24 13:44:32 +0000570 There are no newlines in path names, so it's safe as terminator.
cristy3ed852e2009-09-05 21:47:34 +0000571 */
572 profile=GetImageProfile(image,"8bim");
573 if (profile == (StringInfo *) NULL)
574 return(MagickFalse);
575 count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
576 format);
577 if ((count != 2) && (count != 3) && (count != 4))
578 return(MagickFalse);
579 if (count < 4)
580 (void) CopyMagickString(format,"SVG",MaxTextExtent);
581 if (count < 3)
582 *name='\0';
583 sub_number=1;
584 if (*name == '#')
cristyad740052010-07-03 01:38:03 +0000585 sub_number=(ssize_t) StringToLong(&name[1]);
cristy3ed852e2009-09-05 21:47:34 +0000586 sub_number=MagickMax(sub_number,1L);
587 resource=(char *) NULL;
588 status=MagickFalse;
589 length=GetStringInfoLength(profile);
590 info=GetStringInfoDatum(profile);
591 while ((length > 0) && (status == MagickFalse))
592 {
593 if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
594 continue;
595 if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
596 continue;
597 if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
598 continue;
599 if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
600 continue;
cristybb503372010-05-27 20:51:26 +0000601 id=(ssize_t) ReadPropertyMSBShort(&info,&length);
cristycee97112010-05-28 00:44:52 +0000602 if (id < (ssize_t) start)
cristy3ed852e2009-09-05 21:47:34 +0000603 continue;
cristycee97112010-05-28 00:44:52 +0000604 if (id > (ssize_t) stop)
cristy3ed852e2009-09-05 21:47:34 +0000605 continue;
606 if (resource != (char *) NULL)
607 resource=DestroyString(resource);
608 count=(ssize_t) ReadPropertyByte(&info,&length);
609 if ((count != 0) && ((size_t) count <= length))
610 {
611 resource=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000612 if (~(1UL*count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000613 resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
614 sizeof(*resource));
615 if (resource != (char *) NULL)
616 {
cristybb503372010-05-27 20:51:26 +0000617 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000618 resource[i]=(char) ReadPropertyByte(&info,&length);
619 resource[count]='\0';
620 }
621 }
622 if ((count & 0x01) == 0)
623 (void) ReadPropertyByte(&info,&length);
cristy55a91cd2010-12-01 00:57:40 +0000624 count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
cristy3ed852e2009-09-05 21:47:34 +0000625 if ((*name != '\0') && (*name != '#'))
626 if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
627 {
628 /*
629 No name match, scroll forward and try next.
630 */
631 info+=count;
632 length-=count;
633 continue;
634 }
635 if ((*name == '#') && (sub_number != 1))
636 {
637 /*
638 No numbered match, scroll forward and try next.
639 */
640 sub_number--;
641 info+=count;
642 length-=count;
643 continue;
644 }
645 /*
646 We have the resource of interest.
647 */
648 attribute=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000649 if (~(1UL*count) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000650 attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
651 sizeof(*attribute));
652 if (attribute != (char *) NULL)
653 {
654 (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
655 attribute[count]='\0';
656 info+=count;
657 length-=count;
658 if ((id <= 1999) || (id >= 2999))
659 (void) SetImageProperty((Image *) image,key,(const char *)
cristyd15e6592011-10-15 00:13:06 +0000660 attribute,exception);
cristy3ed852e2009-09-05 21:47:34 +0000661 else
662 {
663 char
664 *path;
665
666 if (LocaleCompare(format,"svg") == 0)
667 path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
668 image->columns,image->rows);
669 else
670 path=TracePSClippath((unsigned char *) attribute,(size_t) count,
671 image->columns,image->rows);
cristyd15e6592011-10-15 00:13:06 +0000672 (void) SetImageProperty((Image *) image,key,(const char *) path,
673 exception);
cristy3ed852e2009-09-05 21:47:34 +0000674 path=DestroyString(path);
675 }
676 attribute=DestroyString(attribute);
677 status=MagickTrue;
678 }
679 }
680 if (resource != (char *) NULL)
681 resource=DestroyString(resource);
682 return(status);
683}
684
685static inline unsigned short ReadPropertyShort(const EndianType endian,
686 const unsigned char *buffer)
687{
688 unsigned short
689 value;
690
691 if (endian == MSBEndian)
692 {
693 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
694 ((unsigned char *) buffer)[1]);
695 return((unsigned short) (value & 0xffff));
696 }
697 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
698 return((unsigned short) (value & 0xffff));
699}
700
cristybb503372010-05-27 20:51:26 +0000701static inline size_t ReadPropertyLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +0000702 const unsigned char *buffer)
703{
cristybb503372010-05-27 20:51:26 +0000704 size_t
cristy3ed852e2009-09-05 21:47:34 +0000705 value;
706
707 if (endian == MSBEndian)
708 {
cristybb503372010-05-27 20:51:26 +0000709 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +0000710 (buffer[2] << 8) | buffer[3]);
cristybb503372010-05-27 20:51:26 +0000711 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000712 }
cristybb503372010-05-27 20:51:26 +0000713 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
cristy3ed852e2009-09-05 21:47:34 +0000714 (buffer[1] << 8 ) | (buffer[0]));
cristybb503372010-05-27 20:51:26 +0000715 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +0000716}
717
718static MagickBooleanType GetEXIFProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +0000719 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000720{
721#define MaxDirectoryStack 16
722#define EXIF_DELIMITER "\n"
723#define EXIF_NUM_FORMATS 12
724#define EXIF_FMT_BYTE 1
725#define EXIF_FMT_STRING 2
726#define EXIF_FMT_USHORT 3
727#define EXIF_FMT_ULONG 4
728#define EXIF_FMT_URATIONAL 5
729#define EXIF_FMT_SBYTE 6
730#define EXIF_FMT_UNDEFINED 7
731#define EXIF_FMT_SSHORT 8
732#define EXIF_FMT_SLONG 9
733#define EXIF_FMT_SRATIONAL 10
734#define EXIF_FMT_SINGLE 11
735#define EXIF_FMT_DOUBLE 12
736#define TAG_EXIF_OFFSET 0x8769
737#define TAG_GPS_OFFSET 0x8825
738#define TAG_INTEROP_OFFSET 0xa005
739
cristy96ea4862011-11-22 00:45:47 +0000740#define EXIFMultipleValues(size,format,arg) \
cristy3ed852e2009-09-05 21:47:34 +0000741{ \
cristybb503372010-05-27 20:51:26 +0000742 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000743 component; \
744 \
745 size_t \
746 length; \
747 \
748 unsigned char \
749 *p1; \
750 \
751 length=0; \
752 p1=p; \
753 for (component=0; component < components; component++) \
754 { \
cristyb51dff52011-05-19 16:55:47 +0000755 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristy3ed852e2009-09-05 21:47:34 +0000756 format", ",arg); \
cristy37e0b382011-06-07 13:31:21 +0000757 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000758 length=MaxTextExtent-1; \
759 p1+=size; \
760 } \
761 if (length > 1) \
762 buffer[length-2]='\0'; \
763 value=AcquireString(buffer); \
764}
765
cristy96ea4862011-11-22 00:45:47 +0000766#define EXIFMultipleFractions(size,format,arg1,arg2) \
cristy3ed852e2009-09-05 21:47:34 +0000767{ \
cristybb503372010-05-27 20:51:26 +0000768 ssize_t \
cristy3ed852e2009-09-05 21:47:34 +0000769 component; \
770 \
771 size_t \
772 length; \
773 \
774 unsigned char \
775 *p1; \
776 \
777 length=0; \
778 p1=p; \
779 for (component=0; component < components; component++) \
780 { \
cristyb51dff52011-05-19 16:55:47 +0000781 length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
cristy96ea4862011-11-22 00:45:47 +0000782 format", ",arg1,arg2); \
cristy37e0b382011-06-07 13:31:21 +0000783 if (length >= (MaxTextExtent-1)) \
cristy3ed852e2009-09-05 21:47:34 +0000784 length=MaxTextExtent-1; \
785 p1+=size; \
786 } \
787 if (length > 1) \
788 buffer[length-2]='\0'; \
789 value=AcquireString(buffer); \
790}
791
792 typedef struct _DirectoryInfo
793 {
794 const unsigned char
795 *directory;
796
cristybb503372010-05-27 20:51:26 +0000797 size_t
cristy3ed852e2009-09-05 21:47:34 +0000798 entry,
799 offset;
800 } DirectoryInfo;
801
802 typedef struct _TagInfo
803 {
cristybb503372010-05-27 20:51:26 +0000804 size_t
cristy3ed852e2009-09-05 21:47:34 +0000805 tag;
806
807 const char
808 *description;
809 } TagInfo;
810
811 static TagInfo
812 EXIFTag[] =
813 {
814 { 0x001, "exif:InteroperabilityIndex" },
815 { 0x002, "exif:InteroperabilityVersion" },
816 { 0x100, "exif:ImageWidth" },
817 { 0x101, "exif:ImageLength" },
818 { 0x102, "exif:BitsPerSample" },
819 { 0x103, "exif:Compression" },
820 { 0x106, "exif:PhotometricInterpretation" },
821 { 0x10a, "exif:FillOrder" },
822 { 0x10d, "exif:DocumentName" },
823 { 0x10e, "exif:ImageDescription" },
824 { 0x10f, "exif:Make" },
825 { 0x110, "exif:Model" },
826 { 0x111, "exif:StripOffsets" },
827 { 0x112, "exif:Orientation" },
828 { 0x115, "exif:SamplesPerPixel" },
829 { 0x116, "exif:RowsPerStrip" },
830 { 0x117, "exif:StripByteCounts" },
831 { 0x11a, "exif:XResolution" },
832 { 0x11b, "exif:YResolution" },
833 { 0x11c, "exif:PlanarConfiguration" },
834 { 0x11d, "exif:PageName" },
835 { 0x11e, "exif:XPosition" },
836 { 0x11f, "exif:YPosition" },
837 { 0x118, "exif:MinSampleValue" },
838 { 0x119, "exif:MaxSampleValue" },
839 { 0x120, "exif:FreeOffsets" },
840 { 0x121, "exif:FreeByteCounts" },
841 { 0x122, "exif:GrayResponseUnit" },
842 { 0x123, "exif:GrayResponseCurve" },
843 { 0x124, "exif:T4Options" },
844 { 0x125, "exif:T6Options" },
845 { 0x128, "exif:ResolutionUnit" },
846 { 0x12d, "exif:TransferFunction" },
847 { 0x131, "exif:Software" },
848 { 0x132, "exif:DateTime" },
849 { 0x13b, "exif:Artist" },
850 { 0x13e, "exif:WhitePoint" },
851 { 0x13f, "exif:PrimaryChromaticities" },
852 { 0x140, "exif:ColorMap" },
853 { 0x141, "exif:HalfToneHints" },
854 { 0x142, "exif:TileWidth" },
855 { 0x143, "exif:TileLength" },
856 { 0x144, "exif:TileOffsets" },
857 { 0x145, "exif:TileByteCounts" },
858 { 0x14a, "exif:SubIFD" },
859 { 0x14c, "exif:InkSet" },
860 { 0x14d, "exif:InkNames" },
861 { 0x14e, "exif:NumberOfInks" },
862 { 0x150, "exif:DotRange" },
863 { 0x151, "exif:TargetPrinter" },
864 { 0x152, "exif:ExtraSample" },
865 { 0x153, "exif:SampleFormat" },
866 { 0x154, "exif:SMinSampleValue" },
867 { 0x155, "exif:SMaxSampleValue" },
868 { 0x156, "exif:TransferRange" },
869 { 0x157, "exif:ClipPath" },
870 { 0x158, "exif:XClipPathUnits" },
871 { 0x159, "exif:YClipPathUnits" },
872 { 0x15a, "exif:Indexed" },
873 { 0x15b, "exif:JPEGTables" },
874 { 0x15f, "exif:OPIProxy" },
875 { 0x200, "exif:JPEGProc" },
876 { 0x201, "exif:JPEGInterchangeFormat" },
877 { 0x202, "exif:JPEGInterchangeFormatLength" },
878 { 0x203, "exif:JPEGRestartInterval" },
879 { 0x205, "exif:JPEGLosslessPredictors" },
880 { 0x206, "exif:JPEGPointTransforms" },
881 { 0x207, "exif:JPEGQTables" },
882 { 0x208, "exif:JPEGDCTables" },
883 { 0x209, "exif:JPEGACTables" },
884 { 0x211, "exif:YCbCrCoefficients" },
885 { 0x212, "exif:YCbCrSubSampling" },
886 { 0x213, "exif:YCbCrPositioning" },
887 { 0x214, "exif:ReferenceBlackWhite" },
888 { 0x2bc, "exif:ExtensibleMetadataPlatform" },
889 { 0x301, "exif:Gamma" },
890 { 0x302, "exif:ICCProfileDescriptor" },
891 { 0x303, "exif:SRGBRenderingIntent" },
892 { 0x320, "exif:ImageTitle" },
893 { 0x5001, "exif:ResolutionXUnit" },
894 { 0x5002, "exif:ResolutionYUnit" },
895 { 0x5003, "exif:ResolutionXLengthUnit" },
896 { 0x5004, "exif:ResolutionYLengthUnit" },
897 { 0x5005, "exif:PrintFlags" },
898 { 0x5006, "exif:PrintFlagsVersion" },
899 { 0x5007, "exif:PrintFlagsCrop" },
900 { 0x5008, "exif:PrintFlagsBleedWidth" },
901 { 0x5009, "exif:PrintFlagsBleedWidthScale" },
902 { 0x500A, "exif:HalftoneLPI" },
903 { 0x500B, "exif:HalftoneLPIUnit" },
904 { 0x500C, "exif:HalftoneDegree" },
905 { 0x500D, "exif:HalftoneShape" },
906 { 0x500E, "exif:HalftoneMisc" },
907 { 0x500F, "exif:HalftoneScreen" },
908 { 0x5010, "exif:JPEGQuality" },
909 { 0x5011, "exif:GridSize" },
910 { 0x5012, "exif:ThumbnailFormat" },
911 { 0x5013, "exif:ThumbnailWidth" },
912 { 0x5014, "exif:ThumbnailHeight" },
913 { 0x5015, "exif:ThumbnailColorDepth" },
914 { 0x5016, "exif:ThumbnailPlanes" },
915 { 0x5017, "exif:ThumbnailRawBytes" },
916 { 0x5018, "exif:ThumbnailSize" },
917 { 0x5019, "exif:ThumbnailCompressedSize" },
918 { 0x501a, "exif:ColorTransferFunction" },
919 { 0x501b, "exif:ThumbnailData" },
920 { 0x5020, "exif:ThumbnailImageWidth" },
921 { 0x5021, "exif:ThumbnailImageHeight" },
922 { 0x5022, "exif:ThumbnailBitsPerSample" },
923 { 0x5023, "exif:ThumbnailCompression" },
924 { 0x5024, "exif:ThumbnailPhotometricInterp" },
925 { 0x5025, "exif:ThumbnailImageDescription" },
926 { 0x5026, "exif:ThumbnailEquipMake" },
927 { 0x5027, "exif:ThumbnailEquipModel" },
928 { 0x5028, "exif:ThumbnailStripOffsets" },
929 { 0x5029, "exif:ThumbnailOrientation" },
930 { 0x502a, "exif:ThumbnailSamplesPerPixel" },
931 { 0x502b, "exif:ThumbnailRowsPerStrip" },
932 { 0x502c, "exif:ThumbnailStripBytesCount" },
933 { 0x502d, "exif:ThumbnailResolutionX" },
934 { 0x502e, "exif:ThumbnailResolutionY" },
935 { 0x502f, "exif:ThumbnailPlanarConfig" },
936 { 0x5030, "exif:ThumbnailResolutionUnit" },
937 { 0x5031, "exif:ThumbnailTransferFunction" },
938 { 0x5032, "exif:ThumbnailSoftwareUsed" },
939 { 0x5033, "exif:ThumbnailDateTime" },
940 { 0x5034, "exif:ThumbnailArtist" },
941 { 0x5035, "exif:ThumbnailWhitePoint" },
942 { 0x5036, "exif:ThumbnailPrimaryChromaticities" },
943 { 0x5037, "exif:ThumbnailYCbCrCoefficients" },
944 { 0x5038, "exif:ThumbnailYCbCrSubsampling" },
945 { 0x5039, "exif:ThumbnailYCbCrPositioning" },
946 { 0x503A, "exif:ThumbnailRefBlackWhite" },
947 { 0x503B, "exif:ThumbnailCopyRight" },
948 { 0x5090, "exif:LuminanceTable" },
949 { 0x5091, "exif:ChrominanceTable" },
950 { 0x5100, "exif:FrameDelay" },
951 { 0x5101, "exif:LoopCount" },
952 { 0x5110, "exif:PixelUnit" },
953 { 0x5111, "exif:PixelPerUnitX" },
954 { 0x5112, "exif:PixelPerUnitY" },
955 { 0x5113, "exif:PaletteHistogram" },
956 { 0x1000, "exif:RelatedImageFileFormat" },
957 { 0x1001, "exif:RelatedImageLength" },
958 { 0x1002, "exif:RelatedImageWidth" },
959 { 0x800d, "exif:ImageID" },
960 { 0x80e3, "exif:Matteing" },
961 { 0x80e4, "exif:DataType" },
962 { 0x80e5, "exif:ImageDepth" },
963 { 0x80e6, "exif:TileDepth" },
964 { 0x828d, "exif:CFARepeatPatternDim" },
965 { 0x828e, "exif:CFAPattern2" },
966 { 0x828f, "exif:BatteryLevel" },
967 { 0x8298, "exif:Copyright" },
968 { 0x829a, "exif:ExposureTime" },
969 { 0x829d, "exif:FNumber" },
970 { 0x83bb, "exif:IPTC/NAA" },
971 { 0x84e3, "exif:IT8RasterPadding" },
972 { 0x84e5, "exif:IT8ColorTable" },
973 { 0x8649, "exif:ImageResourceInformation" },
974 { 0x8769, "exif:ExifOffset" },
975 { 0x8773, "exif:InterColorProfile" },
976 { 0x8822, "exif:ExposureProgram" },
977 { 0x8824, "exif:SpectralSensitivity" },
978 { 0x8825, "exif:GPSInfo" },
979 { 0x8827, "exif:ISOSpeedRatings" },
980 { 0x8828, "exif:OECF" },
981 { 0x8829, "exif:Interlace" },
982 { 0x882a, "exif:TimeZoneOffset" },
983 { 0x882b, "exif:SelfTimerMode" },
984 { 0x9000, "exif:ExifVersion" },
985 { 0x9003, "exif:DateTimeOriginal" },
986 { 0x9004, "exif:DateTimeDigitized" },
987 { 0x9101, "exif:ComponentsConfiguration" },
988 { 0x9102, "exif:CompressedBitsPerPixel" },
989 { 0x9201, "exif:ShutterSpeedValue" },
990 { 0x9202, "exif:ApertureValue" },
991 { 0x9203, "exif:BrightnessValue" },
992 { 0x9204, "exif:ExposureBiasValue" },
993 { 0x9205, "exif:MaxApertureValue" },
994 { 0x9206, "exif:SubjectDistance" },
995 { 0x9207, "exif:MeteringMode" },
996 { 0x9208, "exif:LightSource" },
997 { 0x9209, "exif:Flash" },
998 { 0x920a, "exif:FocalLength" },
999 { 0x920b, "exif:FlashEnergy" },
1000 { 0x920c, "exif:SpatialFrequencyResponse" },
1001 { 0x920d, "exif:Noise" },
1002 { 0x9211, "exif:ImageNumber" },
1003 { 0x9212, "exif:SecurityClassification" },
1004 { 0x9213, "exif:ImageHistory" },
1005 { 0x9214, "exif:SubjectArea" },
1006 { 0x9215, "exif:ExposureIndex" },
1007 { 0x9216, "exif:TIFF-EPStandardID" },
1008 { 0x927c, "exif:MakerNote" },
1009 { 0x9C9b, "exif:WinXP-Title" },
1010 { 0x9C9c, "exif:WinXP-Comments" },
1011 { 0x9C9d, "exif:WinXP-Author" },
1012 { 0x9C9e, "exif:WinXP-Keywords" },
1013 { 0x9C9f, "exif:WinXP-Subject" },
1014 { 0x9286, "exif:UserComment" },
1015 { 0x9290, "exif:SubSecTime" },
1016 { 0x9291, "exif:SubSecTimeOriginal" },
1017 { 0x9292, "exif:SubSecTimeDigitized" },
1018 { 0xa000, "exif:FlashPixVersion" },
1019 { 0xa001, "exif:ColorSpace" },
1020 { 0xa002, "exif:ExifImageWidth" },
1021 { 0xa003, "exif:ExifImageLength" },
1022 { 0xa004, "exif:RelatedSoundFile" },
1023 { 0xa005, "exif:InteroperabilityOffset" },
1024 { 0xa20b, "exif:FlashEnergy" },
1025 { 0xa20c, "exif:SpatialFrequencyResponse" },
1026 { 0xa20d, "exif:Noise" },
1027 { 0xa20e, "exif:FocalPlaneXResolution" },
1028 { 0xa20f, "exif:FocalPlaneYResolution" },
1029 { 0xa210, "exif:FocalPlaneResolutionUnit" },
1030 { 0xa214, "exif:SubjectLocation" },
1031 { 0xa215, "exif:ExposureIndex" },
1032 { 0xa216, "exif:TIFF/EPStandardID" },
1033 { 0xa217, "exif:SensingMethod" },
1034 { 0xa300, "exif:FileSource" },
1035 { 0xa301, "exif:SceneType" },
1036 { 0xa302, "exif:CFAPattern" },
1037 { 0xa401, "exif:CustomRendered" },
1038 { 0xa402, "exif:ExposureMode" },
1039 { 0xa403, "exif:WhiteBalance" },
1040 { 0xa404, "exif:DigitalZoomRatio" },
1041 { 0xa405, "exif:FocalLengthIn35mmFilm" },
1042 { 0xa406, "exif:SceneCaptureType" },
1043 { 0xa407, "exif:GainControl" },
1044 { 0xa408, "exif:Contrast" },
1045 { 0xa409, "exif:Saturation" },
1046 { 0xa40a, "exif:Sharpness" },
1047 { 0xa40b, "exif:DeviceSettingDescription" },
1048 { 0xa40c, "exif:SubjectDistanceRange" },
1049 { 0xa420, "exif:ImageUniqueID" },
1050 { 0xc4a5, "exif:PrintImageMatching" },
cristyb3fea0e2009-11-28 01:46:20 +00001051 { 0xa500, "exif:Gamma" },
1052 { 0xc640, "exif:CR2Slice" },
cristy3ed852e2009-09-05 21:47:34 +00001053 { 0x10000, "exif:GPSVersionID" },
1054 { 0x10001, "exif:GPSLatitudeRef" },
1055 { 0x10002, "exif:GPSLatitude" },
1056 { 0x10003, "exif:GPSLongitudeRef" },
1057 { 0x10004, "exif:GPSLongitude" },
1058 { 0x10005, "exif:GPSAltitudeRef" },
1059 { 0x10006, "exif:GPSAltitude" },
1060 { 0x10007, "exif:GPSTimeStamp" },
1061 { 0x10008, "exif:GPSSatellites" },
1062 { 0x10009, "exif:GPSStatus" },
1063 { 0x1000a, "exif:GPSMeasureMode" },
1064 { 0x1000b, "exif:GPSDop" },
1065 { 0x1000c, "exif:GPSSpeedRef" },
1066 { 0x1000d, "exif:GPSSpeed" },
1067 { 0x1000e, "exif:GPSTrackRef" },
1068 { 0x1000f, "exif:GPSTrack" },
1069 { 0x10010, "exif:GPSImgDirectionRef" },
1070 { 0x10011, "exif:GPSImgDirection" },
1071 { 0x10012, "exif:GPSMapDatum" },
1072 { 0x10013, "exif:GPSDestLatitudeRef" },
1073 { 0x10014, "exif:GPSDestLatitude" },
1074 { 0x10015, "exif:GPSDestLongitudeRef" },
1075 { 0x10016, "exif:GPSDestLongitude" },
1076 { 0x10017, "exif:GPSDestBearingRef" },
1077 { 0x10018, "exif:GPSDestBearing" },
1078 { 0x10019, "exif:GPSDestDistanceRef" },
1079 { 0x1001a, "exif:GPSDestDistance" },
1080 { 0x1001b, "exif:GPSProcessingMethod" },
1081 { 0x1001c, "exif:GPSAreaInformation" },
1082 { 0x1001d, "exif:GPSDateStamp" },
1083 { 0x1001e, "exif:GPSDifferential" },
1084 { 0x0000, NULL}
1085 };
1086
1087 const StringInfo
1088 *profile;
1089
1090 const unsigned char
1091 *directory,
1092 *exif;
1093
1094 DirectoryInfo
1095 directory_stack[MaxDirectoryStack];
1096
1097 EndianType
1098 endian;
1099
cristy929ea322011-02-21 15:21:35 +00001100 MagickBooleanType
1101 status;
cristy3ed852e2009-09-05 21:47:34 +00001102
cristybb503372010-05-27 20:51:26 +00001103 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001104 i;
1105
1106 size_t
cristy3ed852e2009-09-05 21:47:34 +00001107 entry,
cristy929ea322011-02-21 15:21:35 +00001108 length,
cristy3ed852e2009-09-05 21:47:34 +00001109 number_entries,
1110 tag_offset,
1111 tag;
1112
cristy929ea322011-02-21 15:21:35 +00001113 ssize_t
1114 all,
1115 id,
1116 level,
1117 offset,
1118 tag_value;
1119
1120 static int
1121 tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1122
cristy3ed852e2009-09-05 21:47:34 +00001123 /*
1124 If EXIF data exists, then try to parse the request for a tag.
1125 */
1126 profile=GetImageProfile(image,"exif");
1127 if (profile == (StringInfo *) NULL)
1128 return(MagickFalse);
1129 if ((property == (const char *) NULL) || (*property == '\0'))
1130 return(MagickFalse);
1131 while (isspace((int) ((unsigned char) *property)) != 0)
1132 property++;
1133 all=0;
1134 tag=(~0UL);
1135 switch (*(property+5))
1136 {
1137 case '*':
1138 {
1139 /*
1140 Caller has asked for all the tags in the EXIF data.
1141 */
1142 tag=0;
1143 all=1; /* return the data in description=value format */
1144 break;
1145 }
1146 case '!':
1147 {
1148 tag=0;
1149 all=2; /* return the data in tagid=value format */
1150 break;
1151 }
1152 case '#':
1153 case '@':
1154 {
1155 int
1156 c;
1157
1158 size_t
1159 n;
1160
1161 /*
1162 Check for a hex based tag specification first.
1163 */
1164 tag=(*(property+5) == '@') ? 1UL : 0UL;
1165 property+=6;
1166 n=strlen(property);
1167 if (n != 4)
1168 return(MagickFalse);
1169 /*
1170 Parse tag specification as a hex number.
1171 */
1172 n/=4;
1173 do
1174 {
cristybb503372010-05-27 20:51:26 +00001175 for (i=(ssize_t) n-1L; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +00001176 {
1177 c=(*property++);
1178 tag<<=4;
1179 if ((c >= '0') && (c <= '9'))
1180 tag|=(c-'0');
1181 else
1182 if ((c >= 'A') && (c <= 'F'))
1183 tag|=(c-('A'-10));
1184 else
1185 if ((c >= 'a') && (c <= 'f'))
1186 tag|=(c-('a'-10));
1187 else
1188 return(MagickFalse);
1189 }
1190 } while (*property != '\0');
1191 break;
1192 }
1193 default:
1194 {
1195 /*
1196 Try to match the text with a tag name instead.
1197 */
1198 for (i=0; ; i++)
1199 {
1200 if (EXIFTag[i].tag == 0)
1201 break;
1202 if (LocaleCompare(EXIFTag[i].description,property) == 0)
1203 {
cristybb503372010-05-27 20:51:26 +00001204 tag=(size_t) EXIFTag[i].tag;
cristy3ed852e2009-09-05 21:47:34 +00001205 break;
1206 }
1207 }
1208 break;
1209 }
1210 }
1211 if (tag == (~0UL))
1212 return(MagickFalse);
1213 length=GetStringInfoLength(profile);
1214 exif=GetStringInfoDatum(profile);
1215 while (length != 0)
1216 {
1217 if (ReadPropertyByte(&exif,&length) != 0x45)
1218 continue;
1219 if (ReadPropertyByte(&exif,&length) != 0x78)
1220 continue;
1221 if (ReadPropertyByte(&exif,&length) != 0x69)
1222 continue;
1223 if (ReadPropertyByte(&exif,&length) != 0x66)
1224 continue;
1225 if (ReadPropertyByte(&exif,&length) != 0x00)
1226 continue;
1227 if (ReadPropertyByte(&exif,&length) != 0x00)
1228 continue;
1229 break;
1230 }
1231 if (length < 16)
1232 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001233 id=(ssize_t) ReadPropertyShort(LSBEndian,exif);
cristy3ed852e2009-09-05 21:47:34 +00001234 endian=LSBEndian;
1235 if (id == 0x4949)
1236 endian=LSBEndian;
1237 else
1238 if (id == 0x4D4D)
1239 endian=MSBEndian;
1240 else
1241 return(MagickFalse);
1242 if (ReadPropertyShort(endian,exif+2) != 0x002a)
1243 return(MagickFalse);
1244 /*
1245 This the offset to the first IFD.
1246 */
cristy55a91cd2010-12-01 00:57:40 +00001247 offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
cristy3ed852e2009-09-05 21:47:34 +00001248 if ((size_t) offset >= length)
1249 return(MagickFalse);
1250 /*
1251 Set the pointer to the first IFD and follow it were it leads.
1252 */
cristy929ea322011-02-21 15:21:35 +00001253 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001254 directory=exif+offset;
1255 level=0;
1256 entry=0;
1257 tag_offset=0;
1258 do
1259 {
1260 /*
1261 If there is anything on the stack then pop it off.
1262 */
1263 if (level > 0)
1264 {
1265 level--;
1266 directory=directory_stack[level].directory;
1267 entry=directory_stack[level].entry;
1268 tag_offset=directory_stack[level].offset;
1269 }
1270 /*
1271 Determine how many entries there are in the current IFD.
1272 */
1273 number_entries=ReadPropertyShort(endian,directory);
1274 for ( ; entry < number_entries; entry++)
1275 {
cristy3ed852e2009-09-05 21:47:34 +00001276 register unsigned char
1277 *p,
1278 *q;
1279
1280 size_t
cristy9d314ff2011-03-09 01:30:28 +00001281 format,
cristy3ed852e2009-09-05 21:47:34 +00001282 number_bytes;
1283
cristy9d314ff2011-03-09 01:30:28 +00001284 ssize_t
1285 components;
cristy3ed852e2009-09-05 21:47:34 +00001286
1287 q=(unsigned char *) (directory+2+(12*entry));
cristyeaedf062010-05-29 22:36:02 +00001288 tag_value=(ssize_t) (ReadPropertyShort(endian,q)+tag_offset);
cristybb503372010-05-27 20:51:26 +00001289 format=(size_t) ReadPropertyShort(endian,q+2);
cristy3ed852e2009-09-05 21:47:34 +00001290 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1291 break;
cristy55a91cd2010-12-01 00:57:40 +00001292 components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00001293 number_bytes=(size_t) components*tag_bytes[format];
1294 if (number_bytes <= 4)
1295 p=q+8;
1296 else
1297 {
1298 ssize_t
1299 offset;
1300
1301 /*
1302 The directory entry contains an offset.
1303 */
cristy55a91cd2010-12-01 00:57:40 +00001304 offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
cristy3ed852e2009-09-05 21:47:34 +00001305 if ((size_t) (offset+number_bytes) > length)
1306 continue;
1307 p=(unsigned char *) (exif+offset);
1308 }
cristybb503372010-05-27 20:51:26 +00001309 if ((all != 0) || (tag == (size_t) tag_value))
cristy3ed852e2009-09-05 21:47:34 +00001310 {
1311 char
1312 buffer[MaxTextExtent],
1313 *value;
1314
1315 switch (format)
1316 {
1317 case EXIF_FMT_BYTE:
1318 case EXIF_FMT_UNDEFINED:
1319 {
cristy13adad02011-08-16 19:22:15 +00001320 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001321 break;
1322 }
1323 case EXIF_FMT_SBYTE:
1324 {
cristye8c25f92010-06-03 00:53:06 +00001325 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
cristy3ed852e2009-09-05 21:47:34 +00001326 break;
1327 }
1328 case EXIF_FMT_SSHORT:
1329 {
1330 EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
1331 break;
1332 }
1333 case EXIF_FMT_USHORT:
1334 {
1335 EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
1336 break;
1337 }
1338 case EXIF_FMT_ULONG:
1339 {
cristye8c25f92010-06-03 00:53:06 +00001340 EXIFMultipleValues(4,"%.20g",(double)
1341 ReadPropertyLong(endian,p1));
cristy3ed852e2009-09-05 21:47:34 +00001342 break;
1343 }
1344 case EXIF_FMT_SLONG:
1345 {
cristye8c25f92010-06-03 00:53:06 +00001346 EXIFMultipleValues(4,"%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001347 ((int) ReadPropertyLong(endian,p1)));
cristy3ed852e2009-09-05 21:47:34 +00001348 break;
1349 }
1350 case EXIF_FMT_URATIONAL:
1351 {
cristye8c25f92010-06-03 00:53:06 +00001352 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
1353 ReadPropertyLong(endian,p1),(double)
cristy3ed852e2009-09-05 21:47:34 +00001354 ReadPropertyLong(endian,p1+4));
1355 break;
1356 }
1357 case EXIF_FMT_SRATIONAL:
1358 {
cristye8c25f92010-06-03 00:53:06 +00001359 EXIFMultipleFractions(8,"%.20g/%.20g",(double)
cristy96ea4862011-11-22 00:45:47 +00001360 ((int) ReadPropertyLong(endian,p1)),(double)
1361 ((int) ReadPropertyLong(endian,p1+4)));
cristy3ed852e2009-09-05 21:47:34 +00001362 break;
1363 }
1364 case EXIF_FMT_SINGLE:
1365 {
1366 EXIFMultipleValues(4,"%f",(double) *(float *) p1);
1367 break;
1368 }
1369 case EXIF_FMT_DOUBLE:
1370 {
1371 EXIFMultipleValues(8,"%f",*(double *) p1);
1372 break;
1373 }
1374 default:
1375 case EXIF_FMT_STRING:
1376 {
1377 value=(char *) NULL;
1378 if (~(1UL*number_bytes) >= 1)
1379 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1380 sizeof(*value));
1381 if (value != (char *) NULL)
1382 {
cristybb503372010-05-27 20:51:26 +00001383 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001384 i;
1385
cristybb503372010-05-27 20:51:26 +00001386 for (i=0; i < (ssize_t) number_bytes; i++)
cristy3ed852e2009-09-05 21:47:34 +00001387 {
1388 value[i]='.';
1389 if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1390 value[i]=(char) p[i];
1391 }
1392 value[i]='\0';
1393 }
1394 break;
1395 }
1396 }
1397 if (value != (char *) NULL)
1398 {
1399 char
1400 key[MaxTextExtent];
1401
1402 register const char
1403 *p;
1404
1405 (void) CopyMagickString(key,property,MaxTextExtent);
1406 switch (all)
1407 {
1408 case 1:
1409 {
1410 const char
1411 *description;
1412
cristybb503372010-05-27 20:51:26 +00001413 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001414 i;
1415
1416 description="unknown";
1417 for (i=0; ; i++)
1418 {
1419 if (EXIFTag[i].tag == 0)
1420 break;
cristybb503372010-05-27 20:51:26 +00001421 if ((ssize_t) EXIFTag[i].tag == tag_value)
cristy3ed852e2009-09-05 21:47:34 +00001422 {
1423 description=EXIFTag[i].description;
1424 break;
1425 }
1426 }
cristy96ea4862011-11-22 00:45:47 +00001427 (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
cristy3ed852e2009-09-05 21:47:34 +00001428 break;
1429 }
1430 case 2:
1431 {
1432 if (tag_value < 0x10000)
cristyb51dff52011-05-19 16:55:47 +00001433 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001434 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001435 else
1436 if (tag_value < 0x20000)
cristyb51dff52011-05-19 16:55:47 +00001437 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001438 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001439 else
cristyb51dff52011-05-19 16:55:47 +00001440 (void) FormatLocaleString(key,MaxTextExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001441 break;
1442 }
1443 }
1444 p=(const char *) NULL;
1445 if (image->properties != (void *) NULL)
1446 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1447 image->properties,key);
1448 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001449 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001450 value=DestroyString(value);
cristy929ea322011-02-21 15:21:35 +00001451 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001452 }
1453 }
1454 if ((tag_value == TAG_EXIF_OFFSET) ||
1455 (tag_value == TAG_INTEROP_OFFSET) ||
1456 (tag_value == TAG_GPS_OFFSET))
1457 {
1458 size_t
1459 offset;
1460
1461 offset=(size_t) ReadPropertyLong(endian,p);
1462 if ((offset < length) && (level < (MaxDirectoryStack-2)))
1463 {
cristybb503372010-05-27 20:51:26 +00001464 size_t
cristy3ed852e2009-09-05 21:47:34 +00001465 tag_offset1;
1466
1467 tag_offset1=(tag_value == TAG_GPS_OFFSET) ? 0x10000UL : 0UL;
1468 directory_stack[level].directory=directory;
1469 entry++;
1470 directory_stack[level].entry=entry;
1471 directory_stack[level].offset=tag_offset;
1472 level++;
1473 directory_stack[level].directory=exif+offset;
1474 directory_stack[level].offset=tag_offset1;
1475 directory_stack[level].entry=0;
1476 level++;
1477 if ((directory+2+(12*number_entries)) > (exif+length))
1478 break;
1479 offset=(size_t) ReadPropertyLong(endian,directory+2+(12*
1480 number_entries));
1481 if ((offset != 0) && (offset < length) &&
1482 (level < (MaxDirectoryStack-2)))
1483 {
1484 directory_stack[level].directory=exif+offset;
1485 directory_stack[level].entry=0;
1486 directory_stack[level].offset=tag_offset1;
1487 level++;
1488 }
1489 }
1490 break;
1491 }
1492 }
1493 } while (level > 0);
cristy929ea322011-02-21 15:21:35 +00001494 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001495}
1496
cristy13adad02011-08-16 19:22:15 +00001497static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001498{
1499 char
1500 *xmp_profile;
1501
1502 const StringInfo
1503 *profile;
1504
1505 ExceptionInfo
1506 *exception;
1507
1508 MagickBooleanType
1509 status;
1510
1511 register const char
1512 *p;
1513
1514 XMLTreeInfo
1515 *child,
1516 *description,
1517 *node,
1518 *rdf,
1519 *xmp;
1520
1521 profile=GetImageProfile(image,"xmp");
1522 if (profile == (StringInfo *) NULL)
1523 return(MagickFalse);
1524 if ((property == (const char *) NULL) || (*property == '\0'))
1525 return(MagickFalse);
1526 xmp_profile=StringInfoToString(profile);
1527 if (xmp_profile == (char *) NULL)
1528 return(MagickFalse);
1529 for (p=xmp_profile; *p != '\0'; p++)
1530 if ((*p == '<') && (*(p+1) == 'x'))
1531 break;
1532 exception=AcquireExceptionInfo();
1533 xmp=NewXMLTree((char *) p,exception);
1534 xmp_profile=DestroyString(xmp_profile);
1535 exception=DestroyExceptionInfo(exception);
1536 if (xmp == (XMLTreeInfo *) NULL)
1537 return(MagickFalse);
1538 status=MagickFalse;
1539 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1540 if (rdf != (XMLTreeInfo *) NULL)
1541 {
1542 if (image->properties == (void *) NULL)
1543 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1544 RelinquishMagickMemory,RelinquishMagickMemory);
1545 description=GetXMLTreeChild(rdf,"rdf:Description");
1546 while (description != (XMLTreeInfo *) NULL)
1547 {
1548 node=GetXMLTreeChild(description,(const char *) NULL);
1549 while (node != (XMLTreeInfo *) NULL)
1550 {
1551 child=GetXMLTreeChild(node,(const char *) NULL);
1552 if (child == (XMLTreeInfo *) NULL)
1553 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1554 ConstantString(GetXMLTreeTag(node)),
1555 ConstantString(GetXMLTreeContent(node)));
1556 while (child != (XMLTreeInfo *) NULL)
1557 {
1558 if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
1559 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1560 ConstantString(GetXMLTreeTag(child)),
1561 ConstantString(GetXMLTreeContent(child)));
1562 child=GetXMLTreeSibling(child);
1563 }
1564 node=GetXMLTreeSibling(node);
1565 }
1566 description=GetNextXMLTreeTag(description);
1567 }
1568 }
1569 xmp=DestroyXMLTree(xmp);
1570 return(status);
1571}
1572
1573static char *TracePSClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001574 const size_t magick_unused(columns),
1575 const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001576{
1577 char
1578 *path,
1579 *message;
1580
cristy3ed852e2009-09-05 21:47:34 +00001581 MagickBooleanType
1582 in_subpath;
1583
1584 PointInfo
1585 first[3],
1586 last[3],
1587 point[3];
1588
cristybb503372010-05-27 20:51:26 +00001589 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001590 i,
1591 x;
1592
cristy9d314ff2011-03-09 01:30:28 +00001593 ssize_t
1594 knot_count,
1595 selector,
1596 y;
1597
cristy3ed852e2009-09-05 21:47:34 +00001598 path=AcquireString((char *) NULL);
1599 if (path == (char *) NULL)
1600 return((char *) NULL);
1601 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001602 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001603 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001604 (void) FormatLocaleString(message,MaxTextExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001605 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001606 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001607 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001608 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001609 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001610 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001611 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001612 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001613 " /v {currentpoint 6 2 roll curveto} bind def\n");
1614 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001615 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001616 " /y {2 copy curveto} bind def\n");
1617 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001618 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001619 " /z {closepath} bind def\n");
1620 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001621 (void) FormatLocaleString(message,MaxTextExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001622 (void) ConcatenateString(&path,message);
1623 /*
1624 The clipping path format is defined in "Adobe Photoshop File
1625 Formats Specification" version 6.0 downloadable from adobe.com.
1626 */
1627 (void) ResetMagickMemory(point,0,sizeof(point));
1628 (void) ResetMagickMemory(first,0,sizeof(first));
1629 (void) ResetMagickMemory(last,0,sizeof(last));
1630 knot_count=0;
1631 in_subpath=MagickFalse;
1632 while (length > 0)
1633 {
cristybb503372010-05-27 20:51:26 +00001634 selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001635 switch (selector)
1636 {
1637 case 0:
1638 case 3:
1639 {
1640 if (knot_count != 0)
1641 {
1642 blob+=24;
1643 length-=24;
1644 break;
1645 }
1646 /*
1647 Expected subpath length record.
1648 */
cristybb503372010-05-27 20:51:26 +00001649 knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001650 blob+=22;
1651 length-=22;
1652 break;
1653 }
1654 case 1:
1655 case 2:
1656 case 4:
1657 case 5:
1658 {
1659 if (knot_count == 0)
1660 {
1661 /*
1662 Unexpected subpath knot
1663 */
1664 blob+=24;
1665 length-=24;
1666 break;
1667 }
1668 /*
1669 Add sub-path knot
1670 */
1671 for (i=0; i < 3; i++)
1672 {
cristy13b9a2e2010-11-10 14:03:51 +00001673 size_t
cristy97433202009-10-27 02:05:08 +00001674 xx,
1675 yy;
1676
1677 yy=ReadPropertyMSBLong(&blob,&length);
1678 xx=ReadPropertyMSBLong(&blob,&length);
cristybb503372010-05-27 20:51:26 +00001679 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001680 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001681 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001682 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001683 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001684 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001685 point[i].x=(double) x/4096/4096;
1686 point[i].y=1.0-(double) y/4096/4096;
1687 }
1688 if (in_subpath == MagickFalse)
1689 {
cristyb51dff52011-05-19 16:55:47 +00001690 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001691 point[1].x,point[1].y);
1692 for (i=0; i < 3; i++)
1693 {
1694 first[i]=point[i];
1695 last[i]=point[i];
1696 }
1697 }
1698 else
1699 {
1700 /*
1701 Handle special cases when Bezier curves are used to describe
1702 corners and straight lines.
1703 */
1704 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1705 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001706 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001707 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001708 else
1709 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001710 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001711 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001712 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001713 else
1714 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001715 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001716 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001717 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001718 else
cristyb51dff52011-05-19 16:55:47 +00001719 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001720 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001721 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001722 for (i=0; i < 3; i++)
1723 last[i]=point[i];
1724 }
1725 (void) ConcatenateString(&path,message);
1726 in_subpath=MagickTrue;
1727 knot_count--;
1728 /*
1729 Close the subpath if there are no more knots.
1730 */
1731 if (knot_count == 0)
1732 {
1733 /*
1734 Same special handling as above except we compare to the
1735 first point in the path and close the path.
1736 */
1737 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1738 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001739 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001740 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001741 else
1742 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001743 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001744 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001745 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001746 else
1747 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001748 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001749 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001750 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001751 else
cristyb51dff52011-05-19 16:55:47 +00001752 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001753 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001754 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001755 (void) ConcatenateString(&path,message);
1756 in_subpath=MagickFalse;
1757 }
1758 break;
1759 }
1760 case 6:
1761 case 7:
1762 case 8:
1763 default:
1764 {
1765 blob+=24;
1766 length-=24;
1767 break;
1768 }
1769 }
1770 }
1771 /*
1772 Returns an empty PS path if the path has no knots.
1773 */
cristyb51dff52011-05-19 16:55:47 +00001774 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001775 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001776 (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001777 (void) ConcatenateString(&path,message);
1778 message=DestroyString(message);
1779 return(path);
1780}
1781
1782static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001783 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001784{
1785 char
1786 *path,
1787 *message;
1788
cristy3ed852e2009-09-05 21:47:34 +00001789 MagickBooleanType
1790 in_subpath;
1791
1792 PointInfo
1793 first[3],
1794 last[3],
1795 point[3];
1796
cristybb503372010-05-27 20:51:26 +00001797 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001798 i;
1799
cristy9d314ff2011-03-09 01:30:28 +00001800 ssize_t
1801 knot_count,
1802 selector,
1803 x,
1804 y;
1805
cristy3ed852e2009-09-05 21:47:34 +00001806 path=AcquireString((char *) NULL);
1807 if (path == (char *) NULL)
1808 return((char *) NULL);
1809 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001810 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001811 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
1812 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001813 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001814 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001815 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001816 (void) FormatLocaleString(message,MaxTextExtent,"<g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001817 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001818 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001819 "<path style=\"fill:#00000000;stroke:#00000000;");
1820 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001821 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001822 "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
1823 (void) ConcatenateString(&path,message);
1824 (void) ResetMagickMemory(point,0,sizeof(point));
1825 (void) ResetMagickMemory(first,0,sizeof(first));
1826 (void) ResetMagickMemory(last,0,sizeof(last));
1827 knot_count=0;
1828 in_subpath=MagickFalse;
1829 while (length != 0)
1830 {
cristybb503372010-05-27 20:51:26 +00001831 selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001832 switch (selector)
1833 {
1834 case 0:
1835 case 3:
1836 {
1837 if (knot_count != 0)
1838 {
1839 blob+=24;
1840 length-=24;
1841 break;
1842 }
1843 /*
1844 Expected subpath length record.
1845 */
cristybb503372010-05-27 20:51:26 +00001846 knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001847 blob+=22;
1848 length-=22;
1849 break;
1850 }
1851 case 1:
1852 case 2:
1853 case 4:
1854 case 5:
1855 {
1856 if (knot_count == 0)
1857 {
1858 /*
1859 Unexpected subpath knot.
1860 */
1861 blob+=24;
1862 length-=24;
cristy97433202009-10-27 02:05:08 +00001863 break;
1864 }
1865 /*
1866 Add sub-path knot
1867 */
1868 for (i=0; i < 3; i++)
1869 {
cristyd4982b42011-03-21 14:24:23 +00001870 size_t
cristy97433202009-10-27 02:05:08 +00001871 xx,
1872 yy;
1873
1874 yy=ReadPropertyMSBLong(&blob,&length);
1875 xx=ReadPropertyMSBLong(&blob,&length);
cristybb503372010-05-27 20:51:26 +00001876 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001877 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001878 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001879 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001880 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001881 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00001882 point[i].x=(double) x*columns/4096/4096;
1883 point[i].y=(double) y*rows/4096/4096;
1884 }
1885 if (in_subpath == MagickFalse)
1886 {
cristyb51dff52011-05-19 16:55:47 +00001887 (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001888 point[1].x,point[1].y);
1889 for (i=0; i < 3; i++)
1890 {
1891 first[i]=point[i];
1892 last[i]=point[i];
1893 }
cristy3ed852e2009-09-05 21:47:34 +00001894 }
1895 else
1896 {
cristy97433202009-10-27 02:05:08 +00001897 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1898 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001899 (void) FormatLocaleString(message,MaxTextExtent,"L %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001900 point[1].x,point[1].y);
1901 else
cristyb51dff52011-05-19 16:55:47 +00001902 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001903 "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
cristy97433202009-10-27 02:05:08 +00001904 point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001905 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00001906 last[i]=point[i];
1907 }
1908 (void) ConcatenateString(&path,message);
1909 in_subpath=MagickTrue;
1910 knot_count--;
1911 /*
1912 Close the subpath if there are no more knots.
1913 */
1914 if (knot_count == 0)
1915 {
1916 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1917 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001918 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001919 "L %g,%g Z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001920 else
1921 {
cristyb51dff52011-05-19 16:55:47 +00001922 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001923 "C %g,%g %g,%g %g,%g Z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001924 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy97433202009-10-27 02:05:08 +00001925 (void) ConcatenateString(&path,message);
cristy3ed852e2009-09-05 21:47:34 +00001926 }
cristy97433202009-10-27 02:05:08 +00001927 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001928 }
cristy97433202009-10-27 02:05:08 +00001929 break;
cristy3ed852e2009-09-05 21:47:34 +00001930 }
1931 case 6:
1932 case 7:
1933 case 8:
1934 default:
1935 {
1936 blob+=24;
1937 length-=24;
1938 break;
1939 }
1940 }
1941 }
1942 /*
1943 Return an empty SVG image if the path does not have knots.
1944 */
cristyb51dff52011-05-19 16:55:47 +00001945 (void) FormatLocaleString(message,MaxTextExtent,"\"/>\n");
cristy3ed852e2009-09-05 21:47:34 +00001946 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001947 (void) FormatLocaleString(message,MaxTextExtent,"</g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001948 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001949 (void) FormatLocaleString(message,MaxTextExtent,"</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00001950 (void) ConcatenateString(&path,message);
1951 message=DestroyString(message);
1952 return(path);
1953}
1954
1955MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00001956 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001957{
cristy3ed852e2009-09-05 21:47:34 +00001958 FxInfo
1959 *fx_info;
1960
1961 MagickRealType
1962 alpha;
1963
1964 MagickStatusType
1965 status;
1966
1967 register const char
1968 *p;
1969
1970 assert(image != (Image *) NULL);
1971 assert(image->signature == MagickSignature);
1972 if (image->debug != MagickFalse)
1973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1974 p=(const char *) NULL;
cristy27f7af22010-06-21 12:23:21 +00001975 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001976 {
cristy5eb9fc82011-02-21 15:05:41 +00001977 if (property == (const char *) NULL)
1978 {
1979 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
1980 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
1981 image->properties);
1982 return(p);
1983 }
cristyd4982b42011-03-21 14:24:23 +00001984 if (LocaleNCompare("fx:",property,3) != 0)
1985 {
1986 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1987 image->properties,property);
1988 if (p != (const char *) NULL)
1989 return(p);
1990 }
cristy3ed852e2009-09-05 21:47:34 +00001991 }
cristy5eb9fc82011-02-21 15:05:41 +00001992 if ((property == (const char *) NULL) ||
1993 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001994 return(p);
cristy3ed852e2009-09-05 21:47:34 +00001995 switch (*property)
1996 {
1997 case '8':
1998 {
1999 if (LocaleNCompare("8bim:",property,5) == 0)
2000 {
cristyd15e6592011-10-15 00:13:06 +00002001 if ((Get8BIMProperty(image,property,exception) != MagickFalse) &&
cristy5eb9fc82011-02-21 15:05:41 +00002002 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002003 {
2004 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2005 image->properties,property);
2006 return(p);
2007 }
2008 }
2009 break;
2010 }
2011 case 'E':
2012 case 'e':
2013 {
2014 if (LocaleNCompare("exif:",property,5) == 0)
2015 {
cristyd15e6592011-10-15 00:13:06 +00002016 if ((GetEXIFProperty(image,property,exception) != MagickFalse) &&
cristy5eb9fc82011-02-21 15:05:41 +00002017 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002018 {
2019 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2020 image->properties,property);
2021 return(p);
2022 }
2023 }
2024 break;
2025 }
2026 case 'F':
2027 case 'f':
2028 {
cristy27f7af22010-06-21 12:23:21 +00002029 if (LocaleNCompare("fx:",property,3) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002030 {
cristy27f7af22010-06-21 12:23:21 +00002031 fx_info=AcquireFxInfo(image,property+3);
cristy2bddff82011-07-25 18:39:12 +00002032 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
cristy0568ffc2011-07-25 16:54:14 +00002033 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002034 fx_info=DestroyFxInfo(fx_info);
2035 if (status != MagickFalse)
2036 {
2037 char
2038 value[MaxTextExtent];
2039
cristyb51dff52011-05-19 16:55:47 +00002040 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002041 GetMagickPrecision(),(double) alpha);
cristyd15e6592011-10-15 00:13:06 +00002042 (void) SetImageProperty((Image *) image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00002043 }
cristy5eb9fc82011-02-21 15:05:41 +00002044 if (image->properties != (void *) NULL)
2045 {
2046 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2047 image->properties,property);
2048 return(p);
2049 }
cristy3ed852e2009-09-05 21:47:34 +00002050 }
2051 break;
2052 }
2053 case 'I':
2054 case 'i':
2055 {
2056 if (LocaleNCompare("iptc:",property,5) == 0)
2057 {
cristyd15e6592011-10-15 00:13:06 +00002058 if ((GetIPTCProperty(image,property,exception) != MagickFalse) &&
cristy5eb9fc82011-02-21 15:05:41 +00002059 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002060 {
2061 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2062 image->properties,property);
2063 return(p);
2064 }
2065 }
2066 break;
2067 }
2068 case 'P':
2069 case 'p':
2070 {
2071 if (LocaleNCompare("pixel:",property,6) == 0)
2072 {
cristy4c08aed2011-07-01 19:47:50 +00002073 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002074 pixel;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00002077 fx_info=AcquireFxInfo(image,property+6);
cristy0568ffc2011-07-25 16:54:14 +00002078 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
2079 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002080 pixel.red=(MagickRealType) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002081 status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
2082 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002083 pixel.green=(MagickRealType) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002084 status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
2085 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002086 pixel.blue=(MagickRealType) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002087 if (image->colorspace == CMYKColorspace)
2088 {
cristy0568ffc2011-07-25 16:54:14 +00002089 status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristy3ed852e2009-09-05 21:47:34 +00002090 &alpha,exception);
cristy4c08aed2011-07-01 19:47:50 +00002091 pixel.black=(MagickRealType) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002092 }
cristy0568ffc2011-07-25 16:54:14 +00002093 status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
2094 &alpha,exception);
cristy4c08aed2011-07-01 19:47:50 +00002095 pixel.alpha=(MagickRealType) QuantumRange*(1.0-alpha);
cristy3ed852e2009-09-05 21:47:34 +00002096 fx_info=DestroyFxInfo(fx_info);
2097 if (status != MagickFalse)
2098 {
2099 char
2100 name[MaxTextExtent];
2101
cristy269c9412011-10-13 23:41:15 +00002102 (void) QueryColorname(image,&pixel,SVGCompliance,name,
cristy3ed852e2009-09-05 21:47:34 +00002103 exception);
cristyd15e6592011-10-15 00:13:06 +00002104 (void) SetImageProperty((Image *) image,property,name,exception);
2105 return(GetImageProperty(image,property,exception));
cristy3ed852e2009-09-05 21:47:34 +00002106 }
2107 }
2108 break;
2109 }
2110 case 'X':
2111 case 'x':
2112 {
2113 if (LocaleNCompare("xmp:",property,4) == 0)
2114 {
cristy5eb9fc82011-02-21 15:05:41 +00002115 if ((GetXMPProperty(image,property) != MagickFalse) &&
2116 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002117 {
2118 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2119 image->properties,property);
2120 return(p);
2121 }
2122 }
2123 break;
2124 }
cristy5eb9fc82011-02-21 15:05:41 +00002125 default:
2126 break;
cristy3ed852e2009-09-05 21:47:34 +00002127 }
2128 return(p);
2129}
2130
2131/*
2132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2133% %
2134% %
2135% %
2136+ G e t M a g i c k P r o p e r t y %
2137% %
2138% %
2139% %
2140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2141%
2142% GetMagickProperty() gets a value associated with an image property.
2143%
2144% The format of the GetMagickProperty method is:
2145%
cristyad785752011-07-27 23:13:03 +00002146% const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
cristyd15e6592011-10-15 00:13:06 +00002147% const char *key,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002148%
2149% A description of each parameter follows:
2150%
2151% o image_info: the image info.
2152%
2153% o image: the image.
2154%
2155% o key: the key.
2156%
cristyd15e6592011-10-15 00:13:06 +00002157% o exception: return any errors or warnings in this structure.
2158%
cristy3ed852e2009-09-05 21:47:34 +00002159*/
2160MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002161 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002162{
2163 char
2164 value[MaxTextExtent],
2165 filename[MaxTextExtent];
2166
2167 *value='\0';
cristyec6897c2010-05-12 00:43:15 +00002168 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002169 {
2170 case 'b':
2171 {
2172 if (LocaleNCompare("base",property,4) == 0)
2173 {
2174 GetPathComponent(image->magick_filename,BasePath,filename);
2175 (void) CopyMagickString(value,filename,MaxTextExtent);
2176 break;
2177 }
2178 break;
2179 }
2180 case 'c':
2181 {
2182 if (LocaleNCompare("channels",property,8) == 0)
2183 {
2184 /*
2185 Image channels.
2186 */
cristyb51dff52011-05-19 16:55:47 +00002187 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002188 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002189 image->colorspace));
2190 LocaleLower(value);
2191 if (image->matte != MagickFalse)
2192 (void) ConcatenateMagickString(value,"a",MaxTextExtent);
2193 break;
2194 }
2195 if (LocaleNCompare("colorspace",property,10) == 0)
2196 {
2197 ColorspaceType
2198 colorspace;
2199
2200 /*
2201 Image storage class and colorspace.
2202 */
2203 colorspace=image->colorspace;
cristyc82a27b2011-10-21 01:07:16 +00002204 if (IsImageGray(image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002205 colorspace=GRAYColorspace;
cristyb51dff52011-05-19 16:55:47 +00002206 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002207 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristyf2faecf2010-05-28 19:19:36 +00002208 colorspace));
cristy3ed852e2009-09-05 21:47:34 +00002209 break;
2210 }
cristy63054742010-09-20 12:42:28 +00002211 if (LocaleNCompare("copyright",property,9) == 0)
2212 {
2213 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
2214 break;
2215 }
cristy3ed852e2009-09-05 21:47:34 +00002216 break;
2217 }
2218 case 'd':
2219 {
2220 if (LocaleNCompare("depth",property,5) == 0)
2221 {
cristyb51dff52011-05-19 16:55:47 +00002222 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002223 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002224 break;
2225 }
2226 if (LocaleNCompare("directory",property,9) == 0)
2227 {
2228 GetPathComponent(image->magick_filename,HeadPath,filename);
2229 (void) CopyMagickString(value,filename,MaxTextExtent);
2230 break;
2231 }
2232 break;
2233 }
2234 case 'e':
2235 {
2236 if (LocaleNCompare("extension",property,9) == 0)
2237 {
2238 GetPathComponent(image->magick_filename,ExtensionPath,filename);
2239 (void) CopyMagickString(value,filename,MaxTextExtent);
2240 break;
2241 }
2242 break;
2243 }
2244 case 'g':
2245 {
2246 if (LocaleNCompare("group",property,5) == 0)
2247 {
cristyb51dff52011-05-19 16:55:47 +00002248 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",
cristyf2faecf2010-05-28 19:19:36 +00002249 (unsigned long) image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002250 break;
2251 }
2252 break;
2253 }
2254 case 'h':
2255 {
2256 if (LocaleNCompare("height",property,6) == 0)
2257 {
cristyb51dff52011-05-19 16:55:47 +00002258 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002259 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002260 break;
2261 }
2262 break;
2263 }
2264 case 'i':
2265 {
2266 if (LocaleNCompare("input",property,5) == 0)
2267 {
2268 (void) CopyMagickString(value,image->filename,MaxTextExtent);
2269 break;
2270 }
2271 break;
2272 }
2273 case 'k':
2274 {
2275 if (LocaleNCompare("kurtosis",property,8) == 0)
2276 {
2277 double
2278 kurtosis,
2279 skewness;
2280
cristyc82a27b2011-10-21 01:07:16 +00002281 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002282 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002283 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002284 break;
2285 }
2286 break;
2287 }
2288 case 'm':
2289 {
2290 if (LocaleNCompare("magick",property,6) == 0)
2291 {
2292 (void) CopyMagickString(value,image->magick,MaxTextExtent);
2293 break;
2294 }
2295 if (LocaleNCompare("max",property,3) == 0)
2296 {
2297 double
2298 maximum,
2299 minimum;
2300
cristyc82a27b2011-10-21 01:07:16 +00002301 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002302 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002303 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002304 break;
2305 }
2306 if (LocaleNCompare("mean",property,4) == 0)
2307 {
2308 double
2309 mean,
2310 standard_deviation;
2311
cristyd42d9952011-07-08 14:21:50 +00002312 (void) GetImageMean(image,&mean,&standard_deviation,
cristyc82a27b2011-10-21 01:07:16 +00002313 exception);
cristyb51dff52011-05-19 16:55:47 +00002314 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002315 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002316 break;
2317 }
2318 if (LocaleNCompare("min",property,3) == 0)
2319 {
2320 double
2321 maximum,
2322 minimum;
2323
cristyc82a27b2011-10-21 01:07:16 +00002324 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002325 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002326 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002327 break;
2328 }
2329 break;
2330 }
2331 case 'n':
2332 {
2333 if (LocaleNCompare("name",property,4) == 0)
2334 {
2335 (void) CopyMagickString(value,filename,MaxTextExtent);
2336 break;
2337 }
2338 break;
2339 }
2340 case 'o':
2341 {
cristyb0094252011-03-26 12:59:45 +00002342 if (LocaleNCompare("opaque",property,6) == 0)
2343 {
2344 MagickBooleanType
2345 opaque;
2346
cristyc82a27b2011-10-21 01:07:16 +00002347 opaque=IsImageOpaque(image,exception);
cristyb0094252011-03-26 12:59:45 +00002348 (void) CopyMagickString(value,opaque == MagickFalse ? "false" :
2349 "true",MaxTextExtent);
2350 break;
2351 }
cristy3ed852e2009-09-05 21:47:34 +00002352 if (LocaleNCompare("output",property,6) == 0)
2353 {
2354 (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
2355 break;
2356 }
2357 break;
2358 }
2359 case 'p':
2360 {
2361 if (LocaleNCompare("page",property,4) == 0)
2362 {
cristyb51dff52011-05-19 16:55:47 +00002363 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyad785752011-07-27 23:13:03 +00002364 GetImageIndexInList(image)+1);
cristy3ed852e2009-09-05 21:47:34 +00002365 break;
2366 }
2367 break;
2368 }
2369 case 's':
2370 {
2371 if (LocaleNCompare("size",property,4) == 0)
2372 {
2373 char
2374 format[MaxTextExtent];
2375
cristyb9080c92009-12-01 20:13:26 +00002376 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
cristyb51dff52011-05-19 16:55:47 +00002377 (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
cristy3ed852e2009-09-05 21:47:34 +00002378 break;
2379 }
2380 if (LocaleNCompare("scenes",property,6) == 0)
2381 {
cristyb51dff52011-05-19 16:55:47 +00002382 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002383 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00002384 break;
2385 }
2386 if (LocaleNCompare("scene",property,5) == 0)
2387 {
cristyb51dff52011-05-19 16:55:47 +00002388 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002389 image->scene);
cristy3ed852e2009-09-05 21:47:34 +00002390 if (image_info->number_scenes != 0)
cristyb51dff52011-05-19 16:55:47 +00002391 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002392 image_info->scene);
cristy3ed852e2009-09-05 21:47:34 +00002393 break;
2394 }
2395 if (LocaleNCompare("skewness",property,8) == 0)
2396 {
2397 double
2398 kurtosis,
2399 skewness;
2400
cristyc82a27b2011-10-21 01:07:16 +00002401 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002402 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002403 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002404 break;
2405 }
cristyd42d9952011-07-08 14:21:50 +00002406 if (LocaleNCompare("standard-deviation",property,18) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002407 {
2408 double
2409 mean,
2410 standard_deviation;
2411
cristyd42d9952011-07-08 14:21:50 +00002412 (void) GetImageMean(image,&mean,&standard_deviation,
cristyc82a27b2011-10-21 01:07:16 +00002413 exception);
cristyb51dff52011-05-19 16:55:47 +00002414 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002415 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00002416 break;
2417 }
2418 break;
2419 }
2420 case 'u':
2421 {
2422 if (LocaleNCompare("unique",property,6) == 0)
2423 {
2424 (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
2425 (void) CopyMagickString(value,filename,MaxTextExtent);
2426 break;
2427 }
2428 break;
2429 }
cristy63054742010-09-20 12:42:28 +00002430 case 'v':
2431 {
2432 if (LocaleNCompare("version",property,7) == 0)
2433 {
2434 (void) CopyMagickString(value,GetMagickVersion((size_t *) NULL),
2435 MaxTextExtent);
2436 break;
2437 }
2438 break;
2439 }
cristy3ed852e2009-09-05 21:47:34 +00002440 case 'w':
2441 {
2442 if (LocaleNCompare("width",property,5) == 0)
2443 {
cristyb51dff52011-05-19 16:55:47 +00002444 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002445 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00002446 break;
2447 }
2448 break;
2449 }
2450 case 'x':
2451 {
2452 if (LocaleNCompare("xresolution",property,11) == 0)
2453 {
cristyb51dff52011-05-19 16:55:47 +00002454 (void) FormatLocaleString(value,MaxTextExtent,"%g",
cristy2a11bef2011-10-28 18:33:11 +00002455 image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00002456 break;
2457 }
2458 break;
2459 }
2460 case 'y':
2461 {
2462 if (LocaleNCompare("yresolution",property,11) == 0)
2463 {
cristyb51dff52011-05-19 16:55:47 +00002464 (void) FormatLocaleString(value,MaxTextExtent,"%g",
cristy2a11bef2011-10-28 18:33:11 +00002465 image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00002466 break;
2467 }
2468 break;
2469 }
2470 case 'z':
2471 {
2472 if (LocaleNCompare("zero",property,4) == 0)
2473 {
2474 (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
2475 (void) CopyMagickString(value,filename,MaxTextExtent);
2476 break;
2477 }
2478 break;
2479 }
2480 }
2481 if (*value != '\0')
2482 {
2483 if (image->properties == (void *) NULL)
2484 image->properties=NewSplayTree(CompareSplayTreeString,
2485 RelinquishMagickMemory,RelinquishMagickMemory);
2486 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
2487 ConstantString(property),ConstantString(value));
2488 }
cristyd15e6592011-10-15 00:13:06 +00002489 return(GetImageProperty(image,property,exception));
cristy3ed852e2009-09-05 21:47:34 +00002490}
2491
2492/*
2493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2494% %
2495% %
2496% %
2497% G e t N e x t I m a g e P r o p e r t y %
2498% %
2499% %
2500% %
2501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2502%
2503% GetNextImageProperty() gets the next image property value.
2504%
2505% The format of the GetNextImageProperty method is:
2506%
2507% char *GetNextImageProperty(const Image *image)
2508%
2509% A description of each parameter follows:
2510%
2511% o image: the image.
2512%
2513*/
2514MagickExport char *GetNextImageProperty(const Image *image)
2515{
2516 assert(image != (Image *) NULL);
2517 assert(image->signature == MagickSignature);
2518 if (image->debug != MagickFalse)
2519 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2520 image->filename);
2521 if (image->properties == (void *) NULL)
2522 return((char *) NULL);
2523 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
2524}
2525
2526/*
2527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2528% %
2529% %
2530% %
2531% I n t e r p r e t I m a g e P r o p e r t i e s %
2532% %
2533% %
2534% %
2535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2536%
2537% InterpretImageProperties() replaces any embedded formatting characters with
glennrpaa588b82011-07-02 05:43:07 +00002538% the appropriate image property and returns the interpreted text.
cristy3ed852e2009-09-05 21:47:34 +00002539%
2540% The format of the InterpretImageProperties method is:
2541%
2542% char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
cristy018f07f2011-09-04 21:15:19 +00002543% const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002544%
2545% A description of each parameter follows:
2546%
2547% o image_info: the image info.
2548%
2549% o image: the image.
2550%
2551% o embed_text: the address of a character string containing the embedded
2552% formatting characters.
2553%
cristy018f07f2011-09-04 21:15:19 +00002554% o exception: return any errors or warnings in this structure.
2555%
cristy3ed852e2009-09-05 21:47:34 +00002556*/
2557MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00002558 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002559{
2560 char
2561 filename[MaxTextExtent],
2562 *interpret_text,
2563 *text;
2564
2565 const char
2566 *value;
2567
cristy3ed852e2009-09-05 21:47:34 +00002568 register char
2569 *q;
2570
2571 register const char
2572 *p;
2573
cristybb503372010-05-27 20:51:26 +00002574 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002575 i;
2576
2577 size_t
2578 extent,
2579 length;
2580
2581 assert(image != (Image *) NULL);
2582 assert(image->signature == MagickSignature);
2583 if (image->debug != MagickFalse)
2584 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2585 if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
2586 return((char *) NULL);
2587 text=(char *) embed_text;
2588 if ((*text == '@') && ((*(text+1) == '-') ||
2589 (IsPathAccessible(text+1) != MagickFalse)))
cristyc82a27b2011-10-21 01:07:16 +00002590 return(FileToString(embed_text+1,~0,exception));
cristy3ed852e2009-09-05 21:47:34 +00002591 /*
2592 Translate any embedded format characters.
2593 */
cristy3ed852e2009-09-05 21:47:34 +00002594 interpret_text=AcquireString(text);
2595 extent=MaxTextExtent;
2596 p=text;
2597 for (q=interpret_text; *p != '\0'; p++)
2598 {
2599 *q='\0';
2600 if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
2601 {
2602 extent+=MaxTextExtent;
2603 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
2604 MaxTextExtent+1,sizeof(*interpret_text));
2605 if (interpret_text == (char *) NULL)
2606 break;
2607 q=interpret_text+strlen(interpret_text);
2608 }
2609 /*
2610 Process formatting characters in text.
2611 */
2612 if ((*p == '\\') && (*(p+1) == 'r'))
2613 {
2614 *q++='\r';
2615 p++;
2616 continue;
2617 }
2618 if ((*p == '\\') && (*(p+1) == 'n'))
2619 {
2620 *q++='\n';
2621 p++;
2622 continue;
2623 }
2624 if (*p == '\\')
2625 {
2626 p++;
2627 *q++=(*p);
2628 continue;
2629 }
2630 if (*p != '%')
2631 {
2632 *q++=(*p);
2633 continue;
2634 }
2635 p++;
2636 switch (*p)
2637 {
anthony698cbb12011-10-02 12:01:40 +00002638 case '[': /* multi-character substitution */
cristy3ed852e2009-09-05 21:47:34 +00002639 {
2640 char
2641 pattern[MaxTextExtent];
2642
2643 const char
2644 *key,
2645 *value;
2646
cristybb503372010-05-27 20:51:26 +00002647 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002648 depth;
2649
2650 /*
2651 Image value.
2652 */
2653 if (strchr(p,']') == (char *) NULL)
2654 break;
2655 depth=1;
2656 p++;
2657 for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
2658 {
2659 if (*p == '[')
2660 depth++;
2661 if (*p == ']')
2662 depth--;
2663 if (depth <= 0)
2664 break;
2665 pattern[i]=(*p++);
2666 }
2667 pattern[i]='\0';
cristyd15e6592011-10-15 00:13:06 +00002668 value=GetImageProperty(image,pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002669 if (value != (const char *) NULL)
2670 {
2671 length=strlen(value);
2672 if ((size_t) (q-interpret_text+length+1) >= extent)
2673 {
2674 extent+=length;
2675 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
2676 extent+MaxTextExtent,sizeof(*interpret_text));
2677 if (interpret_text == (char *) NULL)
2678 break;
2679 q=interpret_text+strlen(interpret_text);
2680 }
2681 (void) CopyMagickString(q,value,extent);
2682 q+=length;
2683 break;
2684 }
2685 else
2686 if (IsGlob(pattern) != MagickFalse)
2687 {
2688 /*
2689 Iterate over image properties.
2690 */
2691 ResetImagePropertyIterator(image);
2692 key=GetNextImageProperty(image);
2693 while (key != (const char *) NULL)
2694 {
2695 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
2696 {
cristyd15e6592011-10-15 00:13:06 +00002697 value=GetImageProperty(image,key,exception);
cristy3ed852e2009-09-05 21:47:34 +00002698 if (value != (const char *) NULL)
2699 {
2700 length=strlen(key)+strlen(value)+2;
2701 if ((size_t) (q-interpret_text+length+1) >= extent)
2702 {
2703 extent+=length;
2704 interpret_text=(char *) ResizeQuantumMemory(
2705 interpret_text,extent+MaxTextExtent,
2706 sizeof(*interpret_text));
2707 if (interpret_text == (char *) NULL)
2708 break;
2709 q=interpret_text+strlen(interpret_text);
2710 }
cristyb51dff52011-05-19 16:55:47 +00002711 q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
cristy3ed852e2009-09-05 21:47:34 +00002712 }
2713 }
2714 key=GetNextImageProperty(image);
2715 }
2716 }
cristyd15e6592011-10-15 00:13:06 +00002717 value=GetMagickProperty(image_info,image,pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002718 if (value != (const char *) NULL)
2719 {
2720 length=strlen(value);
2721 if ((size_t) (q-interpret_text+length+1) >= extent)
2722 {
2723 extent+=length;
2724 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
2725 extent+MaxTextExtent,sizeof(*interpret_text));
2726 if (interpret_text == (char *) NULL)
2727 break;
2728 q=interpret_text+strlen(interpret_text);
2729 }
2730 (void) CopyMagickString(q,value,extent);
2731 q+=length;
cristy458b5fe2010-05-12 00:54:41 +00002732 break;
cristy3ed852e2009-09-05 21:47:34 +00002733 }
2734 if (image_info == (ImageInfo *) NULL)
2735 break;
2736 value=GetImageOption(image_info,pattern);
2737 if (value != (char *) NULL)
2738 {
2739 length=strlen(value);
2740 if ((size_t) (q-interpret_text+length+1) >= extent)
2741 {
2742 extent+=length;
2743 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
2744 extent+MaxTextExtent,sizeof(*interpret_text));
2745 if (interpret_text == (char *) NULL)
2746 break;
2747 q=interpret_text+strlen(interpret_text);
2748 }
2749 (void) CopyMagickString(q,value,extent);
2750 q+=length;
cristy458b5fe2010-05-12 00:54:41 +00002751 break;
cristy3ed852e2009-09-05 21:47:34 +00002752 }
2753 break;
2754 }
anthony698cbb12011-10-02 12:01:40 +00002755 case 'b': /* image size as read in */
2756 {
2757 char
2758 format[MaxTextExtent];
2759
2760 (void) FormatLocaleString(format,MaxTextExtent,"%.20g",(double)
2761 ((MagickOffsetType) image->extent));
2762 if (image->extent != (MagickSizeType) ((size_t) image->extent))
2763 (void) FormatMagickSize(image->extent,MagickFalse,format);
2764 q+=ConcatenateMagickString(q,format,extent);
2765 q+=ConcatenateMagickString(q,"B",extent);
2766 break;
2767 }
2768 case 'c': /* image comment properity */
2769 {
cristyd15e6592011-10-15 00:13:06 +00002770 value=GetImageProperty(image,"comment",exception);
anthony698cbb12011-10-02 12:01:40 +00002771 if (value == (const char *) NULL)
2772 break;
2773 length=strlen(value);
2774 if ((size_t) (q-interpret_text+length+1) >= extent)
2775 {
2776 extent+=length;
2777 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
2778 MaxTextExtent,sizeof(*interpret_text));
2779 if (interpret_text == (char *) NULL)
2780 break;
2781 q=interpret_text+strlen(interpret_text);
2782 }
2783 (void) CopyMagickString(q,value,extent);
2784 q+=length;
2785 break;
2786 }
2787 case 'd': /* Directory component of filename */
2788 {
2789 GetPathComponent(image->magick_filename,HeadPath,filename);
2790 q+=CopyMagickString(q,filename,extent);
2791 break;
2792 }
2793 case 'e': /* Filename extension (suffix) of image file */
2794 {
2795 GetPathComponent(image->magick_filename,ExtensionPath,filename);
2796 q+=CopyMagickString(q,filename,extent);
2797 break;
2798 }
2799 case 'f': /* Filename without directory component */
2800 {
2801 GetPathComponent(image->magick_filename,TailPath,filename);
2802 q+=CopyMagickString(q,filename,extent);
2803 break;
2804 }
2805 case 't': /* Base filename without directory or extention */
2806 {
2807 GetPathComponent(image->magick_filename,BasePath,filename);
2808 q+=CopyMagickString(q,filename,extent);
2809 break;
2810 }
2811 case 'g': /* Image geometry, canvas and offset */
2812 {
2813 q+=FormatLocaleString(q,extent,"%.20gx%.20g%+.20g%+.20g",(double)
2814 image->page.width,(double) image->page.height,(double) image->page.x,
2815 (double) image->page.y);
2816 break;
2817 }
2818 case 'h': /* Image height */
2819 {
2820 q+=FormatLocaleString(q,extent,"%.20g",(double) (image->rows != 0 ?
2821 image->rows : image->magick_rows));
2822 break;
2823 }
2824 case 'i': /* Images filename - (output filename with "info:" ) */
2825 {
2826 q+=CopyMagickString(q,image->filename,extent);
2827 break;
2828 }
2829 case 'k': /* Number of unique colors */
2830 {
2831 q+=FormatLocaleString(q,extent,"%.20g",(double) GetNumberColors(image,
cristyc82a27b2011-10-21 01:07:16 +00002832 (FILE *) NULL,exception));
anthony698cbb12011-10-02 12:01:40 +00002833 break;
2834 }
2835 case 'l': /* Image label */
2836 {
cristyd15e6592011-10-15 00:13:06 +00002837 value=GetImageProperty(image,"label",exception);
anthony698cbb12011-10-02 12:01:40 +00002838 if (value == (const char *) NULL)
2839 break;
2840 length=strlen(value);
2841 if ((size_t) (q-interpret_text+length+1) >= extent)
2842 {
2843 extent+=length;
2844 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
2845 MaxTextExtent,sizeof(*interpret_text));
2846 if (interpret_text == (char *) NULL)
2847 break;
2848 q=interpret_text+strlen(interpret_text);
2849 }
2850 q+=CopyMagickString(q,value,extent);
2851 break;
2852 }
2853 case 'm': /* Image format (file magick) */
2854 {
2855 q+=CopyMagickString(q,image->magick,extent);
2856 break;
2857 }
2858 case 'M': /* Magick filename - exactly as given incl. read mods */
2859 {
2860 q+=CopyMagickString(q,image->magick_filename,extent);
2861 break;
2862 }
2863 case 'n': /* Number of images in the list. */
2864 {
2865 q+=FormatLocaleString(q,extent,"%.20g",(double)
2866 GetImageListLength(image));
2867 break;
2868 }
2869 case 'o': /* Image output filename */
2870 {
2871 q+=CopyMagickString(q,image_info->filename,extent);
2872 break;
2873 }
2874 case 'p': /* Image index in curent image list */
2875 {
2876 q+=FormatLocaleString(q,extent,"%.20g",(double)
2877 GetImageIndexInList(image));
2878 break;
2879 }
2880 case 'q': /* Quantum depth of image in memory */
2881 {
2882 q+=FormatLocaleString(q,extent,"%.20g",(double)
2883 MAGICKCORE_QUANTUM_DEPTH);
2884 break;
2885 }
2886 case 'r': /* Image storage class and colorspace. */
2887 {
2888 ColorspaceType
2889 colorspace;
2890
2891 colorspace=image->colorspace;
cristyc82a27b2011-10-21 01:07:16 +00002892 if (IsImageGray(image,exception) != MagickFalse)
anthony698cbb12011-10-02 12:01:40 +00002893 colorspace=GRAYColorspace;
2894 q+=FormatLocaleString(q,extent,"%s%s%s",CommandOptionToMnemonic(
2895 MagickClassOptions,(ssize_t) image->storage_class),
2896 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
2897 image->matte != MagickFalse ? "Matte" : "");
2898 break;
2899 }
2900 case 's': /* Image scene number */
2901 {
2902 if (image_info->number_scenes == 0)
2903 q+=FormatLocaleString(q,extent,"%.20g",(double) image->scene);
2904 else
2905 q+=FormatLocaleString(q,extent,"%.20g",(double) image_info->scene);
2906 break;
2907 }
2908 case 'u': /* Unique filename */
2909 {
2910 (void) CopyMagickString(filename,image_info->unique,extent);
2911 q+=CopyMagickString(q,filename,extent);
2912 break;
2913 }
2914 case 'w': /* Image width */
2915 {
2916 q+=FormatLocaleString(q,extent,"%.20g",(double) (image->columns != 0 ?
2917 image->columns : image->magick_columns));
2918 break;
2919 }
2920 case 'x': /* Image horizontal resolution (density). */
2921 {
cristy2a11bef2011-10-28 18:33:11 +00002922 q+=FormatLocaleString(q,extent,"%g %s",image->resolution.x,
anthony698cbb12011-10-02 12:01:40 +00002923 CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
2924 image->units));
2925 break;
2926 }
2927 case 'y': /* Image vertical resolution (density) */
2928 {
cristy2a11bef2011-10-28 18:33:11 +00002929 q+=FormatLocaleString(q,extent,"%g %s",image->resolution.y,
anthony698cbb12011-10-02 12:01:40 +00002930 CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
2931 image->units));
2932 break;
2933 }
2934 case 'z': /* Image depth as read in */
2935 {
2936 q+=FormatLocaleString(q,extent,"%.20g",(double) image->depth);
2937 break;
2938 }
2939 case 'A': /* Image alpha channel */
2940 {
2941 q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
2942 MagickBooleanOptions,(ssize_t) image->matte));
2943 break;
2944 }
2945 case 'C': /* Image compression method. */
2946 {
2947 q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
2948 MagickCompressOptions,(ssize_t) image->compression));
2949 break;
2950 }
2951 case 'D': /* Image dispose method. */
2952 {
2953 q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
2954 MagickDisposeOptions,(ssize_t) image->dispose));
2955 break;
2956 }
2957 case 'G': /* Image size as geometry = "%wx%h" */
2958 {
2959 q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double)
2960 image->magick_columns,(double) image->magick_rows);
2961 break;
2962 }
2963 case 'H': /* layer canvas height */
2964 {
2965 q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.height);
2966 break;
2967 }
2968 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2969 {
2970 q+=FormatLocaleString(q,extent,"%+ld%+ld",(long) image->page.x,(long)
2971 image->page.y);
2972 break;
2973 }
2974 case 'P': /* layer canvas page size = "%Wx%H" */
2975 {
2976 q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double) image->page.width,
2977 (double) image->page.height);
2978 break;
2979 }
2980 case 'Q': /* image compression quality */
2981 {
2982 q+=FormatLocaleString(q,extent,"%.20g",(double) image->quality);
2983 break;
2984 }
2985 case 'S': /* Image scenes */
2986 {
2987 if (image_info->number_scenes == 0)
2988 q+=CopyMagickString(q,"2147483647",extent);
2989 else
2990 q+=FormatLocaleString(q,extent,"%.20g",(double) (image_info->scene+
2991 image_info->number_scenes));
2992 break;
2993 }
2994 case 'T': /* image time delay for animations */
2995 {
2996 q+=FormatLocaleString(q,extent,"%.20g",(double) image->delay);
2997 break;
2998 }
2999 case 'W': /* layer canvas width */
3000 {
3001 q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.width);
3002 break;
3003 }
3004 case 'X': /* layer canvas X offset */
3005 {
3006 q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.x);
3007 break;
3008 }
3009 case 'Y': /* layer canvas Y offset */
3010 {
3011 q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.y);
3012 break;
3013 }
3014 case 'Z': /* Unique filename. */
3015 {
3016 (void) CopyMagickString(filename,image_info->zero,extent);
3017 q+=CopyMagickString(q,filename,extent);
3018 break;
3019 }
3020 case '@': /* Image bounding box. */
cristy3ed852e2009-09-05 21:47:34 +00003021 {
3022 RectangleInfo
3023 page;
3024
cristy018f07f2011-09-04 21:15:19 +00003025 page=GetImageBoundingBox(image,exception);
cristyb51dff52011-05-19 16:55:47 +00003026 q+=FormatLocaleString(q,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristye8c25f92010-06-03 00:53:06 +00003027 (double) page.width,(double) page.height,(double) page.x,(double)
3028 page.y);
cristy3ed852e2009-09-05 21:47:34 +00003029 break;
3030 }
anthony698cbb12011-10-02 12:01:40 +00003031 case '#': /* Image signature */
cristy3ed852e2009-09-05 21:47:34 +00003032 {
cristy018f07f2011-09-04 21:15:19 +00003033 (void) SignatureImage(image,exception);
cristyd15e6592011-10-15 00:13:06 +00003034 value=GetImageProperty(image,"signature",exception);
cristy3ed852e2009-09-05 21:47:34 +00003035 if (value == (const char *) NULL)
3036 break;
3037 q+=CopyMagickString(q,value,extent);
3038 break;
3039 }
anthony698cbb12011-10-02 12:01:40 +00003040 case '%': /* percent escaped */
cristy3ed852e2009-09-05 21:47:34 +00003041 {
3042 *q++=(*p);
3043 break;
3044 }
anthony698cbb12011-10-02 12:01:40 +00003045 default: /* percent not expanded */
cristy3ed852e2009-09-05 21:47:34 +00003046 {
3047 *q++='%';
3048 *q++=(*p);
3049 break;
3050 }
3051 }
3052 }
3053 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003054 if (text != (const char *) embed_text)
3055 text=DestroyString(text);
3056 (void) SubstituteString(&interpret_text,"&lt;","<");
3057 (void) SubstituteString(&interpret_text,"&gt;",">");
3058 return(interpret_text);
3059}
3060
3061/*
3062%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3063% %
3064% %
3065% %
3066% R e m o v e I m a g e P r o p e r t y %
3067% %
3068% %
3069% %
3070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3071%
3072% RemoveImageProperty() removes a property from the image and returns its
3073% value.
3074%
3075% The format of the RemoveImageProperty method is:
3076%
3077% char *RemoveImageProperty(Image *image,const char *property)
3078%
3079% A description of each parameter follows:
3080%
3081% o image: the image.
3082%
3083% o property: the image property.
3084%
3085*/
3086MagickExport char *RemoveImageProperty(Image *image,
3087 const char *property)
3088{
3089 char
3090 *value;
3091
3092 assert(image != (Image *) NULL);
3093 assert(image->signature == MagickSignature);
3094 if (image->debug != MagickFalse)
3095 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3096 image->filename);
3097 if (image->properties == (void *) NULL)
3098 return((char *) NULL);
3099 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3100 property);
3101 return(value);
3102}
3103
3104/*
3105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3106% %
3107% %
3108% %
3109% 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 %
3110% %
3111% %
3112% %
3113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3114%
3115% ResetImagePropertyIterator() resets the image properties iterator. Use it
3116% in conjunction with GetNextImageProperty() to iterate over all the values
3117% associated with an image property.
3118%
3119% The format of the ResetImagePropertyIterator method is:
3120%
3121% ResetImagePropertyIterator(Image *image)
3122%
3123% A description of each parameter follows:
3124%
3125% o image: the image.
3126%
3127*/
3128MagickExport void ResetImagePropertyIterator(const Image *image)
3129{
3130 assert(image != (Image *) NULL);
3131 assert(image->signature == MagickSignature);
3132 if (image->debug != MagickFalse)
3133 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3134 image->filename);
3135 if (image->properties == (void *) NULL)
3136 return;
3137 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3138}
3139
3140/*
3141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3142% %
3143% %
3144% %
3145% S e t I m a g e P r o p e r t y %
3146% %
3147% %
3148% %
3149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3150%
3151% SetImageProperty() associates an value with an image property.
3152%
3153% The format of the SetImageProperty method is:
3154%
3155% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003156% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003157%
3158% A description of each parameter follows:
3159%
3160% o image: the image.
3161%
3162% o property: the image property.
3163%
3164% o values: the image property values.
3165%
cristyd15e6592011-10-15 00:13:06 +00003166% o exception: return any errors or warnings in this structure.
3167%
cristy3ed852e2009-09-05 21:47:34 +00003168*/
3169MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003170 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003171{
3172 MagickBooleanType
3173 status;
3174
3175 MagickStatusType
3176 flags;
3177
3178 assert(image != (Image *) NULL);
3179 assert(image->signature == MagickSignature);
3180 if (image->debug != MagickFalse)
3181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3182 image->filename);
3183 if (image->properties == (void *) NULL)
3184 image->properties=NewSplayTree(CompareSplayTreeString,
3185 RelinquishMagickMemory,RelinquishMagickMemory);
3186 if ((value == (const char *) NULL) || (*value == '\0'))
3187 return(DeleteImageProperty(image,property));
3188 status=MagickTrue;
3189 switch (*property)
3190 {
3191 case 'B':
3192 case 'b':
3193 {
cristyc6c08ab2010-07-24 23:50:09 +00003194 if (LocaleCompare(property,"background") == 0)
3195 {
cristy9950d572011-10-01 18:22:35 +00003196 (void) QueryColorCompliance(value,AllCompliance,
3197 &image->background_color,exception);
cristyc6c08ab2010-07-24 23:50:09 +00003198 break;
3199 }
cristy3ed852e2009-09-05 21:47:34 +00003200 if (LocaleCompare(property,"bias") == 0)
3201 {
cristy9b34e302011-11-05 02:15:45 +00003202 image->bias=StringToDoubleInterval(value,(double) QuantumRange+1.0);
cristy3ed852e2009-09-05 21:47:34 +00003203 break;
3204 }
3205 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3206 ConstantString(property),ConstantString(value));
3207 break;
3208 }
3209 case 'C':
3210 case 'c':
3211 {
3212 if (LocaleCompare(property,"colorspace") == 0)
3213 {
cristybb503372010-05-27 20:51:26 +00003214 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003215 colorspace;
3216
cristy042ee782011-04-22 18:48:30 +00003217 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003218 value);
3219 if (colorspace < 0)
3220 break;
cristy63240882011-08-05 19:05:27 +00003221 (void) SetImageColorspace(image,(ColorspaceType) colorspace,
3222 exception);
cristy3ed852e2009-09-05 21:47:34 +00003223 break;
3224 }
3225 if (LocaleCompare(property,"compose") == 0)
3226 {
cristybb503372010-05-27 20:51:26 +00003227 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003228 compose;
3229
cristy042ee782011-04-22 18:48:30 +00003230 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003231 if (compose < 0)
3232 break;
3233 image->compose=(CompositeOperator) compose;
3234 break;
3235 }
3236 if (LocaleCompare(property,"compress") == 0)
3237 {
cristybb503372010-05-27 20:51:26 +00003238 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003239 compression;
3240
cristy042ee782011-04-22 18:48:30 +00003241 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003242 value);
3243 if (compression < 0)
3244 break;
3245 image->compression=(CompressionType) compression;
3246 break;
3247 }
3248 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3249 ConstantString(property),ConstantString(value));
3250 break;
3251 }
3252 case 'D':
3253 case 'd':
3254 {
3255 if (LocaleCompare(property,"delay") == 0)
3256 {
3257 GeometryInfo
3258 geometry_info;
3259
3260 flags=ParseGeometry(value,&geometry_info);
3261 if ((flags & GreaterValue) != 0)
3262 {
cristybb503372010-05-27 20:51:26 +00003263 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3264 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003265 }
3266 else
3267 if ((flags & LessValue) != 0)
3268 {
cristybb503372010-05-27 20:51:26 +00003269 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
cristyc6c08ab2010-07-24 23:50:09 +00003270 image->ticks_per_second=(ssize_t)
3271 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003272 }
3273 else
cristybb503372010-05-27 20:51:26 +00003274 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003275 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003276 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003277 break;
3278 }
cristyfbb56842010-08-02 11:26:33 +00003279 if (LocaleCompare(property,"density") == 0)
3280 {
3281 GeometryInfo
3282 geometry_info;
3283
3284 flags=ParseGeometry(value,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +00003285 image->resolution.x=geometry_info.rho;
3286 image->resolution.y=geometry_info.sigma;
cristyfbb56842010-08-02 11:26:33 +00003287 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +00003288 image->resolution.y=image->resolution.x;
cristyfbb56842010-08-02 11:26:33 +00003289 }
cristy3ed852e2009-09-05 21:47:34 +00003290 if (LocaleCompare(property,"depth") == 0)
3291 {
cristye27293e2009-12-18 02:53:20 +00003292 image->depth=StringToUnsignedLong(value);
cristy3ed852e2009-09-05 21:47:34 +00003293 break;
3294 }
3295 if (LocaleCompare(property,"dispose") == 0)
3296 {
cristybb503372010-05-27 20:51:26 +00003297 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003298 dispose;
3299
cristy042ee782011-04-22 18:48:30 +00003300 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003301 if (dispose < 0)
3302 break;
3303 image->dispose=(DisposeType) dispose;
3304 break;
3305 }
3306 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3307 ConstantString(property),ConstantString(value));
3308 break;
3309 }
3310 case 'G':
3311 case 'g':
3312 {
3313 if (LocaleCompare(property,"gravity") == 0)
3314 {
cristybb503372010-05-27 20:51:26 +00003315 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003316 gravity;
3317
cristy042ee782011-04-22 18:48:30 +00003318 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003319 if (gravity < 0)
3320 break;
3321 image->gravity=(GravityType) gravity;
3322 break;
3323 }
3324 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3325 ConstantString(property),ConstantString(value));
3326 break;
3327 }
3328 case 'I':
3329 case 'i':
3330 {
3331 if (LocaleCompare(property,"intent") == 0)
3332 {
cristybb503372010-05-27 20:51:26 +00003333 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003334 rendering_intent;
3335
cristy042ee782011-04-22 18:48:30 +00003336 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003337 value);
3338 if (rendering_intent < 0)
3339 break;
3340 image->rendering_intent=(RenderingIntent) rendering_intent;
3341 break;
3342 }
3343 if (LocaleCompare(property,"interpolate") == 0)
3344 {
cristybb503372010-05-27 20:51:26 +00003345 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003346 interpolate;
3347
cristy042ee782011-04-22 18:48:30 +00003348 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003349 value);
3350 if (interpolate < 0)
3351 break;
cristy5c4e2582011-09-11 19:21:03 +00003352 image->interpolate=(PixelInterpolateMethod) interpolate;
cristy3ed852e2009-09-05 21:47:34 +00003353 break;
3354 }
3355 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3356 ConstantString(property),ConstantString(value));
3357 break;
3358 }
3359 case 'L':
3360 case 'l':
3361 {
3362 if (LocaleCompare(property,"loop") == 0)
3363 {
cristye27293e2009-12-18 02:53:20 +00003364 image->iterations=StringToUnsignedLong(value);
cristy3ed852e2009-09-05 21:47:34 +00003365 break;
3366 }
3367 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3368 ConstantString(property),ConstantString(value));
3369 break;
3370 }
3371 case 'P':
3372 case 'p':
3373 {
3374 if (LocaleCompare(property,"page") == 0)
3375 {
3376 char
3377 *geometry;
3378
3379 geometry=GetPageGeometry(value);
3380 flags=ParseAbsoluteGeometry(geometry,&image->page);
3381 geometry=DestroyString(geometry);
3382 break;
3383 }
cristy071dd7b2010-04-09 13:04:54 +00003384 if (LocaleCompare(property,"profile") == 0)
3385 {
3386 ImageInfo
3387 *image_info;
3388
3389 StringInfo
3390 *profile;
3391
3392 image_info=AcquireImageInfo();
3393 (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
cristyc6c08ab2010-07-24 23:50:09 +00003394 (void) SetImageInfo(image_info,1,exception);
3395 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00003396 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003397 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00003398 image_info=DestroyImageInfo(image_info);
3399 break;
3400 }
cristy3ed852e2009-09-05 21:47:34 +00003401 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3402 ConstantString(property),ConstantString(value));
3403 break;
3404 }
3405 case 'R':
3406 case 'r':
3407 {
3408 if (LocaleCompare(property,"rendering-intent") == 0)
3409 {
cristybb503372010-05-27 20:51:26 +00003410 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003411 rendering_intent;
3412
cristy042ee782011-04-22 18:48:30 +00003413 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003414 value);
3415 if (rendering_intent < 0)
3416 break;
3417 image->rendering_intent=(RenderingIntent) rendering_intent;
3418 break;
3419 }
3420 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3421 ConstantString(property),ConstantString(value));
3422 break;
3423 }
3424 case 'T':
3425 case 't':
3426 {
3427 if (LocaleCompare(property,"tile-offset") == 0)
3428 {
3429 char
3430 *geometry;
3431
3432 geometry=GetPageGeometry(value);
3433 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
3434 geometry=DestroyString(geometry);
3435 break;
3436 }
3437 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3438 ConstantString(property),ConstantString(value));
3439 break;
3440 }
cristyfbb56842010-08-02 11:26:33 +00003441 case 'U':
3442 case 'u':
3443 {
3444 if (LocaleCompare(property,"units") == 0)
3445 {
3446 ssize_t
3447 units;
3448
cristy042ee782011-04-22 18:48:30 +00003449 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00003450 if (units < 0)
3451 break;
3452 image->units=(ResolutionType) units;
3453 break;
3454 }
3455 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3456 ConstantString(property),ConstantString(value));
3457 break;
3458 }
cristy3ed852e2009-09-05 21:47:34 +00003459 default:
3460 {
3461 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3462 ConstantString(property),ConstantString(value));
3463 break;
3464 }
3465 }
3466 return(status);
3467}