blob: 2e54e20ddc25879d9a21dfda9d44ae225e822532 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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;
141 image->x_resolution=clone_image->x_resolution;
142 image->y_resolution=clone_image->y_resolution;
143 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
740#define EXIFMultipleValues(size, format, arg) \
741{ \
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
766#define EXIFMultipleFractions(size, format, arg1, arg2) \
767{ \
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, \
cristy3ed852e2009-09-05 21:47:34 +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)
1347 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)
1360 ReadPropertyLong(endian,p1),(double)
cristy3ed852e2009-09-05 21:47:34 +00001361 ReadPropertyLong(endian,p1+4));
1362 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 }
cristyb51dff52011-05-19 16:55:47 +00001427 (void) FormatLocaleString(key,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00001428 description);
1429 break;
1430 }
1431 case 2:
1432 {
1433 if (tag_value < 0x10000)
cristyb51dff52011-05-19 16:55:47 +00001434 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001435 (unsigned long) tag_value);
cristy3ed852e2009-09-05 21:47:34 +00001436 else
1437 if (tag_value < 0x20000)
cristyb51dff52011-05-19 16:55:47 +00001438 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
cristyf2faecf2010-05-28 19:19:36 +00001439 (unsigned long) (tag_value & 0xffff));
cristy3ed852e2009-09-05 21:47:34 +00001440 else
cristyb51dff52011-05-19 16:55:47 +00001441 (void) FormatLocaleString(key,MaxTextExtent,"unknown");
cristy3ed852e2009-09-05 21:47:34 +00001442 break;
1443 }
1444 }
1445 p=(const char *) NULL;
1446 if (image->properties != (void *) NULL)
1447 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1448 image->properties,key);
1449 if (p == (const char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00001450 (void) SetImageProperty((Image *) image,key,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001451 value=DestroyString(value);
cristy929ea322011-02-21 15:21:35 +00001452 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001453 }
1454 }
1455 if ((tag_value == TAG_EXIF_OFFSET) ||
1456 (tag_value == TAG_INTEROP_OFFSET) ||
1457 (tag_value == TAG_GPS_OFFSET))
1458 {
1459 size_t
1460 offset;
1461
1462 offset=(size_t) ReadPropertyLong(endian,p);
1463 if ((offset < length) && (level < (MaxDirectoryStack-2)))
1464 {
cristybb503372010-05-27 20:51:26 +00001465 size_t
cristy3ed852e2009-09-05 21:47:34 +00001466 tag_offset1;
1467
1468 tag_offset1=(tag_value == TAG_GPS_OFFSET) ? 0x10000UL : 0UL;
1469 directory_stack[level].directory=directory;
1470 entry++;
1471 directory_stack[level].entry=entry;
1472 directory_stack[level].offset=tag_offset;
1473 level++;
1474 directory_stack[level].directory=exif+offset;
1475 directory_stack[level].offset=tag_offset1;
1476 directory_stack[level].entry=0;
1477 level++;
1478 if ((directory+2+(12*number_entries)) > (exif+length))
1479 break;
1480 offset=(size_t) ReadPropertyLong(endian,directory+2+(12*
1481 number_entries));
1482 if ((offset != 0) && (offset < length) &&
1483 (level < (MaxDirectoryStack-2)))
1484 {
1485 directory_stack[level].directory=exif+offset;
1486 directory_stack[level].entry=0;
1487 directory_stack[level].offset=tag_offset1;
1488 level++;
1489 }
1490 }
1491 break;
1492 }
1493 }
1494 } while (level > 0);
cristy929ea322011-02-21 15:21:35 +00001495 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001496}
1497
cristy13adad02011-08-16 19:22:15 +00001498static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
cristy3ed852e2009-09-05 21:47:34 +00001499{
1500 char
1501 *xmp_profile;
1502
1503 const StringInfo
1504 *profile;
1505
1506 ExceptionInfo
1507 *exception;
1508
1509 MagickBooleanType
1510 status;
1511
1512 register const char
1513 *p;
1514
1515 XMLTreeInfo
1516 *child,
1517 *description,
1518 *node,
1519 *rdf,
1520 *xmp;
1521
1522 profile=GetImageProfile(image,"xmp");
1523 if (profile == (StringInfo *) NULL)
1524 return(MagickFalse);
1525 if ((property == (const char *) NULL) || (*property == '\0'))
1526 return(MagickFalse);
1527 xmp_profile=StringInfoToString(profile);
1528 if (xmp_profile == (char *) NULL)
1529 return(MagickFalse);
1530 for (p=xmp_profile; *p != '\0'; p++)
1531 if ((*p == '<') && (*(p+1) == 'x'))
1532 break;
1533 exception=AcquireExceptionInfo();
1534 xmp=NewXMLTree((char *) p,exception);
1535 xmp_profile=DestroyString(xmp_profile);
1536 exception=DestroyExceptionInfo(exception);
1537 if (xmp == (XMLTreeInfo *) NULL)
1538 return(MagickFalse);
1539 status=MagickFalse;
1540 rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1541 if (rdf != (XMLTreeInfo *) NULL)
1542 {
1543 if (image->properties == (void *) NULL)
1544 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1545 RelinquishMagickMemory,RelinquishMagickMemory);
1546 description=GetXMLTreeChild(rdf,"rdf:Description");
1547 while (description != (XMLTreeInfo *) NULL)
1548 {
1549 node=GetXMLTreeChild(description,(const char *) NULL);
1550 while (node != (XMLTreeInfo *) NULL)
1551 {
1552 child=GetXMLTreeChild(node,(const char *) NULL);
1553 if (child == (XMLTreeInfo *) NULL)
1554 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1555 ConstantString(GetXMLTreeTag(node)),
1556 ConstantString(GetXMLTreeContent(node)));
1557 while (child != (XMLTreeInfo *) NULL)
1558 {
1559 if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
1560 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1561 ConstantString(GetXMLTreeTag(child)),
1562 ConstantString(GetXMLTreeContent(child)));
1563 child=GetXMLTreeSibling(child);
1564 }
1565 node=GetXMLTreeSibling(node);
1566 }
1567 description=GetNextXMLTreeTag(description);
1568 }
1569 }
1570 xmp=DestroyXMLTree(xmp);
1571 return(status);
1572}
1573
1574static char *TracePSClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001575 const size_t magick_unused(columns),
1576 const size_t magick_unused(rows))
cristy3ed852e2009-09-05 21:47:34 +00001577{
1578 char
1579 *path,
1580 *message;
1581
cristy3ed852e2009-09-05 21:47:34 +00001582 MagickBooleanType
1583 in_subpath;
1584
1585 PointInfo
1586 first[3],
1587 last[3],
1588 point[3];
1589
cristybb503372010-05-27 20:51:26 +00001590 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001591 i,
1592 x;
1593
cristy9d314ff2011-03-09 01:30:28 +00001594 ssize_t
1595 knot_count,
1596 selector,
1597 y;
1598
cristy3ed852e2009-09-05 21:47:34 +00001599 path=AcquireString((char *) NULL);
1600 if (path == (char *) NULL)
1601 return((char *) NULL);
1602 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001603 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
cristy3ed852e2009-09-05 21:47:34 +00001604 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001605 (void) FormatLocaleString(message,MaxTextExtent,"{\n");
cristy3ed852e2009-09-05 21:47:34 +00001606 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001607 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001608 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001609 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001610 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001611 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n");
cristy3ed852e2009-09-05 21:47:34 +00001612 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001613 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001614 " /v {currentpoint 6 2 roll curveto} bind def\n");
1615 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001616 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001617 " /y {2 copy curveto} bind def\n");
1618 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001619 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001620 " /z {closepath} bind def\n");
1621 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001622 (void) FormatLocaleString(message,MaxTextExtent," newpath\n");
cristy3ed852e2009-09-05 21:47:34 +00001623 (void) ConcatenateString(&path,message);
1624 /*
1625 The clipping path format is defined in "Adobe Photoshop File
1626 Formats Specification" version 6.0 downloadable from adobe.com.
1627 */
1628 (void) ResetMagickMemory(point,0,sizeof(point));
1629 (void) ResetMagickMemory(first,0,sizeof(first));
1630 (void) ResetMagickMemory(last,0,sizeof(last));
1631 knot_count=0;
1632 in_subpath=MagickFalse;
1633 while (length > 0)
1634 {
cristybb503372010-05-27 20:51:26 +00001635 selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001636 switch (selector)
1637 {
1638 case 0:
1639 case 3:
1640 {
1641 if (knot_count != 0)
1642 {
1643 blob+=24;
1644 length-=24;
1645 break;
1646 }
1647 /*
1648 Expected subpath length record.
1649 */
cristybb503372010-05-27 20:51:26 +00001650 knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001651 blob+=22;
1652 length-=22;
1653 break;
1654 }
1655 case 1:
1656 case 2:
1657 case 4:
1658 case 5:
1659 {
1660 if (knot_count == 0)
1661 {
1662 /*
1663 Unexpected subpath knot
1664 */
1665 blob+=24;
1666 length-=24;
1667 break;
1668 }
1669 /*
1670 Add sub-path knot
1671 */
1672 for (i=0; i < 3; i++)
1673 {
cristy13b9a2e2010-11-10 14:03:51 +00001674 size_t
cristy97433202009-10-27 02:05:08 +00001675 xx,
1676 yy;
1677
1678 yy=ReadPropertyMSBLong(&blob,&length);
1679 xx=ReadPropertyMSBLong(&blob,&length);
cristybb503372010-05-27 20:51:26 +00001680 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001681 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001682 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001683 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001684 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001685 y=(ssize_t) yy-4294967295U-1;
cristy3ed852e2009-09-05 21:47:34 +00001686 point[i].x=(double) x/4096/4096;
1687 point[i].y=1.0-(double) y/4096/4096;
1688 }
1689 if (in_subpath == MagickFalse)
1690 {
cristyb51dff52011-05-19 16:55:47 +00001691 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n",
cristy3ed852e2009-09-05 21:47:34 +00001692 point[1].x,point[1].y);
1693 for (i=0; i < 3; i++)
1694 {
1695 first[i]=point[i];
1696 last[i]=point[i];
1697 }
1698 }
1699 else
1700 {
1701 /*
1702 Handle special cases when Bezier curves are used to describe
1703 corners and straight lines.
1704 */
1705 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1706 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001707 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001708 " %g %g l\n",point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001709 else
1710 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001711 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001712 " %g %g %g %g v\n",point[0].x,point[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001713 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001714 else
1715 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001716 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001717 " %g %g %g %g y\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001718 point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001719 else
cristyb51dff52011-05-19 16:55:47 +00001720 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001721 " %g %g %g %g %g %g c\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001722 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001723 for (i=0; i < 3; i++)
1724 last[i]=point[i];
1725 }
1726 (void) ConcatenateString(&path,message);
1727 in_subpath=MagickTrue;
1728 knot_count--;
1729 /*
1730 Close the subpath if there are no more knots.
1731 */
1732 if (knot_count == 0)
1733 {
1734 /*
1735 Same special handling as above except we compare to the
1736 first point in the path and close the path.
1737 */
1738 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1739 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001740 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001741 " %g %g l z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001742 else
1743 if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
cristyb51dff52011-05-19 16:55:47 +00001744 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001745 " %g %g %g %g v z\n",first[0].x,first[0].y,
cristy8cd5b312010-01-07 01:10:24 +00001746 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001747 else
1748 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001749 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001750 " %g %g %g %g y z\n",last[2].x,last[2].y,
cristy8cd5b312010-01-07 01:10:24 +00001751 first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001752 else
cristyb51dff52011-05-19 16:55:47 +00001753 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001754 " %g %g %g %g %g %g c z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001755 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001756 (void) ConcatenateString(&path,message);
1757 in_subpath=MagickFalse;
1758 }
1759 break;
1760 }
1761 case 6:
1762 case 7:
1763 case 8:
1764 default:
1765 {
1766 blob+=24;
1767 length-=24;
1768 break;
1769 }
1770 }
1771 }
1772 /*
1773 Returns an empty PS path if the path has no knots.
1774 */
cristyb51dff52011-05-19 16:55:47 +00001775 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n");
cristy3ed852e2009-09-05 21:47:34 +00001776 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001777 (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
cristy3ed852e2009-09-05 21:47:34 +00001778 (void) ConcatenateString(&path,message);
1779 message=DestroyString(message);
1780 return(path);
1781}
1782
1783static char *TraceSVGClippath(const unsigned char *blob,size_t length,
cristybb503372010-05-27 20:51:26 +00001784 const size_t columns,const size_t rows)
cristy3ed852e2009-09-05 21:47:34 +00001785{
1786 char
1787 *path,
1788 *message;
1789
cristy3ed852e2009-09-05 21:47:34 +00001790 MagickBooleanType
1791 in_subpath;
1792
1793 PointInfo
1794 first[3],
1795 last[3],
1796 point[3];
1797
cristybb503372010-05-27 20:51:26 +00001798 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001799 i;
1800
cristy9d314ff2011-03-09 01:30:28 +00001801 ssize_t
1802 knot_count,
1803 selector,
1804 x,
1805 y;
1806
cristy3ed852e2009-09-05 21:47:34 +00001807 path=AcquireString((char *) NULL);
1808 if (path == (char *) NULL)
1809 return((char *) NULL);
1810 message=AcquireString((char *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001811 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001812 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
1813 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001814 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001815 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001816 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001817 (void) FormatLocaleString(message,MaxTextExtent,"<g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001818 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001819 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001820 "<path style=\"fill:#00000000;stroke:#00000000;");
1821 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001822 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001823 "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
1824 (void) ConcatenateString(&path,message);
1825 (void) ResetMagickMemory(point,0,sizeof(point));
1826 (void) ResetMagickMemory(first,0,sizeof(first));
1827 (void) ResetMagickMemory(last,0,sizeof(last));
1828 knot_count=0;
1829 in_subpath=MagickFalse;
1830 while (length != 0)
1831 {
cristybb503372010-05-27 20:51:26 +00001832 selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001833 switch (selector)
1834 {
1835 case 0:
1836 case 3:
1837 {
1838 if (knot_count != 0)
1839 {
1840 blob+=24;
1841 length-=24;
1842 break;
1843 }
1844 /*
1845 Expected subpath length record.
1846 */
cristybb503372010-05-27 20:51:26 +00001847 knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
cristy3ed852e2009-09-05 21:47:34 +00001848 blob+=22;
1849 length-=22;
1850 break;
1851 }
1852 case 1:
1853 case 2:
1854 case 4:
1855 case 5:
1856 {
1857 if (knot_count == 0)
1858 {
1859 /*
1860 Unexpected subpath knot.
1861 */
1862 blob+=24;
1863 length-=24;
cristy97433202009-10-27 02:05:08 +00001864 break;
1865 }
1866 /*
1867 Add sub-path knot
1868 */
1869 for (i=0; i < 3; i++)
1870 {
cristyd4982b42011-03-21 14:24:23 +00001871 size_t
cristy97433202009-10-27 02:05:08 +00001872 xx,
1873 yy;
1874
1875 yy=ReadPropertyMSBLong(&blob,&length);
1876 xx=ReadPropertyMSBLong(&blob,&length);
cristybb503372010-05-27 20:51:26 +00001877 x=(ssize_t) xx;
cristy97433202009-10-27 02:05:08 +00001878 if (xx > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001879 x=(ssize_t) xx-4294967295U-1;
cristybb503372010-05-27 20:51:26 +00001880 y=(ssize_t) yy;
cristy97433202009-10-27 02:05:08 +00001881 if (yy > 2147483647)
cristy17df8572011-05-25 16:39:58 +00001882 y=(ssize_t) yy-4294967295U-1;
cristy97433202009-10-27 02:05:08 +00001883 point[i].x=(double) x*columns/4096/4096;
1884 point[i].y=(double) y*rows/4096/4096;
1885 }
1886 if (in_subpath == MagickFalse)
1887 {
cristyb51dff52011-05-19 16:55:47 +00001888 (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001889 point[1].x,point[1].y);
1890 for (i=0; i < 3; i++)
1891 {
1892 first[i]=point[i];
1893 last[i]=point[i];
1894 }
cristy3ed852e2009-09-05 21:47:34 +00001895 }
1896 else
1897 {
cristy97433202009-10-27 02:05:08 +00001898 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1899 (point[0].x == point[1].x) && (point[0].y == point[1].y))
cristyb51dff52011-05-19 16:55:47 +00001900 (void) FormatLocaleString(message,MaxTextExtent,"L %g,%g\n",
cristy97433202009-10-27 02:05:08 +00001901 point[1].x,point[1].y);
1902 else
cristyb51dff52011-05-19 16:55:47 +00001903 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001904 "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
cristy97433202009-10-27 02:05:08 +00001905 point[0].x,point[0].y,point[1].x,point[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001906 for (i=0; i < 3; i++)
cristy97433202009-10-27 02:05:08 +00001907 last[i]=point[i];
1908 }
1909 (void) ConcatenateString(&path,message);
1910 in_subpath=MagickTrue;
1911 knot_count--;
1912 /*
1913 Close the subpath if there are no more knots.
1914 */
1915 if (knot_count == 0)
1916 {
1917 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
1918 (first[0].x == first[1].x) && (first[0].y == first[1].y))
cristyb51dff52011-05-19 16:55:47 +00001919 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001920 "L %g,%g Z\n",first[1].x,first[1].y);
cristy3ed852e2009-09-05 21:47:34 +00001921 else
1922 {
cristyb51dff52011-05-19 16:55:47 +00001923 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001924 "C %g,%g %g,%g %g,%g Z\n",last[2].x,
cristy8cd5b312010-01-07 01:10:24 +00001925 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
cristy97433202009-10-27 02:05:08 +00001926 (void) ConcatenateString(&path,message);
cristy3ed852e2009-09-05 21:47:34 +00001927 }
cristy97433202009-10-27 02:05:08 +00001928 in_subpath=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001929 }
cristy97433202009-10-27 02:05:08 +00001930 break;
cristy3ed852e2009-09-05 21:47:34 +00001931 }
1932 case 6:
1933 case 7:
1934 case 8:
1935 default:
1936 {
1937 blob+=24;
1938 length-=24;
1939 break;
1940 }
1941 }
1942 }
1943 /*
1944 Return an empty SVG image if the path does not have knots.
1945 */
cristyb51dff52011-05-19 16:55:47 +00001946 (void) FormatLocaleString(message,MaxTextExtent,"\"/>\n");
cristy3ed852e2009-09-05 21:47:34 +00001947 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001948 (void) FormatLocaleString(message,MaxTextExtent,"</g>\n");
cristy3ed852e2009-09-05 21:47:34 +00001949 (void) ConcatenateString(&path,message);
cristyb51dff52011-05-19 16:55:47 +00001950 (void) FormatLocaleString(message,MaxTextExtent,"</svg>\n");
cristy3ed852e2009-09-05 21:47:34 +00001951 (void) ConcatenateString(&path,message);
1952 message=DestroyString(message);
1953 return(path);
1954}
1955
1956MagickExport const char *GetImageProperty(const Image *image,
cristyd15e6592011-10-15 00:13:06 +00001957 const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001958{
cristy3ed852e2009-09-05 21:47:34 +00001959 FxInfo
1960 *fx_info;
1961
1962 MagickRealType
1963 alpha;
1964
1965 MagickStatusType
1966 status;
1967
1968 register const char
1969 *p;
1970
1971 assert(image != (Image *) NULL);
1972 assert(image->signature == MagickSignature);
1973 if (image->debug != MagickFalse)
1974 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1975 p=(const char *) NULL;
cristy27f7af22010-06-21 12:23:21 +00001976 if (image->properties != (void *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001977 {
cristy5eb9fc82011-02-21 15:05:41 +00001978 if (property == (const char *) NULL)
1979 {
1980 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
1981 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
1982 image->properties);
1983 return(p);
1984 }
cristyd4982b42011-03-21 14:24:23 +00001985 if (LocaleNCompare("fx:",property,3) != 0)
1986 {
1987 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
1988 image->properties,property);
1989 if (p != (const char *) NULL)
1990 return(p);
1991 }
cristy3ed852e2009-09-05 21:47:34 +00001992 }
cristy5eb9fc82011-02-21 15:05:41 +00001993 if ((property == (const char *) NULL) ||
1994 (strchr(property,':') == (char *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001995 return(p);
cristy3ed852e2009-09-05 21:47:34 +00001996 switch (*property)
1997 {
1998 case '8':
1999 {
2000 if (LocaleNCompare("8bim:",property,5) == 0)
2001 {
cristyd15e6592011-10-15 00:13:06 +00002002 if ((Get8BIMProperty(image,property,exception) != MagickFalse) &&
cristy5eb9fc82011-02-21 15:05:41 +00002003 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002004 {
2005 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2006 image->properties,property);
2007 return(p);
2008 }
2009 }
2010 break;
2011 }
2012 case 'E':
2013 case 'e':
2014 {
2015 if (LocaleNCompare("exif:",property,5) == 0)
2016 {
cristyd15e6592011-10-15 00:13:06 +00002017 if ((GetEXIFProperty(image,property,exception) != MagickFalse) &&
cristy5eb9fc82011-02-21 15:05:41 +00002018 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002019 {
2020 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2021 image->properties,property);
2022 return(p);
2023 }
2024 }
2025 break;
2026 }
2027 case 'F':
2028 case 'f':
2029 {
cristy27f7af22010-06-21 12:23:21 +00002030 if (LocaleNCompare("fx:",property,3) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002031 {
cristy27f7af22010-06-21 12:23:21 +00002032 fx_info=AcquireFxInfo(image,property+3);
cristy2bddff82011-07-25 18:39:12 +00002033 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
cristy0568ffc2011-07-25 16:54:14 +00002034 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002035 fx_info=DestroyFxInfo(fx_info);
2036 if (status != MagickFalse)
2037 {
2038 char
2039 value[MaxTextExtent];
2040
cristyb51dff52011-05-19 16:55:47 +00002041 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002042 GetMagickPrecision(),(double) alpha);
cristyd15e6592011-10-15 00:13:06 +00002043 (void) SetImageProperty((Image *) image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +00002044 }
cristy5eb9fc82011-02-21 15:05:41 +00002045 if (image->properties != (void *) NULL)
2046 {
2047 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2048 image->properties,property);
2049 return(p);
2050 }
cristy3ed852e2009-09-05 21:47:34 +00002051 }
2052 break;
2053 }
2054 case 'I':
2055 case 'i':
2056 {
2057 if (LocaleNCompare("iptc:",property,5) == 0)
2058 {
cristyd15e6592011-10-15 00:13:06 +00002059 if ((GetIPTCProperty(image,property,exception) != MagickFalse) &&
cristy5eb9fc82011-02-21 15:05:41 +00002060 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002061 {
2062 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2063 image->properties,property);
2064 return(p);
2065 }
2066 }
2067 break;
2068 }
2069 case 'P':
2070 case 'p':
2071 {
2072 if (LocaleNCompare("pixel:",property,6) == 0)
2073 {
cristy4c08aed2011-07-01 19:47:50 +00002074 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002075 pixel;
2076
cristy4c08aed2011-07-01 19:47:50 +00002077 GetPixelInfo(image,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00002078 fx_info=AcquireFxInfo(image,property+6);
cristy0568ffc2011-07-25 16:54:14 +00002079 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
2080 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002081 pixel.red=(MagickRealType) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002082 status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
2083 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002084 pixel.green=(MagickRealType) QuantumRange*alpha;
cristy0568ffc2011-07-25 16:54:14 +00002085 status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
2086 &alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002087 pixel.blue=(MagickRealType) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002088 if (image->colorspace == CMYKColorspace)
2089 {
cristy0568ffc2011-07-25 16:54:14 +00002090 status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
cristy3ed852e2009-09-05 21:47:34 +00002091 &alpha,exception);
cristy4c08aed2011-07-01 19:47:50 +00002092 pixel.black=(MagickRealType) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +00002093 }
cristy0568ffc2011-07-25 16:54:14 +00002094 status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
2095 &alpha,exception);
cristy4c08aed2011-07-01 19:47:50 +00002096 pixel.alpha=(MagickRealType) QuantumRange*(1.0-alpha);
cristy3ed852e2009-09-05 21:47:34 +00002097 fx_info=DestroyFxInfo(fx_info);
2098 if (status != MagickFalse)
2099 {
2100 char
2101 name[MaxTextExtent];
2102
cristy269c9412011-10-13 23:41:15 +00002103 (void) QueryColorname(image,&pixel,SVGCompliance,name,
cristy3ed852e2009-09-05 21:47:34 +00002104 exception);
cristyd15e6592011-10-15 00:13:06 +00002105 (void) SetImageProperty((Image *) image,property,name,exception);
2106 return(GetImageProperty(image,property,exception));
cristy3ed852e2009-09-05 21:47:34 +00002107 }
2108 }
2109 break;
2110 }
2111 case 'X':
2112 case 'x':
2113 {
2114 if (LocaleNCompare("xmp:",property,4) == 0)
2115 {
cristy5eb9fc82011-02-21 15:05:41 +00002116 if ((GetXMPProperty(image,property) != MagickFalse) &&
2117 (image->properties != (void *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002118 {
2119 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2120 image->properties,property);
2121 return(p);
2122 }
2123 }
2124 break;
2125 }
cristy5eb9fc82011-02-21 15:05:41 +00002126 default:
2127 break;
cristy3ed852e2009-09-05 21:47:34 +00002128 }
2129 return(p);
2130}
2131
2132/*
2133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2134% %
2135% %
2136% %
2137+ G e t M a g i c k P r o p e r t y %
2138% %
2139% %
2140% %
2141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2142%
2143% GetMagickProperty() gets a value associated with an image property.
2144%
2145% The format of the GetMagickProperty method is:
2146%
cristyad785752011-07-27 23:13:03 +00002147% const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
cristyd15e6592011-10-15 00:13:06 +00002148% const char *key,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002149%
2150% A description of each parameter follows:
2151%
2152% o image_info: the image info.
2153%
2154% o image: the image.
2155%
2156% o key: the key.
2157%
cristyd15e6592011-10-15 00:13:06 +00002158% o exception: return any errors or warnings in this structure.
2159%
cristy3ed852e2009-09-05 21:47:34 +00002160*/
2161MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00002162 Image *image,const char *property,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002163{
2164 char
2165 value[MaxTextExtent],
2166 filename[MaxTextExtent];
2167
2168 *value='\0';
cristyec6897c2010-05-12 00:43:15 +00002169 switch (*property)
cristy3ed852e2009-09-05 21:47:34 +00002170 {
2171 case 'b':
2172 {
2173 if (LocaleNCompare("base",property,4) == 0)
2174 {
2175 GetPathComponent(image->magick_filename,BasePath,filename);
2176 (void) CopyMagickString(value,filename,MaxTextExtent);
2177 break;
2178 }
2179 break;
2180 }
2181 case 'c':
2182 {
2183 if (LocaleNCompare("channels",property,8) == 0)
2184 {
2185 /*
2186 Image channels.
2187 */
cristyb51dff52011-05-19 16:55:47 +00002188 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002189 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002190 image->colorspace));
2191 LocaleLower(value);
2192 if (image->matte != MagickFalse)
2193 (void) ConcatenateMagickString(value,"a",MaxTextExtent);
2194 break;
2195 }
2196 if (LocaleNCompare("colorspace",property,10) == 0)
2197 {
2198 ColorspaceType
2199 colorspace;
2200
2201 /*
2202 Image storage class and colorspace.
2203 */
2204 colorspace=image->colorspace;
cristyc82a27b2011-10-21 01:07:16 +00002205 if (IsImageGray(image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002206 colorspace=GRAYColorspace;
cristyb51dff52011-05-19 16:55:47 +00002207 (void) FormatLocaleString(value,MaxTextExtent,"%s",
cristy042ee782011-04-22 18:48:30 +00002208 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
cristyf2faecf2010-05-28 19:19:36 +00002209 colorspace));
cristy3ed852e2009-09-05 21:47:34 +00002210 break;
2211 }
cristy63054742010-09-20 12:42:28 +00002212 if (LocaleNCompare("copyright",property,9) == 0)
2213 {
2214 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
2215 break;
2216 }
cristy3ed852e2009-09-05 21:47:34 +00002217 break;
2218 }
2219 case 'd':
2220 {
2221 if (LocaleNCompare("depth",property,5) == 0)
2222 {
cristyb51dff52011-05-19 16:55:47 +00002223 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002224 image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002225 break;
2226 }
2227 if (LocaleNCompare("directory",property,9) == 0)
2228 {
2229 GetPathComponent(image->magick_filename,HeadPath,filename);
2230 (void) CopyMagickString(value,filename,MaxTextExtent);
2231 break;
2232 }
2233 break;
2234 }
2235 case 'e':
2236 {
2237 if (LocaleNCompare("extension",property,9) == 0)
2238 {
2239 GetPathComponent(image->magick_filename,ExtensionPath,filename);
2240 (void) CopyMagickString(value,filename,MaxTextExtent);
2241 break;
2242 }
2243 break;
2244 }
2245 case 'g':
2246 {
2247 if (LocaleNCompare("group",property,5) == 0)
2248 {
cristyb51dff52011-05-19 16:55:47 +00002249 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",
cristyf2faecf2010-05-28 19:19:36 +00002250 (unsigned long) image_info->group);
cristy3ed852e2009-09-05 21:47:34 +00002251 break;
2252 }
2253 break;
2254 }
2255 case 'h':
2256 {
2257 if (LocaleNCompare("height",property,6) == 0)
2258 {
cristyb51dff52011-05-19 16:55:47 +00002259 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
cristye8c25f92010-06-03 00:53:06 +00002260 image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
cristy3ed852e2009-09-05 21:47:34 +00002261 break;
2262 }
2263 break;
2264 }
2265 case 'i':
2266 {
2267 if (LocaleNCompare("input",property,5) == 0)
2268 {
2269 (void) CopyMagickString(value,image->filename,MaxTextExtent);
2270 break;
2271 }
2272 break;
2273 }
2274 case 'k':
2275 {
2276 if (LocaleNCompare("kurtosis",property,8) == 0)
2277 {
2278 double
2279 kurtosis,
2280 skewness;
2281
cristyc82a27b2011-10-21 01:07:16 +00002282 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002283 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002284 GetMagickPrecision(),kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00002285 break;
2286 }
2287 break;
2288 }
2289 case 'm':
2290 {
2291 if (LocaleNCompare("magick",property,6) == 0)
2292 {
2293 (void) CopyMagickString(value,image->magick,MaxTextExtent);
2294 break;
2295 }
2296 if (LocaleNCompare("max",property,3) == 0)
2297 {
2298 double
2299 maximum,
2300 minimum;
2301
cristyc82a27b2011-10-21 01:07:16 +00002302 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002303 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002304 GetMagickPrecision(),maximum);
cristy3ed852e2009-09-05 21:47:34 +00002305 break;
2306 }
2307 if (LocaleNCompare("mean",property,4) == 0)
2308 {
2309 double
2310 mean,
2311 standard_deviation;
2312
cristyd42d9952011-07-08 14:21:50 +00002313 (void) GetImageMean(image,&mean,&standard_deviation,
cristyc82a27b2011-10-21 01:07:16 +00002314 exception);
cristyb51dff52011-05-19 16:55:47 +00002315 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002316 GetMagickPrecision(),mean);
cristy3ed852e2009-09-05 21:47:34 +00002317 break;
2318 }
2319 if (LocaleNCompare("min",property,3) == 0)
2320 {
2321 double
2322 maximum,
2323 minimum;
2324
cristyc82a27b2011-10-21 01:07:16 +00002325 (void) GetImageRange(image,&minimum,&maximum,exception);
cristyb51dff52011-05-19 16:55:47 +00002326 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristyf899bbc2010-08-10 18:11:20 +00002327 GetMagickPrecision(),minimum);
cristy3ed852e2009-09-05 21:47:34 +00002328 break;
2329 }
2330 break;
2331 }
2332 case 'n':
2333 {
2334 if (LocaleNCompare("name",property,4) == 0)
2335 {
2336 (void) CopyMagickString(value,filename,MaxTextExtent);
2337 break;
2338 }
2339 break;
2340 }
2341 case 'o':
2342 {
cristyb0094252011-03-26 12:59:45 +00002343 if (LocaleNCompare("opaque",property,6) == 0)
2344 {
2345 MagickBooleanType
2346 opaque;
2347
cristyc82a27b2011-10-21 01:07:16 +00002348 opaque=IsImageOpaque(image,exception);
cristyb0094252011-03-26 12:59:45 +00002349 (void) CopyMagickString(value,opaque == MagickFalse ? "false" :
2350 "true",MaxTextExtent);
2351 break;
2352 }
cristy3ed852e2009-09-05 21:47:34 +00002353 if (LocaleNCompare("output",property,6) == 0)
2354 {
2355 (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
2356 break;
2357 }
2358 break;
2359 }
2360 case 'p':
2361 {
2362 if (LocaleNCompare("page",property,4) == 0)
2363 {
cristyb51dff52011-05-19 16:55:47 +00002364 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyad785752011-07-27 23:13:03 +00002365 GetImageIndexInList(image)+1);
cristy3ed852e2009-09-05 21:47:34 +00002366 break;
2367 }
2368 break;
2369 }
2370 case 's':
2371 {
2372 if (LocaleNCompare("size",property,4) == 0)
2373 {
2374 char
2375 format[MaxTextExtent];
2376
cristyb9080c92009-12-01 20:13:26 +00002377 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
cristyb51dff52011-05-19 16:55:47 +00002378 (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
cristy3ed852e2009-09-05 21:47:34 +00002379 break;
2380 }
2381 if (LocaleNCompare("scenes",property,6) == 0)
2382 {
cristyb51dff52011-05-19 16:55:47 +00002383 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002384 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00002385 break;
2386 }
2387 if (LocaleNCompare("scene",property,5) == 0)
2388 {
cristyb51dff52011-05-19 16:55:47 +00002389 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002390 image->scene);
cristy3ed852e2009-09-05 21:47:34 +00002391 if (image_info->number_scenes != 0)
cristyb51dff52011-05-19 16:55:47 +00002392 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002393 image_info->scene);
cristy3ed852e2009-09-05 21:47:34 +00002394 break;
2395 }
2396 if (LocaleNCompare("skewness",property,8) == 0)
2397 {
2398 double
2399 kurtosis,
2400 skewness;
2401
cristyc82a27b2011-10-21 01:07:16 +00002402 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00002403 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002404 GetMagickPrecision(),skewness);
cristy3ed852e2009-09-05 21:47:34 +00002405 break;
2406 }
cristyd42d9952011-07-08 14:21:50 +00002407 if (LocaleNCompare("standard-deviation",property,18) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002408 {
2409 double
2410 mean,
2411 standard_deviation;
2412
cristyd42d9952011-07-08 14:21:50 +00002413 (void) GetImageMean(image,&mean,&standard_deviation,
cristyc82a27b2011-10-21 01:07:16 +00002414 exception);
cristyb51dff52011-05-19 16:55:47 +00002415 (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
cristy0596f852010-01-17 20:21:09 +00002416 GetMagickPrecision(),standard_deviation);
cristy3ed852e2009-09-05 21:47:34 +00002417 break;
2418 }
2419 break;
2420 }
2421 case 'u':
2422 {
2423 if (LocaleNCompare("unique",property,6) == 0)
2424 {
2425 (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
2426 (void) CopyMagickString(value,filename,MaxTextExtent);
2427 break;
2428 }
2429 break;
2430 }
cristy63054742010-09-20 12:42:28 +00002431 case 'v':
2432 {
2433 if (LocaleNCompare("version",property,7) == 0)
2434 {
2435 (void) CopyMagickString(value,GetMagickVersion((size_t *) NULL),
2436 MaxTextExtent);
2437 break;
2438 }
2439 break;
2440 }
cristy3ed852e2009-09-05 21:47:34 +00002441 case 'w':
2442 {
2443 if (LocaleNCompare("width",property,5) == 0)
2444 {
cristyb51dff52011-05-19 16:55:47 +00002445 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00002446 (image->magick_columns != 0 ? image->magick_columns : 256));
cristy3ed852e2009-09-05 21:47:34 +00002447 break;
2448 }
2449 break;
2450 }
2451 case 'x':
2452 {
2453 if (LocaleNCompare("xresolution",property,11) == 0)
2454 {
cristyb51dff52011-05-19 16:55:47 +00002455 (void) FormatLocaleString(value,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00002456 image->x_resolution);
2457 break;
2458 }
2459 break;
2460 }
2461 case 'y':
2462 {
2463 if (LocaleNCompare("yresolution",property,11) == 0)
2464 {
cristyb51dff52011-05-19 16:55:47 +00002465 (void) FormatLocaleString(value,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00002466 image->y_resolution);
2467 break;
2468 }
2469 break;
2470 }
2471 case 'z':
2472 {
2473 if (LocaleNCompare("zero",property,4) == 0)
2474 {
2475 (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
2476 (void) CopyMagickString(value,filename,MaxTextExtent);
2477 break;
2478 }
2479 break;
2480 }
2481 }
2482 if (*value != '\0')
2483 {
2484 if (image->properties == (void *) NULL)
2485 image->properties=NewSplayTree(CompareSplayTreeString,
2486 RelinquishMagickMemory,RelinquishMagickMemory);
2487 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
2488 ConstantString(property),ConstantString(value));
2489 }
cristyd15e6592011-10-15 00:13:06 +00002490 return(GetImageProperty(image,property,exception));
cristy3ed852e2009-09-05 21:47:34 +00002491}
2492
2493/*
2494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2495% %
2496% %
2497% %
2498% G e t N e x t I m a g e P r o p e r t y %
2499% %
2500% %
2501% %
2502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2503%
2504% GetNextImageProperty() gets the next image property value.
2505%
2506% The format of the GetNextImageProperty method is:
2507%
2508% char *GetNextImageProperty(const Image *image)
2509%
2510% A description of each parameter follows:
2511%
2512% o image: the image.
2513%
2514*/
2515MagickExport char *GetNextImageProperty(const Image *image)
2516{
2517 assert(image != (Image *) NULL);
2518 assert(image->signature == MagickSignature);
2519 if (image->debug != MagickFalse)
2520 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2521 image->filename);
2522 if (image->properties == (void *) NULL)
2523 return((char *) NULL);
2524 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
2525}
2526
2527/*
2528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2529% %
2530% %
2531% %
2532% I n t e r p r e t I m a g e P r o p e r t i e s %
2533% %
2534% %
2535% %
2536%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2537%
2538% InterpretImageProperties() replaces any embedded formatting characters with
glennrpaa588b82011-07-02 05:43:07 +00002539% the appropriate image property and returns the interpreted text.
cristy3ed852e2009-09-05 21:47:34 +00002540%
2541% The format of the InterpretImageProperties method is:
2542%
2543% char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
cristy018f07f2011-09-04 21:15:19 +00002544% const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002545%
2546% A description of each parameter follows:
2547%
2548% o image_info: the image info.
2549%
2550% o image: the image.
2551%
2552% o embed_text: the address of a character string containing the embedded
2553% formatting characters.
2554%
cristy018f07f2011-09-04 21:15:19 +00002555% o exception: return any errors or warnings in this structure.
2556%
cristy3ed852e2009-09-05 21:47:34 +00002557*/
2558MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
cristy018f07f2011-09-04 21:15:19 +00002559 Image *image,const char *embed_text,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002560{
2561 char
2562 filename[MaxTextExtent],
2563 *interpret_text,
2564 *text;
2565
2566 const char
2567 *value;
2568
cristy3ed852e2009-09-05 21:47:34 +00002569 register char
2570 *q;
2571
2572 register const char
2573 *p;
2574
cristybb503372010-05-27 20:51:26 +00002575 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002576 i;
2577
2578 size_t
2579 extent,
2580 length;
2581
2582 assert(image != (Image *) NULL);
2583 assert(image->signature == MagickSignature);
2584 if (image->debug != MagickFalse)
2585 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2586 if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
2587 return((char *) NULL);
2588 text=(char *) embed_text;
2589 if ((*text == '@') && ((*(text+1) == '-') ||
2590 (IsPathAccessible(text+1) != MagickFalse)))
cristyc82a27b2011-10-21 01:07:16 +00002591 return(FileToString(embed_text+1,~0,exception));
cristy3ed852e2009-09-05 21:47:34 +00002592 /*
2593 Translate any embedded format characters.
2594 */
cristy3ed852e2009-09-05 21:47:34 +00002595 interpret_text=AcquireString(text);
2596 extent=MaxTextExtent;
2597 p=text;
2598 for (q=interpret_text; *p != '\0'; p++)
2599 {
2600 *q='\0';
2601 if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
2602 {
2603 extent+=MaxTextExtent;
2604 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
2605 MaxTextExtent+1,sizeof(*interpret_text));
2606 if (interpret_text == (char *) NULL)
2607 break;
2608 q=interpret_text+strlen(interpret_text);
2609 }
2610 /*
2611 Process formatting characters in text.
2612 */
2613 if ((*p == '\\') && (*(p+1) == 'r'))
2614 {
2615 *q++='\r';
2616 p++;
2617 continue;
2618 }
2619 if ((*p == '\\') && (*(p+1) == 'n'))
2620 {
2621 *q++='\n';
2622 p++;
2623 continue;
2624 }
2625 if (*p == '\\')
2626 {
2627 p++;
2628 *q++=(*p);
2629 continue;
2630 }
2631 if (*p != '%')
2632 {
2633 *q++=(*p);
2634 continue;
2635 }
2636 p++;
2637 switch (*p)
2638 {
anthony698cbb12011-10-02 12:01:40 +00002639 case '[': /* multi-character substitution */
cristy3ed852e2009-09-05 21:47:34 +00002640 {
2641 char
2642 pattern[MaxTextExtent];
2643
2644 const char
2645 *key,
2646 *value;
2647
cristybb503372010-05-27 20:51:26 +00002648 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002649 depth;
2650
2651 /*
2652 Image value.
2653 */
2654 if (strchr(p,']') == (char *) NULL)
2655 break;
2656 depth=1;
2657 p++;
2658 for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
2659 {
2660 if (*p == '[')
2661 depth++;
2662 if (*p == ']')
2663 depth--;
2664 if (depth <= 0)
2665 break;
2666 pattern[i]=(*p++);
2667 }
2668 pattern[i]='\0';
cristyd15e6592011-10-15 00:13:06 +00002669 value=GetImageProperty(image,pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002670 if (value != (const char *) NULL)
2671 {
2672 length=strlen(value);
2673 if ((size_t) (q-interpret_text+length+1) >= extent)
2674 {
2675 extent+=length;
2676 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
2677 extent+MaxTextExtent,sizeof(*interpret_text));
2678 if (interpret_text == (char *) NULL)
2679 break;
2680 q=interpret_text+strlen(interpret_text);
2681 }
2682 (void) CopyMagickString(q,value,extent);
2683 q+=length;
2684 break;
2685 }
2686 else
2687 if (IsGlob(pattern) != MagickFalse)
2688 {
2689 /*
2690 Iterate over image properties.
2691 */
2692 ResetImagePropertyIterator(image);
2693 key=GetNextImageProperty(image);
2694 while (key != (const char *) NULL)
2695 {
2696 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
2697 {
cristyd15e6592011-10-15 00:13:06 +00002698 value=GetImageProperty(image,key,exception);
cristy3ed852e2009-09-05 21:47:34 +00002699 if (value != (const char *) NULL)
2700 {
2701 length=strlen(key)+strlen(value)+2;
2702 if ((size_t) (q-interpret_text+length+1) >= extent)
2703 {
2704 extent+=length;
2705 interpret_text=(char *) ResizeQuantumMemory(
2706 interpret_text,extent+MaxTextExtent,
2707 sizeof(*interpret_text));
2708 if (interpret_text == (char *) NULL)
2709 break;
2710 q=interpret_text+strlen(interpret_text);
2711 }
cristyb51dff52011-05-19 16:55:47 +00002712 q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
cristy3ed852e2009-09-05 21:47:34 +00002713 }
2714 }
2715 key=GetNextImageProperty(image);
2716 }
2717 }
cristyd15e6592011-10-15 00:13:06 +00002718 value=GetMagickProperty(image_info,image,pattern,exception);
cristy3ed852e2009-09-05 21:47:34 +00002719 if (value != (const char *) NULL)
2720 {
2721 length=strlen(value);
2722 if ((size_t) (q-interpret_text+length+1) >= extent)
2723 {
2724 extent+=length;
2725 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
2726 extent+MaxTextExtent,sizeof(*interpret_text));
2727 if (interpret_text == (char *) NULL)
2728 break;
2729 q=interpret_text+strlen(interpret_text);
2730 }
2731 (void) CopyMagickString(q,value,extent);
2732 q+=length;
cristy458b5fe2010-05-12 00:54:41 +00002733 break;
cristy3ed852e2009-09-05 21:47:34 +00002734 }
2735 if (image_info == (ImageInfo *) NULL)
2736 break;
2737 value=GetImageOption(image_info,pattern);
2738 if (value != (char *) NULL)
2739 {
2740 length=strlen(value);
2741 if ((size_t) (q-interpret_text+length+1) >= extent)
2742 {
2743 extent+=length;
2744 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
2745 extent+MaxTextExtent,sizeof(*interpret_text));
2746 if (interpret_text == (char *) NULL)
2747 break;
2748 q=interpret_text+strlen(interpret_text);
2749 }
2750 (void) CopyMagickString(q,value,extent);
2751 q+=length;
cristy458b5fe2010-05-12 00:54:41 +00002752 break;
cristy3ed852e2009-09-05 21:47:34 +00002753 }
2754 break;
2755 }
anthony698cbb12011-10-02 12:01:40 +00002756 case 'b': /* image size as read in */
2757 {
2758 char
2759 format[MaxTextExtent];
2760
2761 (void) FormatLocaleString(format,MaxTextExtent,"%.20g",(double)
2762 ((MagickOffsetType) image->extent));
2763 if (image->extent != (MagickSizeType) ((size_t) image->extent))
2764 (void) FormatMagickSize(image->extent,MagickFalse,format);
2765 q+=ConcatenateMagickString(q,format,extent);
2766 q+=ConcatenateMagickString(q,"B",extent);
2767 break;
2768 }
2769 case 'c': /* image comment properity */
2770 {
cristyd15e6592011-10-15 00:13:06 +00002771 value=GetImageProperty(image,"comment",exception);
anthony698cbb12011-10-02 12:01:40 +00002772 if (value == (const char *) NULL)
2773 break;
2774 length=strlen(value);
2775 if ((size_t) (q-interpret_text+length+1) >= extent)
2776 {
2777 extent+=length;
2778 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
2779 MaxTextExtent,sizeof(*interpret_text));
2780 if (interpret_text == (char *) NULL)
2781 break;
2782 q=interpret_text+strlen(interpret_text);
2783 }
2784 (void) CopyMagickString(q,value,extent);
2785 q+=length;
2786 break;
2787 }
2788 case 'd': /* Directory component of filename */
2789 {
2790 GetPathComponent(image->magick_filename,HeadPath,filename);
2791 q+=CopyMagickString(q,filename,extent);
2792 break;
2793 }
2794 case 'e': /* Filename extension (suffix) of image file */
2795 {
2796 GetPathComponent(image->magick_filename,ExtensionPath,filename);
2797 q+=CopyMagickString(q,filename,extent);
2798 break;
2799 }
2800 case 'f': /* Filename without directory component */
2801 {
2802 GetPathComponent(image->magick_filename,TailPath,filename);
2803 q+=CopyMagickString(q,filename,extent);
2804 break;
2805 }
2806 case 't': /* Base filename without directory or extention */
2807 {
2808 GetPathComponent(image->magick_filename,BasePath,filename);
2809 q+=CopyMagickString(q,filename,extent);
2810 break;
2811 }
2812 case 'g': /* Image geometry, canvas and offset */
2813 {
2814 q+=FormatLocaleString(q,extent,"%.20gx%.20g%+.20g%+.20g",(double)
2815 image->page.width,(double) image->page.height,(double) image->page.x,
2816 (double) image->page.y);
2817 break;
2818 }
2819 case 'h': /* Image height */
2820 {
2821 q+=FormatLocaleString(q,extent,"%.20g",(double) (image->rows != 0 ?
2822 image->rows : image->magick_rows));
2823 break;
2824 }
2825 case 'i': /* Images filename - (output filename with "info:" ) */
2826 {
2827 q+=CopyMagickString(q,image->filename,extent);
2828 break;
2829 }
2830 case 'k': /* Number of unique colors */
2831 {
2832 q+=FormatLocaleString(q,extent,"%.20g",(double) GetNumberColors(image,
cristyc82a27b2011-10-21 01:07:16 +00002833 (FILE *) NULL,exception));
anthony698cbb12011-10-02 12:01:40 +00002834 break;
2835 }
2836 case 'l': /* Image label */
2837 {
cristyd15e6592011-10-15 00:13:06 +00002838 value=GetImageProperty(image,"label",exception);
anthony698cbb12011-10-02 12:01:40 +00002839 if (value == (const char *) NULL)
2840 break;
2841 length=strlen(value);
2842 if ((size_t) (q-interpret_text+length+1) >= extent)
2843 {
2844 extent+=length;
2845 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
2846 MaxTextExtent,sizeof(*interpret_text));
2847 if (interpret_text == (char *) NULL)
2848 break;
2849 q=interpret_text+strlen(interpret_text);
2850 }
2851 q+=CopyMagickString(q,value,extent);
2852 break;
2853 }
2854 case 'm': /* Image format (file magick) */
2855 {
2856 q+=CopyMagickString(q,image->magick,extent);
2857 break;
2858 }
2859 case 'M': /* Magick filename - exactly as given incl. read mods */
2860 {
2861 q+=CopyMagickString(q,image->magick_filename,extent);
2862 break;
2863 }
2864 case 'n': /* Number of images in the list. */
2865 {
2866 q+=FormatLocaleString(q,extent,"%.20g",(double)
2867 GetImageListLength(image));
2868 break;
2869 }
2870 case 'o': /* Image output filename */
2871 {
2872 q+=CopyMagickString(q,image_info->filename,extent);
2873 break;
2874 }
2875 case 'p': /* Image index in curent image list */
2876 {
2877 q+=FormatLocaleString(q,extent,"%.20g",(double)
2878 GetImageIndexInList(image));
2879 break;
2880 }
2881 case 'q': /* Quantum depth of image in memory */
2882 {
2883 q+=FormatLocaleString(q,extent,"%.20g",(double)
2884 MAGICKCORE_QUANTUM_DEPTH);
2885 break;
2886 }
2887 case 'r': /* Image storage class and colorspace. */
2888 {
2889 ColorspaceType
2890 colorspace;
2891
2892 colorspace=image->colorspace;
cristyc82a27b2011-10-21 01:07:16 +00002893 if (IsImageGray(image,exception) != MagickFalse)
anthony698cbb12011-10-02 12:01:40 +00002894 colorspace=GRAYColorspace;
2895 q+=FormatLocaleString(q,extent,"%s%s%s",CommandOptionToMnemonic(
2896 MagickClassOptions,(ssize_t) image->storage_class),
2897 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
2898 image->matte != MagickFalse ? "Matte" : "");
2899 break;
2900 }
2901 case 's': /* Image scene number */
2902 {
2903 if (image_info->number_scenes == 0)
2904 q+=FormatLocaleString(q,extent,"%.20g",(double) image->scene);
2905 else
2906 q+=FormatLocaleString(q,extent,"%.20g",(double) image_info->scene);
2907 break;
2908 }
2909 case 'u': /* Unique filename */
2910 {
2911 (void) CopyMagickString(filename,image_info->unique,extent);
2912 q+=CopyMagickString(q,filename,extent);
2913 break;
2914 }
2915 case 'w': /* Image width */
2916 {
2917 q+=FormatLocaleString(q,extent,"%.20g",(double) (image->columns != 0 ?
2918 image->columns : image->magick_columns));
2919 break;
2920 }
2921 case 'x': /* Image horizontal resolution (density). */
2922 {
2923 q+=FormatLocaleString(q,extent,"%g %s",image->x_resolution,
2924 CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
2925 image->units));
2926 break;
2927 }
2928 case 'y': /* Image vertical resolution (density) */
2929 {
2930 q+=FormatLocaleString(q,extent,"%g %s",image->y_resolution,
2931 CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
2932 image->units));
2933 break;
2934 }
2935 case 'z': /* Image depth as read in */
2936 {
2937 q+=FormatLocaleString(q,extent,"%.20g",(double) image->depth);
2938 break;
2939 }
2940 case 'A': /* Image alpha channel */
2941 {
2942 q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
2943 MagickBooleanOptions,(ssize_t) image->matte));
2944 break;
2945 }
2946 case 'C': /* Image compression method. */
2947 {
2948 q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
2949 MagickCompressOptions,(ssize_t) image->compression));
2950 break;
2951 }
2952 case 'D': /* Image dispose method. */
2953 {
2954 q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
2955 MagickDisposeOptions,(ssize_t) image->dispose));
2956 break;
2957 }
2958 case 'G': /* Image size as geometry = "%wx%h" */
2959 {
2960 q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double)
2961 image->magick_columns,(double) image->magick_rows);
2962 break;
2963 }
2964 case 'H': /* layer canvas height */
2965 {
2966 q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.height);
2967 break;
2968 }
2969 case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2970 {
2971 q+=FormatLocaleString(q,extent,"%+ld%+ld",(long) image->page.x,(long)
2972 image->page.y);
2973 break;
2974 }
2975 case 'P': /* layer canvas page size = "%Wx%H" */
2976 {
2977 q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double) image->page.width,
2978 (double) image->page.height);
2979 break;
2980 }
2981 case 'Q': /* image compression quality */
2982 {
2983 q+=FormatLocaleString(q,extent,"%.20g",(double) image->quality);
2984 break;
2985 }
2986 case 'S': /* Image scenes */
2987 {
2988 if (image_info->number_scenes == 0)
2989 q+=CopyMagickString(q,"2147483647",extent);
2990 else
2991 q+=FormatLocaleString(q,extent,"%.20g",(double) (image_info->scene+
2992 image_info->number_scenes));
2993 break;
2994 }
2995 case 'T': /* image time delay for animations */
2996 {
2997 q+=FormatLocaleString(q,extent,"%.20g",(double) image->delay);
2998 break;
2999 }
3000 case 'W': /* layer canvas width */
3001 {
3002 q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.width);
3003 break;
3004 }
3005 case 'X': /* layer canvas X offset */
3006 {
3007 q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.x);
3008 break;
3009 }
3010 case 'Y': /* layer canvas Y offset */
3011 {
3012 q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.y);
3013 break;
3014 }
3015 case 'Z': /* Unique filename. */
3016 {
3017 (void) CopyMagickString(filename,image_info->zero,extent);
3018 q+=CopyMagickString(q,filename,extent);
3019 break;
3020 }
3021 case '@': /* Image bounding box. */
cristy3ed852e2009-09-05 21:47:34 +00003022 {
3023 RectangleInfo
3024 page;
3025
cristy018f07f2011-09-04 21:15:19 +00003026 page=GetImageBoundingBox(image,exception);
cristyb51dff52011-05-19 16:55:47 +00003027 q+=FormatLocaleString(q,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
cristye8c25f92010-06-03 00:53:06 +00003028 (double) page.width,(double) page.height,(double) page.x,(double)
3029 page.y);
cristy3ed852e2009-09-05 21:47:34 +00003030 break;
3031 }
anthony698cbb12011-10-02 12:01:40 +00003032 case '#': /* Image signature */
cristy3ed852e2009-09-05 21:47:34 +00003033 {
cristy018f07f2011-09-04 21:15:19 +00003034 (void) SignatureImage(image,exception);
cristyd15e6592011-10-15 00:13:06 +00003035 value=GetImageProperty(image,"signature",exception);
cristy3ed852e2009-09-05 21:47:34 +00003036 if (value == (const char *) NULL)
3037 break;
3038 q+=CopyMagickString(q,value,extent);
3039 break;
3040 }
anthony698cbb12011-10-02 12:01:40 +00003041 case '%': /* percent escaped */
cristy3ed852e2009-09-05 21:47:34 +00003042 {
3043 *q++=(*p);
3044 break;
3045 }
anthony698cbb12011-10-02 12:01:40 +00003046 default: /* percent not expanded */
cristy3ed852e2009-09-05 21:47:34 +00003047 {
3048 *q++='%';
3049 *q++=(*p);
3050 break;
3051 }
3052 }
3053 }
3054 *q='\0';
cristy3ed852e2009-09-05 21:47:34 +00003055 if (text != (const char *) embed_text)
3056 text=DestroyString(text);
3057 (void) SubstituteString(&interpret_text,"&lt;","<");
3058 (void) SubstituteString(&interpret_text,"&gt;",">");
3059 return(interpret_text);
3060}
3061
3062/*
3063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3064% %
3065% %
3066% %
3067% R e m o v e I m a g e P r o p e r t y %
3068% %
3069% %
3070% %
3071%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3072%
3073% RemoveImageProperty() removes a property from the image and returns its
3074% value.
3075%
3076% The format of the RemoveImageProperty method is:
3077%
3078% char *RemoveImageProperty(Image *image,const char *property)
3079%
3080% A description of each parameter follows:
3081%
3082% o image: the image.
3083%
3084% o property: the image property.
3085%
3086*/
3087MagickExport char *RemoveImageProperty(Image *image,
3088 const char *property)
3089{
3090 char
3091 *value;
3092
3093 assert(image != (Image *) NULL);
3094 assert(image->signature == MagickSignature);
3095 if (image->debug != MagickFalse)
3096 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3097 image->filename);
3098 if (image->properties == (void *) NULL)
3099 return((char *) NULL);
3100 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
3101 property);
3102 return(value);
3103}
3104
3105/*
3106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3107% %
3108% %
3109% %
3110% 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 %
3111% %
3112% %
3113% %
3114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3115%
3116% ResetImagePropertyIterator() resets the image properties iterator. Use it
3117% in conjunction with GetNextImageProperty() to iterate over all the values
3118% associated with an image property.
3119%
3120% The format of the ResetImagePropertyIterator method is:
3121%
3122% ResetImagePropertyIterator(Image *image)
3123%
3124% A description of each parameter follows:
3125%
3126% o image: the image.
3127%
3128*/
3129MagickExport void ResetImagePropertyIterator(const Image *image)
3130{
3131 assert(image != (Image *) NULL);
3132 assert(image->signature == MagickSignature);
3133 if (image->debug != MagickFalse)
3134 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3135 image->filename);
3136 if (image->properties == (void *) NULL)
3137 return;
3138 ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
3139}
3140
3141/*
3142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3143% %
3144% %
3145% %
3146% S e t I m a g e P r o p e r t y %
3147% %
3148% %
3149% %
3150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3151%
3152% SetImageProperty() associates an value with an image property.
3153%
3154% The format of the SetImageProperty method is:
3155%
3156% MagickBooleanType SetImageProperty(Image *image,const char *property,
cristyd15e6592011-10-15 00:13:06 +00003157% const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003158%
3159% A description of each parameter follows:
3160%
3161% o image: the image.
3162%
3163% o property: the image property.
3164%
3165% o values: the image property values.
3166%
cristyd15e6592011-10-15 00:13:06 +00003167% o exception: return any errors or warnings in this structure.
3168%
cristy3ed852e2009-09-05 21:47:34 +00003169*/
3170MagickExport MagickBooleanType SetImageProperty(Image *image,
cristyd15e6592011-10-15 00:13:06 +00003171 const char *property,const char *value,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003172{
3173 MagickBooleanType
3174 status;
3175
3176 MagickStatusType
3177 flags;
3178
3179 assert(image != (Image *) NULL);
3180 assert(image->signature == MagickSignature);
3181 if (image->debug != MagickFalse)
3182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3183 image->filename);
3184 if (image->properties == (void *) NULL)
3185 image->properties=NewSplayTree(CompareSplayTreeString,
3186 RelinquishMagickMemory,RelinquishMagickMemory);
3187 if ((value == (const char *) NULL) || (*value == '\0'))
3188 return(DeleteImageProperty(image,property));
3189 status=MagickTrue;
3190 switch (*property)
3191 {
3192 case 'B':
3193 case 'b':
3194 {
cristyc6c08ab2010-07-24 23:50:09 +00003195 if (LocaleCompare(property,"background") == 0)
3196 {
cristy9950d572011-10-01 18:22:35 +00003197 (void) QueryColorCompliance(value,AllCompliance,
3198 &image->background_color,exception);
cristyc6c08ab2010-07-24 23:50:09 +00003199 break;
3200 }
cristy3ed852e2009-09-05 21:47:34 +00003201 if (LocaleCompare(property,"bias") == 0)
3202 {
cristyf2f27272009-12-17 14:48:46 +00003203 image->bias=SiPrefixToDouble(value,QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00003204 break;
3205 }
3206 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3207 ConstantString(property),ConstantString(value));
3208 break;
3209 }
3210 case 'C':
3211 case 'c':
3212 {
3213 if (LocaleCompare(property,"colorspace") == 0)
3214 {
cristybb503372010-05-27 20:51:26 +00003215 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003216 colorspace;
3217
cristy042ee782011-04-22 18:48:30 +00003218 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003219 value);
3220 if (colorspace < 0)
3221 break;
cristy63240882011-08-05 19:05:27 +00003222 (void) SetImageColorspace(image,(ColorspaceType) colorspace,
3223 exception);
cristy3ed852e2009-09-05 21:47:34 +00003224 break;
3225 }
3226 if (LocaleCompare(property,"compose") == 0)
3227 {
cristybb503372010-05-27 20:51:26 +00003228 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003229 compose;
3230
cristy042ee782011-04-22 18:48:30 +00003231 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003232 if (compose < 0)
3233 break;
3234 image->compose=(CompositeOperator) compose;
3235 break;
3236 }
3237 if (LocaleCompare(property,"compress") == 0)
3238 {
cristybb503372010-05-27 20:51:26 +00003239 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003240 compression;
3241
cristy042ee782011-04-22 18:48:30 +00003242 compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003243 value);
3244 if (compression < 0)
3245 break;
3246 image->compression=(CompressionType) compression;
3247 break;
3248 }
3249 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3250 ConstantString(property),ConstantString(value));
3251 break;
3252 }
3253 case 'D':
3254 case 'd':
3255 {
3256 if (LocaleCompare(property,"delay") == 0)
3257 {
3258 GeometryInfo
3259 geometry_info;
3260
3261 flags=ParseGeometry(value,&geometry_info);
3262 if ((flags & GreaterValue) != 0)
3263 {
cristybb503372010-05-27 20:51:26 +00003264 if (image->delay > (size_t) floor(geometry_info.rho+0.5))
3265 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003266 }
3267 else
3268 if ((flags & LessValue) != 0)
3269 {
cristybb503372010-05-27 20:51:26 +00003270 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
cristyc6c08ab2010-07-24 23:50:09 +00003271 image->ticks_per_second=(ssize_t)
3272 floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003273 }
3274 else
cristybb503372010-05-27 20:51:26 +00003275 image->delay=(size_t) floor(geometry_info.rho+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003276 if ((flags & SigmaValue) != 0)
cristybb503372010-05-27 20:51:26 +00003277 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00003278 break;
3279 }
cristyfbb56842010-08-02 11:26:33 +00003280 if (LocaleCompare(property,"density") == 0)
3281 {
3282 GeometryInfo
3283 geometry_info;
3284
3285 flags=ParseGeometry(value,&geometry_info);
3286 image->x_resolution=geometry_info.rho;
3287 image->y_resolution=geometry_info.sigma;
3288 if ((flags & SigmaValue) == 0)
3289 image->y_resolution=image->x_resolution;
3290 }
cristy3ed852e2009-09-05 21:47:34 +00003291 if (LocaleCompare(property,"depth") == 0)
3292 {
cristye27293e2009-12-18 02:53:20 +00003293 image->depth=StringToUnsignedLong(value);
cristy3ed852e2009-09-05 21:47:34 +00003294 break;
3295 }
3296 if (LocaleCompare(property,"dispose") == 0)
3297 {
cristybb503372010-05-27 20:51:26 +00003298 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003299 dispose;
3300
cristy042ee782011-04-22 18:48:30 +00003301 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003302 if (dispose < 0)
3303 break;
3304 image->dispose=(DisposeType) dispose;
3305 break;
3306 }
3307 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3308 ConstantString(property),ConstantString(value));
3309 break;
3310 }
3311 case 'G':
3312 case 'g':
3313 {
3314 if (LocaleCompare(property,"gravity") == 0)
3315 {
cristybb503372010-05-27 20:51:26 +00003316 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003317 gravity;
3318
cristy042ee782011-04-22 18:48:30 +00003319 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
cristy3ed852e2009-09-05 21:47:34 +00003320 if (gravity < 0)
3321 break;
3322 image->gravity=(GravityType) gravity;
3323 break;
3324 }
3325 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3326 ConstantString(property),ConstantString(value));
3327 break;
3328 }
3329 case 'I':
3330 case 'i':
3331 {
3332 if (LocaleCompare(property,"intent") == 0)
3333 {
cristybb503372010-05-27 20:51:26 +00003334 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003335 rendering_intent;
3336
cristy042ee782011-04-22 18:48:30 +00003337 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003338 value);
3339 if (rendering_intent < 0)
3340 break;
3341 image->rendering_intent=(RenderingIntent) rendering_intent;
3342 break;
3343 }
3344 if (LocaleCompare(property,"interpolate") == 0)
3345 {
cristybb503372010-05-27 20:51:26 +00003346 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003347 interpolate;
3348
cristy042ee782011-04-22 18:48:30 +00003349 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003350 value);
3351 if (interpolate < 0)
3352 break;
cristy5c4e2582011-09-11 19:21:03 +00003353 image->interpolate=(PixelInterpolateMethod) interpolate;
cristy3ed852e2009-09-05 21:47:34 +00003354 break;
3355 }
3356 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3357 ConstantString(property),ConstantString(value));
3358 break;
3359 }
3360 case 'L':
3361 case 'l':
3362 {
3363 if (LocaleCompare(property,"loop") == 0)
3364 {
cristye27293e2009-12-18 02:53:20 +00003365 image->iterations=StringToUnsignedLong(value);
cristy3ed852e2009-09-05 21:47:34 +00003366 break;
3367 }
3368 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3369 ConstantString(property),ConstantString(value));
3370 break;
3371 }
3372 case 'P':
3373 case 'p':
3374 {
3375 if (LocaleCompare(property,"page") == 0)
3376 {
3377 char
3378 *geometry;
3379
3380 geometry=GetPageGeometry(value);
3381 flags=ParseAbsoluteGeometry(geometry,&image->page);
3382 geometry=DestroyString(geometry);
3383 break;
3384 }
cristy071dd7b2010-04-09 13:04:54 +00003385 if (LocaleCompare(property,"profile") == 0)
3386 {
3387 ImageInfo
3388 *image_info;
3389
3390 StringInfo
3391 *profile;
3392
3393 image_info=AcquireImageInfo();
3394 (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
cristyc6c08ab2010-07-24 23:50:09 +00003395 (void) SetImageInfo(image_info,1,exception);
3396 profile=FileToStringInfo(image_info->filename,~0UL,exception);
cristy071dd7b2010-04-09 13:04:54 +00003397 if (profile != (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003398 status=SetImageProfile(image,image_info->magick,profile,exception);
cristy071dd7b2010-04-09 13:04:54 +00003399 image_info=DestroyImageInfo(image_info);
3400 break;
3401 }
cristy3ed852e2009-09-05 21:47:34 +00003402 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3403 ConstantString(property),ConstantString(value));
3404 break;
3405 }
3406 case 'R':
3407 case 'r':
3408 {
3409 if (LocaleCompare(property,"rendering-intent") == 0)
3410 {
cristybb503372010-05-27 20:51:26 +00003411 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003412 rendering_intent;
3413
cristy042ee782011-04-22 18:48:30 +00003414 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +00003415 value);
3416 if (rendering_intent < 0)
3417 break;
3418 image->rendering_intent=(RenderingIntent) rendering_intent;
3419 break;
3420 }
3421 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3422 ConstantString(property),ConstantString(value));
3423 break;
3424 }
3425 case 'T':
3426 case 't':
3427 {
3428 if (LocaleCompare(property,"tile-offset") == 0)
3429 {
3430 char
3431 *geometry;
3432
3433 geometry=GetPageGeometry(value);
3434 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
3435 geometry=DestroyString(geometry);
3436 break;
3437 }
3438 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3439 ConstantString(property),ConstantString(value));
3440 break;
3441 }
cristyfbb56842010-08-02 11:26:33 +00003442 case 'U':
3443 case 'u':
3444 {
3445 if (LocaleCompare(property,"units") == 0)
3446 {
3447 ssize_t
3448 units;
3449
cristy042ee782011-04-22 18:48:30 +00003450 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
cristyfbb56842010-08-02 11:26:33 +00003451 if (units < 0)
3452 break;
3453 image->units=(ResolutionType) units;
3454 break;
3455 }
3456 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3457 ConstantString(property),ConstantString(value));
3458 break;
3459 }
cristy3ed852e2009-09-05 21:47:34 +00003460 default:
3461 {
3462 status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
3463 ConstantString(property),ConstantString(value));
3464 break;
3465 }
3466 }
3467 return(status);
3468}