~ubuntu-branches/ubuntu/saucy/imagemagick/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/CVE-2012-0259.patch/magick/property.c

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2012-04-25 10:22:49 UTC
  • Revision ID: package-import@ubuntu.com-20120425102249-h90oao4ng7lbd8cw
Tags: 8:6.6.9.7-5ubuntu3.1
* SECURITY UPDATE: denial of service and possible code execution via
  malformed ResolutionUnit or IOP tags.
  - debian/patches/CVE-2012-0247.patch: properly calculate
    lengths and sizes in magick/{profile,property}.c.
  - CVE-2012-0247
  - CVE-2012-0248
  - CVE-2012-1185
  - CVE-2012-1186
* SECURITY UPDATE: denial of service and possible code execution via
  EXIF tags.
  - debian/patches/CVE-2012-0259.patch: don't copy invalid memory in
    coders/tiff.c, properly initialize buffers in magick/property.c.
  - CVE-2012-0259
  - CVE-2012-1798
* SECURITY UPDATE: denial of service and possible code execution via
  JPEG EXIF integer overflow.
  - debian/patches/CVE-2012-1610.patch: check number of bytes in
    magick/{profile,property}.c.
  - CVE-2012-1610

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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
%                                                                             %
 
20
%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
 
21
%  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
*/
 
43
#include "magick/studio.h"
 
44
#include "magick/attribute.h"
 
45
#include "magick/cache.h"
 
46
#include "magick/color.h"
 
47
#include "magick/compare.h"
 
48
#include "magick/constitute.h"
 
49
#include "magick/draw.h"
 
50
#include "magick/effect.h"
 
51
#include "magick/exception.h"
 
52
#include "magick/exception-private.h"
 
53
#include "magick/fx.h"
 
54
#include "magick/fx-private.h"
 
55
#include "magick/gem.h"
 
56
#include "magick/geometry.h"
 
57
#include "magick/histogram.h"
 
58
#include "magick/image.h"
 
59
#include "magick/image.h"
 
60
#include "magick/layer.h"
 
61
#include "magick/list.h"
 
62
#include "magick/magick.h"
 
63
#include "magick/memory_.h"
 
64
#include "magick/monitor.h"
 
65
#include "magick/montage.h"
 
66
#include "magick/option.h"
 
67
#include "magick/profile.h"
 
68
#include "magick/property.h"
 
69
#include "magick/quantum.h"
 
70
#include "magick/resource_.h"
 
71
#include "magick/splay-tree.h"
 
72
#include "magick/signature-private.h"
 
73
#include "magick/statistic.h"
 
74
#include "magick/string_.h"
 
75
#include "magick/string-private.h"
 
76
#include "magick/token.h"
 
77
#include "magick/utility.h"
 
78
#include "magick/version.h"
 
79
#include "magick/xml-tree.h"
 
80
 
 
81
/*
 
82
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
83
%                                                                             %
 
84
%                                                                             %
 
85
%                                                                             %
 
86
%   C l o n e I m a g e P r o p e r t i e s                                   %
 
87
%                                                                             %
 
88
%                                                                             %
 
89
%                                                                             %
 
90
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
91
%
 
92
%  CloneImageProperties() clones one or more image properties.
 
93
%
 
94
%  The format of the CloneImageProperties method is:
 
95
%
 
96
%      MagickBooleanType CloneImageProperties(Image *image,
 
97
%        const Image *clone_image)
 
98
%
 
99
%  A description of each parameter follows:
 
100
%
 
101
%    o image: the image.
 
102
%
 
103
%    o clone_image: the clone image.
 
104
%
 
105
*/
 
106
MagickExport MagickBooleanType CloneImageProperties(Image *image,
 
107
  const Image *clone_image)
 
108
{
 
109
  assert(image != (Image *) NULL);
 
110
  assert(image->signature == MagickSignature);
 
111
  if (image->debug != MagickFalse)
 
112
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
113
  assert(clone_image != (const Image *) NULL);
 
114
  assert(clone_image->signature == MagickSignature);
 
115
  if (clone_image->debug != MagickFalse)
 
116
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
117
      clone_image->filename);
 
118
  (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
 
119
  (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
 
120
    MaxTextExtent);
 
121
  image->compression=clone_image->compression;
 
122
  image->quality=clone_image->quality;
 
123
  image->depth=clone_image->depth;
 
124
  image->background_color=clone_image->background_color;
 
125
  image->border_color=clone_image->border_color;
 
126
  image->matte_color=clone_image->matte_color;
 
127
  image->transparent_color=clone_image->transparent_color;
 
128
  image->gamma=clone_image->gamma;
 
129
  image->chromaticity=clone_image->chromaticity;
 
130
  image->rendering_intent=clone_image->rendering_intent;
 
131
  image->black_point_compensation=clone_image->black_point_compensation;
 
132
  image->units=clone_image->units;
 
133
  image->montage=(char *) NULL;
 
134
  image->directory=(char *) NULL;
 
135
  (void) CloneString(&image->geometry,clone_image->geometry);
 
136
  image->offset=clone_image->offset;
 
137
  image->x_resolution=clone_image->x_resolution;
 
138
  image->y_resolution=clone_image->y_resolution;
 
139
  image->page=clone_image->page;
 
140
  image->tile_offset=clone_image->tile_offset;
 
141
  image->extract_info=clone_image->extract_info;
 
142
  image->bias=clone_image->bias;
 
143
  image->filter=clone_image->filter;
 
144
  image->blur=clone_image->blur;
 
145
  image->fuzz=clone_image->fuzz;
 
146
  image->interlace=clone_image->interlace;
 
147
  image->interpolate=clone_image->interpolate;
 
148
  image->endian=clone_image->endian;
 
149
  image->gravity=clone_image->gravity;
 
150
  image->compose=clone_image->compose;
 
151
  image->scene=clone_image->scene;
 
152
  image->orientation=clone_image->orientation;
 
153
  image->dispose=clone_image->dispose;
 
154
  image->delay=clone_image->delay;
 
155
  image->ticks_per_second=clone_image->ticks_per_second;
 
156
  image->iterations=clone_image->iterations;
 
157
  image->total_colors=clone_image->total_colors;
 
158
  image->taint=clone_image->taint;
 
159
  image->progress_monitor=clone_image->progress_monitor;
 
160
  image->client_data=clone_image->client_data;
 
161
  image->start_loop=clone_image->start_loop;
 
162
  image->error=clone_image->error;
 
163
  image->signature=clone_image->signature;
 
164
  if (clone_image->properties != (void *) NULL)
 
165
    {
 
166
      if (image->properties != (void *) NULL)
 
167
        DestroyImageProperties(image);
 
168
      image->properties=CloneSplayTree((SplayTreeInfo *)
 
169
        clone_image->properties,(void *(*)(void *)) ConstantString,
 
170
        (void *(*)(void *)) ConstantString);
 
171
    }
 
172
  return(MagickTrue);
 
173
}
 
174
 
 
175
/*
 
176
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
177
%                                                                             %
 
178
%                                                                             %
 
179
%                                                                             %
 
180
%   D e f i n e I m a g e P r o p e r t y                                     %
 
181
%                                                                             %
 
182
%                                                                             %
 
183
%                                                                             %
 
184
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
185
%
 
186
%  DefineImageProperty() associates a key/value pair with an image property.
 
187
%
 
188
%  The format of the DefineImageProperty method is:
 
189
%
 
190
%      MagickBooleanType DefineImageProperty(Image *image,
 
191
%        const char *property)
 
192
%
 
193
%  A description of each parameter follows:
 
194
%
 
195
%    o image: the image.
 
196
%
 
197
%    o property: the image property.
 
198
%
 
199
*/
 
200
MagickExport MagickBooleanType DefineImageProperty(Image *image,
 
201
  const char *property)
 
202
{
 
203
  char
 
204
    key[MaxTextExtent],
 
205
    value[MaxTextExtent];
 
206
 
 
207
  register char
 
208
    *p;
 
209
 
 
210
  assert(image != (Image *) NULL);
 
211
  assert(property != (const char *) NULL);
 
212
  (void) CopyMagickString(key,property,MaxTextExtent-1);
 
213
  for (p=key; *p != '\0'; p++)
 
214
    if (*p == '=')
 
215
      break;
 
216
  *value='\0';
 
217
  if (*p == '=')
 
218
    (void) CopyMagickString(value,p+1,MaxTextExtent);
 
219
  *p='\0';
 
220
  return(SetImageProperty(image,key,value));
 
221
}
 
222
 
 
223
/*
 
224
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
225
%                                                                             %
 
226
%                                                                             %
 
227
%                                                                             %
 
228
%   D e l e t e I m a g e P r o p e r t y                                     %
 
229
%                                                                             %
 
230
%                                                                             %
 
231
%                                                                             %
 
232
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
233
%
 
234
%  DeleteImageProperty() deletes an image property.
 
235
%
 
236
%  The format of the DeleteImageProperty method is:
 
237
%
 
238
%      MagickBooleanType DeleteImageProperty(Image *image,const char *property)
 
239
%
 
240
%  A description of each parameter follows:
 
241
%
 
242
%    o image: the image.
 
243
%
 
244
%    o property: the image property.
 
245
%
 
246
*/
 
247
MagickExport MagickBooleanType DeleteImageProperty(Image *image,
 
248
  const char *property)
 
249
{
 
250
  assert(image != (Image *) NULL);
 
251
  assert(image->signature == MagickSignature);
 
252
  if (image->debug != MagickFalse)
 
253
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
254
      image->filename);
 
255
  if (image->properties == (void *) NULL)
 
256
    return(MagickFalse);
 
257
  return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
 
258
}
 
259
 
 
260
/*
 
261
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
262
%                                                                             %
 
263
%                                                                             %
 
264
%                                                                             %
 
265
%   D e s t r o y I m a g e P r o p e r t i e s                               %
 
266
%                                                                             %
 
267
%                                                                             %
 
268
%                                                                             %
 
269
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
270
%
 
271
%  DestroyImageProperties() releases memory associated with image property
 
272
%  values.
 
273
%
 
274
%  The format of the DestroyDefines method is:
 
275
%
 
276
%      void DestroyImageProperties(Image *image)
 
277
%
 
278
%  A description of each parameter follows:
 
279
%
 
280
%    o image: the image.
 
281
%
 
282
*/
 
283
MagickExport void DestroyImageProperties(Image *image)
 
284
{
 
285
  assert(image != (Image *) NULL);
 
286
  assert(image->signature == MagickSignature);
 
287
  if (image->debug != MagickFalse)
 
288
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
289
      image->filename);
 
290
  if (image->properties != (void *) NULL)
 
291
    image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
 
292
      image->properties);
 
293
}
 
294
 
 
295
/*
 
296
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
297
%                                                                             %
 
298
%                                                                             %
 
299
%                                                                             %
 
300
%  F o r m a t I m a g e P r o p e r t y                                      %
 
301
%                                                                             %
 
302
%                                                                             %
 
303
%                                                                             %
 
304
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
305
%
 
306
%  FormatImageProperty() permits formatted property/value pairs to be saved as
 
307
%  an image property.
 
308
%
 
309
%  The format of the FormatImageProperty method is:
 
310
%
 
311
%      MagickBooleanType FormatImageProperty(Image *image,const char *property,
 
312
%        const char *format,...)
 
313
%
 
314
%  A description of each parameter follows.
 
315
%
 
316
%   o  image:  The image.
 
317
%
 
318
%   o  property:  The attribute property.
 
319
%
 
320
%   o  format:  A string describing the format to use to write the remaining
 
321
%      arguments.
 
322
%
 
323
*/
 
324
 
 
325
MagickExport MagickBooleanType FormatImagePropertyList(Image *image,
 
326
  const char *property,const char *format,va_list operands)
 
327
{
 
328
  char
 
329
    value[MaxTextExtent];
 
330
 
 
331
  int
 
332
    n;
 
333
 
 
334
#if defined(MAGICKCORE_HAVE_VSNPRINTF)
 
335
  n=vsnprintf(value,MaxTextExtent,format,operands);
 
336
#else
 
337
  n=vsprintf(value,format,operands);
 
338
#endif
 
339
  if (n < 0)
 
340
    value[MaxTextExtent-1]='\0';
 
341
  return(SetImageProperty(image,property,value));
 
342
}
 
343
 
 
344
MagickExport MagickBooleanType FormatImageProperty(Image *image,
 
345
  const char *property,const char *format,...)
 
346
{
 
347
  MagickBooleanType
 
348
    status;
 
349
 
 
350
  va_list
 
351
    operands;
 
352
 
 
353
  va_start(operands,format);
 
354
  status=FormatImagePropertyList(image,property,format,operands);
 
355
  va_end(operands);
 
356
  return(status);
 
357
}
 
358
 
 
359
/*
 
360
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
361
%                                                                             %
 
362
%                                                                             %
 
363
%                                                                             %
 
364
%   G e t I m a g e P r o p e r t y                                           %
 
365
%                                                                             %
 
366
%                                                                             %
 
367
%                                                                             %
 
368
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
369
%
 
370
%  GetImageProperty() gets a value associated with an image property.
 
371
%
 
372
%  The format of the GetImageProperty method is:
 
373
%
 
374
%      const char *GetImageProperty(const Image *image,const char *key)
 
375
%
 
376
%  A description of each parameter follows:
 
377
%
 
378
%    o image: the image.
 
379
%
 
380
%    o key: the key.
 
381
%
 
382
*/
 
383
 
 
384
static char
 
385
  *TracePSClippath(const unsigned char *,size_t,const size_t,
 
386
    const size_t),
 
387
  *TraceSVGClippath(const unsigned char *,size_t,const size_t,
 
388
    const size_t);
 
389
 
 
390
static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
 
391
{
 
392
  char
 
393
    *attribute,
 
394
    *message;
 
395
 
 
396
  const StringInfo
 
397
    *profile;
 
398
 
 
399
  long
 
400
    count,
 
401
    dataset,
 
402
    record;
 
403
 
 
404
  register ssize_t
 
405
    i;
 
406
 
 
407
  size_t
 
408
    length;
 
409
 
 
410
  profile=GetImageProfile(image,"iptc");
 
411
  if (profile == (StringInfo *) NULL)
 
412
    profile=GetImageProfile(image,"8bim");
 
413
  if (profile == (StringInfo *) NULL)
 
414
    return(MagickFalse);
 
415
  count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
 
416
  if (count != 2)
 
417
    return(MagickFalse);
 
418
  attribute=(char *) NULL;
 
419
  for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
 
420
  {
 
421
    length=1;
 
422
    if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
 
423
      continue;
 
424
    length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
 
425
    length|=GetStringInfoDatum(profile)[i+4];
 
426
    if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
 
427
        ((long) GetStringInfoDatum(profile)[i+2] == record))
 
428
      {
 
429
        message=(char *) NULL;
 
430
        if (~length >= 1)
 
431
          message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
 
432
        if (message != (char *) NULL)
 
433
          {
 
434
            (void) CopyMagickString(message,(char *) GetStringInfoDatum(
 
435
              profile)+i+5,length+1);
 
436
            (void) ConcatenateString(&attribute,message);
 
437
            (void) ConcatenateString(&attribute,";");
 
438
            message=DestroyString(message);
 
439
          }
 
440
      }
 
441
    i+=5;
 
442
  }
 
443
  if ((attribute == (char *) NULL) || (*attribute == ';'))
 
444
    {
 
445
      if (attribute != (char *) NULL)
 
446
        attribute=DestroyString(attribute);
 
447
      return(MagickFalse);
 
448
    }
 
449
  attribute[strlen(attribute)-1]='\0';
 
450
  (void) SetImageProperty((Image *) image,key,(const char *) attribute);
 
451
  attribute=DestroyString(attribute);
 
452
  return(MagickTrue);
 
453
}
 
454
 
 
455
static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
 
456
{
 
457
  if (x > y)
 
458
    return(x);
 
459
  return(y);
 
460
}
 
461
 
 
462
static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
 
463
{
 
464
  if (x < y)
 
465
    return(x);
 
466
  return(y);
 
467
}
 
468
 
 
469
static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
 
470
{
 
471
  int
 
472
    c;
 
473
 
 
474
  if (*length < 1)
 
475
    return(EOF);
 
476
  c=(int) (*(*p)++);
 
477
  (*length)--;
 
478
  return(c);
 
479
}
 
480
 
 
481
static inline size_t ReadPropertyMSBLong(const unsigned char **p,
 
482
  size_t *length)
 
483
{
 
484
  int
 
485
    c;
 
486
 
 
487
  register ssize_t
 
488
    i;
 
489
 
 
490
  unsigned char
 
491
    buffer[4];
 
492
 
 
493
  size_t
 
494
    value;
 
495
 
 
496
  if (*length < 4)
 
497
    return(~0UL);
 
498
  for (i=0; i < 4; i++)
 
499
  {
 
500
    c=(int) (*(*p)++);
 
501
    (*length)--;
 
502
    buffer[i]=(unsigned char) c;
 
503
  }
 
504
  value=(size_t) (buffer[0] << 24);
 
505
  value|=buffer[1] << 16;
 
506
  value|=buffer[2] << 8;
 
507
  value|=buffer[3];
 
508
  return(value & 0xffffffff);
 
509
}
 
510
 
 
511
static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
 
512
  size_t *length)
 
513
{
 
514
  int
 
515
    c;
 
516
 
 
517
  register ssize_t
 
518
    i;
 
519
 
 
520
  unsigned char
 
521
    buffer[2];
 
522
 
 
523
  unsigned short
 
524
    value;
 
525
 
 
526
  if (*length < 2)
 
527
    return((unsigned short) ~0U);
 
528
  for (i=0; i < 2; i++)
 
529
  {
 
530
    c=(int) (*(*p)++);
 
531
    (*length)--;
 
532
    buffer[i]=(unsigned char) c;
 
533
  }
 
534
  value=(unsigned short) (buffer[0] << 8);
 
535
  value|=buffer[1];
 
536
  return((unsigned short) (value & 0xffff));
 
537
}
 
538
 
 
539
static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
 
540
{
 
541
  char
 
542
    *attribute,
 
543
    format[MaxTextExtent],
 
544
    name[MaxTextExtent],
 
545
    *resource;
 
546
 
 
547
  const StringInfo
 
548
    *profile;
 
549
 
 
550
  const unsigned char
 
551
    *info;
 
552
 
 
553
  long
 
554
    start,
 
555
    stop;
 
556
 
 
557
  MagickBooleanType
 
558
    status;
 
559
 
 
560
  register ssize_t
 
561
    i;
 
562
 
 
563
  ssize_t
 
564
    count,
 
565
    id,
 
566
    sub_number;
 
567
 
 
568
  size_t
 
569
    length;
 
570
 
 
571
  /*
 
572
    There are no newlines in path names, so it's safe as terminator.
 
573
  */
 
574
  profile=GetImageProfile(image,"8bim");
 
575
  if (profile == (StringInfo *) NULL)
 
576
    return(MagickFalse);
 
577
  count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
 
578
    format);
 
579
  if ((count != 2) && (count != 3) && (count != 4))
 
580
    return(MagickFalse);
 
581
  if (count < 4)
 
582
    (void) CopyMagickString(format,"SVG",MaxTextExtent);
 
583
  if (count < 3)
 
584
    *name='\0';
 
585
  sub_number=1;
 
586
  if (*name == '#')
 
587
    sub_number=(ssize_t) StringToLong(&name[1]);
 
588
  sub_number=MagickMax(sub_number,1L);
 
589
  resource=(char *) NULL;
 
590
  status=MagickFalse;
 
591
  length=GetStringInfoLength(profile);
 
592
  info=GetStringInfoDatum(profile);
 
593
  while ((length > 0) && (status == MagickFalse))
 
594
  {
 
595
    if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
 
596
      continue;
 
597
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
 
598
      continue;
 
599
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
 
600
      continue;
 
601
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
 
602
      continue;
 
603
    id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
 
604
    if (id < (ssize_t) start)
 
605
      continue;
 
606
    if (id > (ssize_t) stop)
 
607
      continue;
 
608
    if (resource != (char *) NULL)
 
609
      resource=DestroyString(resource);
 
610
    count=(ssize_t) ReadPropertyByte(&info,&length);
 
611
    if ((count != 0) && ((size_t) count <= length))
 
612
      {
 
613
        resource=(char *) NULL;
 
614
        if (~(1UL*count) >= MaxTextExtent)
 
615
          resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
 
616
            sizeof(*resource));
 
617
        if (resource != (char *) NULL)
 
618
          {
 
619
            for (i=0; i < (ssize_t) count; i++)
 
620
              resource[i]=(char) ReadPropertyByte(&info,&length);
 
621
            resource[count]='\0';
 
622
          }
 
623
      }
 
624
    if ((count & 0x01) == 0)
 
625
      (void) ReadPropertyByte(&info,&length);
 
626
    count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
 
627
    if ((*name != '\0') && (*name != '#'))
 
628
      if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
 
629
        {
 
630
          /*
 
631
            No name match, scroll forward and try next.
 
632
          */
 
633
          info+=count;
 
634
          length-=MagickMin(count,(ssize_t) length);
 
635
          continue;
 
636
        }
 
637
    if ((*name == '#') && (sub_number != 1))
 
638
      {
 
639
        /*
 
640
          No numbered match, scroll forward and try next.
 
641
        */
 
642
        sub_number--;
 
643
        info+=count;
 
644
        length-=MagickMin(count,(ssize_t) length);
 
645
        continue;
 
646
      }
 
647
    /*
 
648
      We have the resource of interest.
 
649
    */
 
650
    attribute=(char *) NULL;
 
651
    if (~(1UL*count) >= MaxTextExtent)
 
652
      attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
 
653
        sizeof(*attribute));
 
654
    if (attribute != (char *) NULL)
 
655
      {
 
656
        (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
 
657
        attribute[count]='\0';
 
658
        info+=count;
 
659
        length-=MagickMin(count,(ssize_t) length);
 
660
        if ((id <= 1999) || (id >= 2999))
 
661
          (void) SetImageProperty((Image *) image,key,(const char *)
 
662
            attribute);
 
663
        else
 
664
          {
 
665
            char
 
666
              *path;
 
667
 
 
668
            if (LocaleCompare(format,"svg") == 0)
 
669
              path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
 
670
                image->columns,image->rows);
 
671
            else
 
672
              path=TracePSClippath((unsigned char *) attribute,(size_t) count,
 
673
                image->columns,image->rows);
 
674
            (void) SetImageProperty((Image *) image,key,(const char *) path);
 
675
            path=DestroyString(path);
 
676
          }
 
677
        attribute=DestroyString(attribute);
 
678
        status=MagickTrue;
 
679
      }
 
680
  }
 
681
  if (resource != (char *) NULL)
 
682
    resource=DestroyString(resource);
 
683
  return(status);
 
684
}
 
685
 
 
686
static inline unsigned short ReadPropertyShort(const EndianType endian,
 
687
  const unsigned char *buffer)
 
688
{
 
689
  unsigned short
 
690
    value;
 
691
 
 
692
  if (endian == MSBEndian)
 
693
    {
 
694
      value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
 
695
        ((unsigned char *) buffer)[1]);
 
696
      return((unsigned short) (value & 0xffff));
 
697
    }
 
698
  value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
 
699
  return((unsigned short) (value & 0xffff));
 
700
}
 
701
 
 
702
static inline size_t ReadPropertyLong(const EndianType endian,
 
703
  const unsigned char *buffer)
 
704
{
 
705
  size_t
 
706
    value;
 
707
 
 
708
  if (endian == MSBEndian)
 
709
    {
 
710
      value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
 
711
        (buffer[2] << 8) | buffer[3]);
 
712
      return((size_t) (value & 0xffffffff));
 
713
    }
 
714
  value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
 
715
    (buffer[1] << 8 ) | (buffer[0]));
 
716
  return((size_t) (value & 0xffffffff));
 
717
}
 
718
 
 
719
static MagickBooleanType GetEXIFProperty(const Image *image,
 
720
  const char *property)
 
721
{
 
722
#define MaxDirectoryStack  16
 
723
#define EXIF_DELIMITER  "\n"
 
724
#define EXIF_NUM_FORMATS  12
 
725
#define EXIF_FMT_BYTE  1
 
726
#define EXIF_FMT_STRING  2
 
727
#define EXIF_FMT_USHORT  3
 
728
#define EXIF_FMT_ULONG  4
 
729
#define EXIF_FMT_URATIONAL  5
 
730
#define EXIF_FMT_SBYTE  6
 
731
#define EXIF_FMT_UNDEFINED  7
 
732
#define EXIF_FMT_SSHORT  8
 
733
#define EXIF_FMT_SLONG  9
 
734
#define EXIF_FMT_SRATIONAL  10
 
735
#define EXIF_FMT_SINGLE  11
 
736
#define EXIF_FMT_DOUBLE  12
 
737
#define TAG_EXIF_OFFSET  0x8769
 
738
#define TAG_GPS_OFFSET  0x8825
 
739
#define TAG_INTEROP_OFFSET  0xa005
 
740
 
 
741
#define EXIFMultipleValues(size, format, arg) \
 
742
{ \
 
743
   ssize_t \
 
744
     component; \
 
745
 \
 
746
   size_t \
 
747
     length; \
 
748
 \
 
749
   unsigned char \
 
750
     *p1; \
 
751
 \
 
752
   length=0; \
 
753
   p1=p; \
 
754
   for (component=0; component < components; component++) \
 
755
   { \
 
756
     length+=FormatMagickString(buffer+length,MaxTextExtent-length, \
 
757
       format", ",arg); \
 
758
     if (length >= MaxTextExtent - 1) \
 
759
       length=MaxTextExtent-1; \
 
760
     p1+=size; \
 
761
   } \
 
762
   if (length > 1) \
 
763
     buffer[length-2]='\0'; \
 
764
   value=AcquireString(buffer); \
 
765
}
 
766
 
 
767
#define EXIFMultipleFractions(size, format, arg1, arg2) \
 
768
{ \
 
769
   ssize_t \
 
770
     component; \
 
771
 \
 
772
   size_t \
 
773
     length; \
 
774
 \
 
775
   unsigned char \
 
776
     *p1; \
 
777
 \
 
778
   length=0; \
 
779
   p1=p; \
 
780
   for (component=0; component < components; component++) \
 
781
   { \
 
782
     length+=FormatMagickString(buffer+length,MaxTextExtent-length, \
 
783
       format", ",arg1, arg2); \
 
784
     if (length >= MaxTextExtent - 1) \
 
785
       length=MaxTextExtent-1; \
 
786
     p1+=size; \
 
787
   } \
 
788
   if (length > 1) \
 
789
     buffer[length-2]='\0'; \
 
790
   value=AcquireString(buffer); \
 
791
}
 
792
 
 
793
  typedef struct _DirectoryInfo
 
794
  {
 
795
    const unsigned char
 
796
      *directory;
 
797
 
 
798
    size_t
 
799
      entry;
 
800
 
 
801
    ssize_t
 
802
      offset;
 
803
  } DirectoryInfo;
 
804
 
 
805
  typedef struct _TagInfo
 
806
  {
 
807
    size_t
 
808
      tag;
 
809
 
 
810
    const char
 
811
      *description;
 
812
  } TagInfo;
 
813
 
 
814
  static TagInfo
 
815
    EXIFTag[] =
 
816
    {
 
817
      {  0x001, "exif:InteroperabilityIndex" },
 
818
      {  0x002, "exif:InteroperabilityVersion" },
 
819
      {  0x100, "exif:ImageWidth" },
 
820
      {  0x101, "exif:ImageLength" },
 
821
      {  0x102, "exif:BitsPerSample" },
 
822
      {  0x103, "exif:Compression" },
 
823
      {  0x106, "exif:PhotometricInterpretation" },
 
824
      {  0x10a, "exif:FillOrder" },
 
825
      {  0x10d, "exif:DocumentName" },
 
826
      {  0x10e, "exif:ImageDescription" },
 
827
      {  0x10f, "exif:Make" },
 
828
      {  0x110, "exif:Model" },
 
829
      {  0x111, "exif:StripOffsets" },
 
830
      {  0x112, "exif:Orientation" },
 
831
      {  0x115, "exif:SamplesPerPixel" },
 
832
      {  0x116, "exif:RowsPerStrip" },
 
833
      {  0x117, "exif:StripByteCounts" },
 
834
      {  0x11a, "exif:XResolution" },
 
835
      {  0x11b, "exif:YResolution" },
 
836
      {  0x11c, "exif:PlanarConfiguration" },
 
837
      {  0x11d, "exif:PageName" },
 
838
      {  0x11e, "exif:XPosition" },
 
839
      {  0x11f, "exif:YPosition" },
 
840
      {  0x118, "exif:MinSampleValue" },
 
841
      {  0x119, "exif:MaxSampleValue" },
 
842
      {  0x120, "exif:FreeOffsets" },
 
843
      {  0x121, "exif:FreeByteCounts" },
 
844
      {  0x122, "exif:GrayResponseUnit" },
 
845
      {  0x123, "exif:GrayResponseCurve" },
 
846
      {  0x124, "exif:T4Options" },
 
847
      {  0x125, "exif:T6Options" },
 
848
      {  0x128, "exif:ResolutionUnit" },
 
849
      {  0x12d, "exif:TransferFunction" },
 
850
      {  0x131, "exif:Software" },
 
851
      {  0x132, "exif:DateTime" },
 
852
      {  0x13b, "exif:Artist" },
 
853
      {  0x13e, "exif:WhitePoint" },
 
854
      {  0x13f, "exif:PrimaryChromaticities" },
 
855
      {  0x140, "exif:ColorMap" },
 
856
      {  0x141, "exif:HalfToneHints" },
 
857
      {  0x142, "exif:TileWidth" },
 
858
      {  0x143, "exif:TileLength" },
 
859
      {  0x144, "exif:TileOffsets" },
 
860
      {  0x145, "exif:TileByteCounts" },
 
861
      {  0x14a, "exif:SubIFD" },
 
862
      {  0x14c, "exif:InkSet" },
 
863
      {  0x14d, "exif:InkNames" },
 
864
      {  0x14e, "exif:NumberOfInks" },
 
865
      {  0x150, "exif:DotRange" },
 
866
      {  0x151, "exif:TargetPrinter" },
 
867
      {  0x152, "exif:ExtraSample" },
 
868
      {  0x153, "exif:SampleFormat" },
 
869
      {  0x154, "exif:SMinSampleValue" },
 
870
      {  0x155, "exif:SMaxSampleValue" },
 
871
      {  0x156, "exif:TransferRange" },
 
872
      {  0x157, "exif:ClipPath" },
 
873
      {  0x158, "exif:XClipPathUnits" },
 
874
      {  0x159, "exif:YClipPathUnits" },
 
875
      {  0x15a, "exif:Indexed" },
 
876
      {  0x15b, "exif:JPEGTables" },
 
877
      {  0x15f, "exif:OPIProxy" },
 
878
      {  0x200, "exif:JPEGProc" },
 
879
      {  0x201, "exif:JPEGInterchangeFormat" },
 
880
      {  0x202, "exif:JPEGInterchangeFormatLength" },
 
881
      {  0x203, "exif:JPEGRestartInterval" },
 
882
      {  0x205, "exif:JPEGLosslessPredictors" },
 
883
      {  0x206, "exif:JPEGPointTransforms" },
 
884
      {  0x207, "exif:JPEGQTables" },
 
885
      {  0x208, "exif:JPEGDCTables" },
 
886
      {  0x209, "exif:JPEGACTables" },
 
887
      {  0x211, "exif:YCbCrCoefficients" },
 
888
      {  0x212, "exif:YCbCrSubSampling" },
 
889
      {  0x213, "exif:YCbCrPositioning" },
 
890
      {  0x214, "exif:ReferenceBlackWhite" },
 
891
      {  0x2bc, "exif:ExtensibleMetadataPlatform" },
 
892
      {  0x301, "exif:Gamma" },
 
893
      {  0x302, "exif:ICCProfileDescriptor" },
 
894
      {  0x303, "exif:SRGBRenderingIntent" },
 
895
      {  0x320, "exif:ImageTitle" },
 
896
      {  0x5001, "exif:ResolutionXUnit" },
 
897
      {  0x5002, "exif:ResolutionYUnit" },
 
898
      {  0x5003, "exif:ResolutionXLengthUnit" },
 
899
      {  0x5004, "exif:ResolutionYLengthUnit" },
 
900
      {  0x5005, "exif:PrintFlags" },
 
901
      {  0x5006, "exif:PrintFlagsVersion" },
 
902
      {  0x5007, "exif:PrintFlagsCrop" },
 
903
      {  0x5008, "exif:PrintFlagsBleedWidth" },
 
904
      {  0x5009, "exif:PrintFlagsBleedWidthScale" },
 
905
      {  0x500A, "exif:HalftoneLPI" },
 
906
      {  0x500B, "exif:HalftoneLPIUnit" },
 
907
      {  0x500C, "exif:HalftoneDegree" },
 
908
      {  0x500D, "exif:HalftoneShape" },
 
909
      {  0x500E, "exif:HalftoneMisc" },
 
910
      {  0x500F, "exif:HalftoneScreen" },
 
911
      {  0x5010, "exif:JPEGQuality" },
 
912
      {  0x5011, "exif:GridSize" },
 
913
      {  0x5012, "exif:ThumbnailFormat" },
 
914
      {  0x5013, "exif:ThumbnailWidth" },
 
915
      {  0x5014, "exif:ThumbnailHeight" },
 
916
      {  0x5015, "exif:ThumbnailColorDepth" },
 
917
      {  0x5016, "exif:ThumbnailPlanes" },
 
918
      {  0x5017, "exif:ThumbnailRawBytes" },
 
919
      {  0x5018, "exif:ThumbnailSize" },
 
920
      {  0x5019, "exif:ThumbnailCompressedSize" },
 
921
      {  0x501a, "exif:ColorTransferFunction" },
 
922
      {  0x501b, "exif:ThumbnailData" },
 
923
      {  0x5020, "exif:ThumbnailImageWidth" },
 
924
      {  0x5021, "exif:ThumbnailImageHeight" },
 
925
      {  0x5022, "exif:ThumbnailBitsPerSample" },
 
926
      {  0x5023, "exif:ThumbnailCompression" },
 
927
      {  0x5024, "exif:ThumbnailPhotometricInterp" },
 
928
      {  0x5025, "exif:ThumbnailImageDescription" },
 
929
      {  0x5026, "exif:ThumbnailEquipMake" },
 
930
      {  0x5027, "exif:ThumbnailEquipModel" },
 
931
      {  0x5028, "exif:ThumbnailStripOffsets" },
 
932
      {  0x5029, "exif:ThumbnailOrientation" },
 
933
      {  0x502a, "exif:ThumbnailSamplesPerPixel" },
 
934
      {  0x502b, "exif:ThumbnailRowsPerStrip" },
 
935
      {  0x502c, "exif:ThumbnailStripBytesCount" },
 
936
      {  0x502d, "exif:ThumbnailResolutionX" },
 
937
      {  0x502e, "exif:ThumbnailResolutionY" },
 
938
      {  0x502f, "exif:ThumbnailPlanarConfig" },
 
939
      {  0x5030, "exif:ThumbnailResolutionUnit" },
 
940
      {  0x5031, "exif:ThumbnailTransferFunction" },
 
941
      {  0x5032, "exif:ThumbnailSoftwareUsed" },
 
942
      {  0x5033, "exif:ThumbnailDateTime" },
 
943
      {  0x5034, "exif:ThumbnailArtist" },
 
944
      {  0x5035, "exif:ThumbnailWhitePoint" },
 
945
      {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
 
946
      {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
 
947
      {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
 
948
      {  0x5039, "exif:ThumbnailYCbCrPositioning" },
 
949
      {  0x503A, "exif:ThumbnailRefBlackWhite" },
 
950
      {  0x503B, "exif:ThumbnailCopyRight" },
 
951
      {  0x5090, "exif:LuminanceTable" },
 
952
      {  0x5091, "exif:ChrominanceTable" },
 
953
      {  0x5100, "exif:FrameDelay" },
 
954
      {  0x5101, "exif:LoopCount" },
 
955
      {  0x5110, "exif:PixelUnit" },
 
956
      {  0x5111, "exif:PixelPerUnitX" },
 
957
      {  0x5112, "exif:PixelPerUnitY" },
 
958
      {  0x5113, "exif:PaletteHistogram" },
 
959
      {  0x1000, "exif:RelatedImageFileFormat" },
 
960
      {  0x1001, "exif:RelatedImageLength" },
 
961
      {  0x1002, "exif:RelatedImageWidth" },
 
962
      {  0x800d, "exif:ImageID" },
 
963
      {  0x80e3, "exif:Matteing" },
 
964
      {  0x80e4, "exif:DataType" },
 
965
      {  0x80e5, "exif:ImageDepth" },
 
966
      {  0x80e6, "exif:TileDepth" },
 
967
      {  0x828d, "exif:CFARepeatPatternDim" },
 
968
      {  0x828e, "exif:CFAPattern2" },
 
969
      {  0x828f, "exif:BatteryLevel" },
 
970
      {  0x8298, "exif:Copyright" },
 
971
      {  0x829a, "exif:ExposureTime" },
 
972
      {  0x829d, "exif:FNumber" },
 
973
      {  0x83bb, "exif:IPTC/NAA" },
 
974
      {  0x84e3, "exif:IT8RasterPadding" },
 
975
      {  0x84e5, "exif:IT8ColorTable" },
 
976
      {  0x8649, "exif:ImageResourceInformation" },
 
977
      {  0x8769, "exif:ExifOffset" },
 
978
      {  0x8773, "exif:InterColorProfile" },
 
979
      {  0x8822, "exif:ExposureProgram" },
 
980
      {  0x8824, "exif:SpectralSensitivity" },
 
981
      {  0x8825, "exif:GPSInfo" },
 
982
      {  0x8827, "exif:ISOSpeedRatings" },
 
983
      {  0x8828, "exif:OECF" },
 
984
      {  0x8829, "exif:Interlace" },
 
985
      {  0x882a, "exif:TimeZoneOffset" },
 
986
      {  0x882b, "exif:SelfTimerMode" },
 
987
      {  0x9000, "exif:ExifVersion" },
 
988
      {  0x9003, "exif:DateTimeOriginal" },
 
989
      {  0x9004, "exif:DateTimeDigitized" },
 
990
      {  0x9101, "exif:ComponentsConfiguration" },
 
991
      {  0x9102, "exif:CompressedBitsPerPixel" },
 
992
      {  0x9201, "exif:ShutterSpeedValue" },
 
993
      {  0x9202, "exif:ApertureValue" },
 
994
      {  0x9203, "exif:BrightnessValue" },
 
995
      {  0x9204, "exif:ExposureBiasValue" },
 
996
      {  0x9205, "exif:MaxApertureValue" },
 
997
      {  0x9206, "exif:SubjectDistance" },
 
998
      {  0x9207, "exif:MeteringMode" },
 
999
      {  0x9208, "exif:LightSource" },
 
1000
      {  0x9209, "exif:Flash" },
 
1001
      {  0x920a, "exif:FocalLength" },
 
1002
      {  0x920b, "exif:FlashEnergy" },
 
1003
      {  0x920c, "exif:SpatialFrequencyResponse" },
 
1004
      {  0x920d, "exif:Noise" },
 
1005
      {  0x9211, "exif:ImageNumber" },
 
1006
      {  0x9212, "exif:SecurityClassification" },
 
1007
      {  0x9213, "exif:ImageHistory" },
 
1008
      {  0x9214, "exif:SubjectArea" },
 
1009
      {  0x9215, "exif:ExposureIndex" },
 
1010
      {  0x9216, "exif:TIFF-EPStandardID" },
 
1011
      {  0x927c, "exif:MakerNote" },
 
1012
      {  0x9C9b, "exif:WinXP-Title" },
 
1013
      {  0x9C9c, "exif:WinXP-Comments" },
 
1014
      {  0x9C9d, "exif:WinXP-Author" },
 
1015
      {  0x9C9e, "exif:WinXP-Keywords" },
 
1016
      {  0x9C9f, "exif:WinXP-Subject" },
 
1017
      {  0x9286, "exif:UserComment" },
 
1018
      {  0x9290, "exif:SubSecTime" },
 
1019
      {  0x9291, "exif:SubSecTimeOriginal" },
 
1020
      {  0x9292, "exif:SubSecTimeDigitized" },
 
1021
      {  0xa000, "exif:FlashPixVersion" },
 
1022
      {  0xa001, "exif:ColorSpace" },
 
1023
      {  0xa002, "exif:ExifImageWidth" },
 
1024
      {  0xa003, "exif:ExifImageLength" },
 
1025
      {  0xa004, "exif:RelatedSoundFile" },
 
1026
      {  0xa005, "exif:InteroperabilityOffset" },
 
1027
      {  0xa20b, "exif:FlashEnergy" },
 
1028
      {  0xa20c, "exif:SpatialFrequencyResponse" },
 
1029
      {  0xa20d, "exif:Noise" },
 
1030
      {  0xa20e, "exif:FocalPlaneXResolution" },
 
1031
      {  0xa20f, "exif:FocalPlaneYResolution" },
 
1032
      {  0xa210, "exif:FocalPlaneResolutionUnit" },
 
1033
      {  0xa214, "exif:SubjectLocation" },
 
1034
      {  0xa215, "exif:ExposureIndex" },
 
1035
      {  0xa216, "exif:TIFF/EPStandardID" },
 
1036
      {  0xa217, "exif:SensingMethod" },
 
1037
      {  0xa300, "exif:FileSource" },
 
1038
      {  0xa301, "exif:SceneType" },
 
1039
      {  0xa302, "exif:CFAPattern" },
 
1040
      {  0xa401, "exif:CustomRendered" },
 
1041
      {  0xa402, "exif:ExposureMode" },
 
1042
      {  0xa403, "exif:WhiteBalance" },
 
1043
      {  0xa404, "exif:DigitalZoomRatio" },
 
1044
      {  0xa405, "exif:FocalLengthIn35mmFilm" },
 
1045
      {  0xa406, "exif:SceneCaptureType" },
 
1046
      {  0xa407, "exif:GainControl" },
 
1047
      {  0xa408, "exif:Contrast" },
 
1048
      {  0xa409, "exif:Saturation" },
 
1049
      {  0xa40a, "exif:Sharpness" },
 
1050
      {  0xa40b, "exif:DeviceSettingDescription" },
 
1051
      {  0xa40c, "exif:SubjectDistanceRange" },
 
1052
      {  0xa420, "exif:ImageUniqueID" },
 
1053
      {  0xc4a5, "exif:PrintImageMatching" },
 
1054
      {  0xa500, "exif:Gamma" },
 
1055
      {  0xc640, "exif:CR2Slice" },
 
1056
      { 0x10000, "exif:GPSVersionID" },
 
1057
      { 0x10001, "exif:GPSLatitudeRef" },
 
1058
      { 0x10002, "exif:GPSLatitude" },
 
1059
      { 0x10003, "exif:GPSLongitudeRef" },
 
1060
      { 0x10004, "exif:GPSLongitude" },
 
1061
      { 0x10005, "exif:GPSAltitudeRef" },
 
1062
      { 0x10006, "exif:GPSAltitude" },
 
1063
      { 0x10007, "exif:GPSTimeStamp" },
 
1064
      { 0x10008, "exif:GPSSatellites" },
 
1065
      { 0x10009, "exif:GPSStatus" },
 
1066
      { 0x1000a, "exif:GPSMeasureMode" },
 
1067
      { 0x1000b, "exif:GPSDop" },
 
1068
      { 0x1000c, "exif:GPSSpeedRef" },
 
1069
      { 0x1000d, "exif:GPSSpeed" },
 
1070
      { 0x1000e, "exif:GPSTrackRef" },
 
1071
      { 0x1000f, "exif:GPSTrack" },
 
1072
      { 0x10010, "exif:GPSImgDirectionRef" },
 
1073
      { 0x10011, "exif:GPSImgDirection" },
 
1074
      { 0x10012, "exif:GPSMapDatum" },
 
1075
      { 0x10013, "exif:GPSDestLatitudeRef" },
 
1076
      { 0x10014, "exif:GPSDestLatitude" },
 
1077
      { 0x10015, "exif:GPSDestLongitudeRef" },
 
1078
      { 0x10016, "exif:GPSDestLongitude" },
 
1079
      { 0x10017, "exif:GPSDestBearingRef" },
 
1080
      { 0x10018, "exif:GPSDestBearing" },
 
1081
      { 0x10019, "exif:GPSDestDistanceRef" },
 
1082
      { 0x1001a, "exif:GPSDestDistance" },
 
1083
      { 0x1001b, "exif:GPSProcessingMethod" },
 
1084
      { 0x1001c, "exif:GPSAreaInformation" },
 
1085
      { 0x1001d, "exif:GPSDateStamp" },
 
1086
      { 0x1001e, "exif:GPSDifferential" },
 
1087
      {  0x0000, NULL}
 
1088
    };
 
1089
 
 
1090
  const StringInfo
 
1091
    *profile;
 
1092
 
 
1093
  const unsigned char
 
1094
    *directory,
 
1095
    *exif;
 
1096
 
 
1097
  DirectoryInfo
 
1098
    directory_stack[MaxDirectoryStack];
 
1099
 
 
1100
  EndianType
 
1101
    endian;
 
1102
 
 
1103
  MagickBooleanType
 
1104
    status;
 
1105
 
 
1106
  register ssize_t
 
1107
    i;
 
1108
 
 
1109
  size_t
 
1110
    entry,
 
1111
    length,
 
1112
    number_entries,
 
1113
    tag;
 
1114
 
 
1115
  SplayTreeInfo
 
1116
    *exif_resources;
 
1117
 
 
1118
  ssize_t
 
1119
    all,
 
1120
    id,
 
1121
    level,
 
1122
    offset,
 
1123
    tag_offset,
 
1124
    tag_value;
 
1125
 
 
1126
  static int
 
1127
    tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
 
1128
 
 
1129
  /*
 
1130
    If EXIF data exists, then try to parse the request for a tag.
 
1131
  */
 
1132
  profile=GetImageProfile(image,"exif");
 
1133
  if (profile == (StringInfo *) NULL)
 
1134
    return(MagickFalse);
 
1135
  if ((property == (const char *) NULL) || (*property == '\0'))
 
1136
    return(MagickFalse);
 
1137
  while (isspace((int) ((unsigned char) *property)) != 0)
 
1138
    property++;
 
1139
  all=0;
 
1140
  tag=(~0UL);
 
1141
  switch (*(property+5))
 
1142
  {
 
1143
    case '*':
 
1144
    {
 
1145
      /*
 
1146
        Caller has asked for all the tags in the EXIF data.
 
1147
      */
 
1148
      tag=0;
 
1149
      all=1; /* return the data in description=value format */
 
1150
      break;
 
1151
    }
 
1152
    case '!':
 
1153
    {
 
1154
      tag=0;
 
1155
      all=2; /* return the data in tagid=value format */
 
1156
      break;
 
1157
    }
 
1158
    case '#':
 
1159
    case '@':
 
1160
    {
 
1161
      int
 
1162
        c;
 
1163
 
 
1164
      size_t
 
1165
        n;
 
1166
 
 
1167
      /*
 
1168
        Check for a hex based tag specification first.
 
1169
      */
 
1170
      tag=(*(property+5) == '@') ? 1UL : 0UL;
 
1171
      property+=6;
 
1172
      n=strlen(property);
 
1173
      if (n != 4)
 
1174
        return(MagickFalse);
 
1175
      /*
 
1176
        Parse tag specification as a hex number.
 
1177
      */
 
1178
      n/=4;
 
1179
      do
 
1180
      {
 
1181
        for (i=(ssize_t) n-1L; i >= 0; i--)
 
1182
        {
 
1183
          c=(*property++);
 
1184
          tag<<=4;
 
1185
          if ((c >= '0') && (c <= '9'))
 
1186
            tag|=(c-'0');
 
1187
          else
 
1188
            if ((c >= 'A') && (c <= 'F'))
 
1189
              tag|=(c-('A'-10));
 
1190
            else
 
1191
              if ((c >= 'a') && (c <= 'f'))
 
1192
                tag|=(c-('a'-10));
 
1193
              else
 
1194
                return(MagickFalse);
 
1195
        }
 
1196
      } while (*property != '\0');
 
1197
      break;
 
1198
    }
 
1199
    default:
 
1200
    {
 
1201
      /*
 
1202
        Try to match the text with a tag name instead.
 
1203
      */
 
1204
      for (i=0; ; i++)
 
1205
      {
 
1206
        if (EXIFTag[i].tag == 0)
 
1207
          break;
 
1208
        if (LocaleCompare(EXIFTag[i].description,property) == 0)
 
1209
          {
 
1210
            tag=(size_t) EXIFTag[i].tag;
 
1211
            break;
 
1212
          }
 
1213
      }
 
1214
      break;
 
1215
    }
 
1216
  }
 
1217
  if (tag == (~0UL))
 
1218
    return(MagickFalse);
 
1219
  length=GetStringInfoLength(profile);
 
1220
  exif=GetStringInfoDatum(profile);
 
1221
  while (length != 0)
 
1222
  {
 
1223
    if (ReadPropertyByte(&exif,&length) != 0x45)
 
1224
      continue;
 
1225
    if (ReadPropertyByte(&exif,&length) != 0x78)
 
1226
      continue;
 
1227
    if (ReadPropertyByte(&exif,&length) != 0x69)
 
1228
      continue;
 
1229
    if (ReadPropertyByte(&exif,&length) != 0x66)
 
1230
      continue;
 
1231
    if (ReadPropertyByte(&exif,&length) != 0x00)
 
1232
      continue;
 
1233
    if (ReadPropertyByte(&exif,&length) != 0x00)
 
1234
      continue;
 
1235
    break;
 
1236
  }
 
1237
  if (length < 16)
 
1238
    return(MagickFalse);
 
1239
  id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
 
1240
  endian=LSBEndian;
 
1241
  if (id == 0x4949)
 
1242
    endian=LSBEndian;
 
1243
  else
 
1244
    if (id == 0x4D4D)
 
1245
      endian=MSBEndian;
 
1246
    else
 
1247
      return(MagickFalse);
 
1248
  if (ReadPropertyShort(endian,exif+2) != 0x002a)
 
1249
    return(MagickFalse);
 
1250
  /*
 
1251
    This the offset to the first IFD.
 
1252
  */
 
1253
  offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
 
1254
  if ((offset < 0) || (size_t) offset >= length)
 
1255
    return(MagickFalse);
 
1256
  /*
 
1257
    Set the pointer to the first IFD and follow it were it leads.
 
1258
  */
 
1259
  status=MagickFalse;
 
1260
  directory=exif+offset;
 
1261
  level=0;
 
1262
  entry=0;
 
1263
  tag_offset=0;
 
1264
  exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
 
1265
    (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
 
1266
  do
 
1267
  {
 
1268
    /*
 
1269
      If there is anything on the stack then pop it off.
 
1270
    */
 
1271
    if (level > 0)
 
1272
      {
 
1273
        level--;
 
1274
        directory=directory_stack[level].directory;
 
1275
        entry=directory_stack[level].entry;
 
1276
        tag_offset=directory_stack[level].offset;
 
1277
      }
 
1278
    /*
 
1279
      Determine how many entries there are in the current IFD.
 
1280
    */
 
1281
    number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
 
1282
    for ( ; entry < number_entries; entry++)
 
1283
    {
 
1284
      register unsigned char
 
1285
        *p,
 
1286
        *q;
 
1287
 
 
1288
      size_t
 
1289
        format,
 
1290
        number_bytes;
 
1291
 
 
1292
      ssize_t
 
1293
        components;
 
1294
 
 
1295
      q=(unsigned char *) (directory+(12*entry)+2);
 
1296
      if (GetValueFromSplayTree(exif_resources,q) == q)
 
1297
        break;
 
1298
      (void) AddValueToSplayTree(exif_resources,q,q);
 
1299
      tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
 
1300
      format=(size_t) ((int) ReadPropertyShort(endian,q+2));
 
1301
      if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
 
1302
        break;
 
1303
      components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
 
1304
      number_bytes=(size_t) components*tag_bytes[format];
 
1305
      if (number_bytes <= 4)
 
1306
        p=q+8;
 
1307
      else
 
1308
        {
 
1309
          ssize_t
 
1310
            offset;
 
1311
 
 
1312
          /*
 
1313
            The directory entry contains an offset.
 
1314
          */
 
1315
          offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
 
1316
          if ((offset+number_bytes) < offset)
 
1317
            continue;  /* prevent overflow */
 
1318
          if ((size_t) (offset+number_bytes) > length)
 
1319
            continue;
 
1320
          p=(unsigned char *) (exif+offset);
 
1321
        }
 
1322
      if ((all != 0) || (tag == (size_t) tag_value))
 
1323
        {
 
1324
          char
 
1325
            buffer[MaxTextExtent],
 
1326
            *value;
 
1327
 
 
1328
          switch (format)
 
1329
          {
 
1330
            case EXIF_FMT_BYTE:
 
1331
            case EXIF_FMT_UNDEFINED:
 
1332
            {
 
1333
              EXIFMultipleValues(1,"%.20g",(double)
 
1334
                (*(unsigned char *) p1));
 
1335
              break;
 
1336
            }
 
1337
            case EXIF_FMT_SBYTE:
 
1338
            {
 
1339
              EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
 
1340
              break;
 
1341
            }
 
1342
            case EXIF_FMT_SSHORT:
 
1343
            {
 
1344
              EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
 
1345
              break;
 
1346
            }
 
1347
            case EXIF_FMT_USHORT:
 
1348
            {
 
1349
              EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
 
1350
              break;
 
1351
            }
 
1352
            case EXIF_FMT_ULONG:
 
1353
            {
 
1354
              EXIFMultipleValues(4,"%.20g",(double)
 
1355
                ((int) ReadPropertyLong(endian,p1)));
 
1356
              break;
 
1357
            }
 
1358
            case EXIF_FMT_SLONG:
 
1359
            {
 
1360
              EXIFMultipleValues(4,"%.20g",(double)
 
1361
                ReadPropertyLong(endian,p1));
 
1362
              break;
 
1363
            }
 
1364
            case EXIF_FMT_URATIONAL:
 
1365
            {
 
1366
              EXIFMultipleFractions(8,"%.20g/%.20g",(double)
 
1367
                ((int) ReadPropertyLong(endian,p1)),(double)
 
1368
                ((int) ReadPropertyLong(endian,p1+4)));
 
1369
              break;
 
1370
            }
 
1371
            case EXIF_FMT_SRATIONAL:
 
1372
            {
 
1373
              EXIFMultipleFractions(8,"%.20g/%.20g",(double)
 
1374
                ReadPropertyLong(endian,p1),(double)
 
1375
                ReadPropertyLong(endian,p1+4));
 
1376
              break;
 
1377
            }
 
1378
            case EXIF_FMT_SINGLE:
 
1379
            {
 
1380
              EXIFMultipleValues(4,"%f",(double) *(float *) p1);
 
1381
              break;
 
1382
            }
 
1383
            case EXIF_FMT_DOUBLE:
 
1384
            {
 
1385
              EXIFMultipleValues(8,"%f",*(double *) p1);
 
1386
              break;
 
1387
            }
 
1388
            default:
 
1389
            case EXIF_FMT_STRING:
 
1390
            {
 
1391
              value=(char *) NULL;
 
1392
              if (~(1UL*number_bytes) >= 1)
 
1393
                value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
 
1394
                  sizeof(*value));
 
1395
              if (value != (char *) NULL)
 
1396
                {
 
1397
                  register ssize_t
 
1398
                    i;
 
1399
 
 
1400
                  for (i=0; i < (ssize_t) number_bytes; i++)
 
1401
                  {
 
1402
                    value[i]='.';
 
1403
                    if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
 
1404
                      value[i]=(char) p[i];
 
1405
                  }
 
1406
                  value[i]='\0';
 
1407
                }
 
1408
              break;
 
1409
            }
 
1410
          }
 
1411
          if (value != (char *) NULL)
 
1412
            {
 
1413
              char
 
1414
                key[MaxTextExtent];
 
1415
 
 
1416
              register const char
 
1417
                *p;
 
1418
 
 
1419
              (void) CopyMagickString(key,property,MaxTextExtent);
 
1420
              switch (all)
 
1421
              {
 
1422
                case 1:
 
1423
                {
 
1424
                  const char
 
1425
                    *description;
 
1426
 
 
1427
                  register ssize_t
 
1428
                    i;
 
1429
 
 
1430
                  description="unknown";
 
1431
                  for (i=0; ; i++)
 
1432
                  {
 
1433
                    if (EXIFTag[i].tag == 0)
 
1434
                      break;
 
1435
                    if ((ssize_t) EXIFTag[i].tag == tag_value)
 
1436
                      {
 
1437
                        description=EXIFTag[i].description;
 
1438
                        break;
 
1439
                      }
 
1440
                  }
 
1441
                  (void) FormatMagickString(key,MaxTextExtent,"%s",
 
1442
                    description);
 
1443
                  break;
 
1444
                }
 
1445
                case 2:
 
1446
                {
 
1447
                  if (tag_value < 0x10000)
 
1448
                    (void) FormatMagickString(key,MaxTextExtent,"#%04lx",
 
1449
                      (unsigned long) tag_value);
 
1450
                  else
 
1451
                    if (tag_value < 0x20000)
 
1452
                      (void) FormatMagickString(key,MaxTextExtent,"@%04lx",
 
1453
                        (unsigned long) (tag_value & 0xffff));
 
1454
                    else
 
1455
                      (void) FormatMagickString(key,MaxTextExtent,"unknown");
 
1456
                  break;
 
1457
                }
 
1458
              }
 
1459
              p=(const char *) NULL;
 
1460
              if (image->properties != (void *) NULL)
 
1461
                p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
1462
                  image->properties,key);
 
1463
              if (p == (const char *) NULL)
 
1464
                (void) SetImageProperty((Image *) image,key,value);
 
1465
              value=DestroyString(value);
 
1466
              status=MagickTrue;
 
1467
            }
 
1468
        }
 
1469
        if ((tag_value == TAG_EXIF_OFFSET) ||
 
1470
            (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
 
1471
          {
 
1472
            ssize_t
 
1473
              offset;
 
1474
 
 
1475
            offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
 
1476
            if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
 
1477
              {
 
1478
                ssize_t
 
1479
                  tag_offset1;
 
1480
 
 
1481
                tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
 
1482
                  0);
 
1483
                directory_stack[level].directory=directory;
 
1484
                entry++;
 
1485
                directory_stack[level].entry=entry;
 
1486
                directory_stack[level].offset=tag_offset;
 
1487
                level++;
 
1488
                directory_stack[level].directory=exif+offset;
 
1489
                directory_stack[level].offset=tag_offset1;
 
1490
                directory_stack[level].entry=0;
 
1491
                level++;
 
1492
                if ((directory+2+(12*number_entries)) > (exif+length))
 
1493
                  break;
 
1494
                offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
 
1495
                  number_entries)));
 
1496
                if ((offset != 0) && ((size_t) offset < length) &&
 
1497
                    (level < (MaxDirectoryStack-2)))
 
1498
                  {
 
1499
                    directory_stack[level].directory=exif+offset;
 
1500
                    directory_stack[level].entry=0;
 
1501
                    directory_stack[level].offset=tag_offset1;
 
1502
                    level++;
 
1503
                  }
 
1504
              }
 
1505
            break;
 
1506
          }
 
1507
    }
 
1508
  } while (level > 0);
 
1509
  exif_resources=DestroySplayTree(exif_resources);
 
1510
  return(status);
 
1511
}
 
1512
 
 
1513
static MagickBooleanType GetXMPProperty(const Image *image,
 
1514
  const char *property)
 
1515
{
 
1516
  char
 
1517
    *xmp_profile;
 
1518
 
 
1519
  const StringInfo
 
1520
    *profile;
 
1521
 
 
1522
  ExceptionInfo
 
1523
    *exception;
 
1524
 
 
1525
  MagickBooleanType
 
1526
    status;
 
1527
 
 
1528
  register const char
 
1529
    *p;
 
1530
 
 
1531
  XMLTreeInfo
 
1532
    *child,
 
1533
    *description,
 
1534
    *node,
 
1535
    *rdf,
 
1536
    *xmp;
 
1537
 
 
1538
  profile=GetImageProfile(image,"xmp");
 
1539
  if (profile == (StringInfo *) NULL)
 
1540
    return(MagickFalse);
 
1541
  if ((property == (const char *) NULL) || (*property == '\0'))
 
1542
    return(MagickFalse);
 
1543
  xmp_profile=StringInfoToString(profile);
 
1544
  if (xmp_profile == (char *) NULL)
 
1545
    return(MagickFalse);
 
1546
  for (p=xmp_profile; *p != '\0'; p++)
 
1547
    if ((*p == '<') && (*(p+1) == 'x'))
 
1548
      break;
 
1549
  exception=AcquireExceptionInfo();
 
1550
  xmp=NewXMLTree((char *) p,exception);
 
1551
  xmp_profile=DestroyString(xmp_profile);
 
1552
  exception=DestroyExceptionInfo(exception);
 
1553
  if (xmp == (XMLTreeInfo *) NULL)
 
1554
    return(MagickFalse);
 
1555
  status=MagickFalse;
 
1556
  rdf=GetXMLTreeChild(xmp,"rdf:RDF");
 
1557
  if (rdf != (XMLTreeInfo *) NULL)
 
1558
    {
 
1559
      if (image->properties == (void *) NULL)
 
1560
        ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
 
1561
          RelinquishMagickMemory,RelinquishMagickMemory);
 
1562
      description=GetXMLTreeChild(rdf,"rdf:Description");
 
1563
      while (description != (XMLTreeInfo *) NULL)
 
1564
      {
 
1565
        node=GetXMLTreeChild(description,(const char *) NULL);
 
1566
        while (node != (XMLTreeInfo *) NULL)
 
1567
        {
 
1568
          child=GetXMLTreeChild(node,(const char *) NULL);
 
1569
          if (child == (XMLTreeInfo *) NULL)
 
1570
            (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
1571
              ConstantString(GetXMLTreeTag(node)),
 
1572
              ConstantString(GetXMLTreeContent(node)));
 
1573
          while (child != (XMLTreeInfo *) NULL)
 
1574
          {
 
1575
            if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
 
1576
              (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
1577
                ConstantString(GetXMLTreeTag(child)),
 
1578
                ConstantString(GetXMLTreeContent(child)));
 
1579
            child=GetXMLTreeSibling(child);
 
1580
          }
 
1581
          node=GetXMLTreeSibling(node);
 
1582
        }
 
1583
        description=GetNextXMLTreeTag(description);
 
1584
      }
 
1585
    }
 
1586
  xmp=DestroyXMLTree(xmp);
 
1587
  return(status);
 
1588
}
 
1589
 
 
1590
static char *TracePSClippath(const unsigned char *blob,size_t length,
 
1591
  const size_t magick_unused(columns),
 
1592
  const size_t magick_unused(rows))
 
1593
{
 
1594
  char
 
1595
    *path,
 
1596
    *message;
 
1597
 
 
1598
  MagickBooleanType
 
1599
    in_subpath;
 
1600
 
 
1601
  PointInfo
 
1602
    first[3],
 
1603
    last[3],
 
1604
    point[3];
 
1605
 
 
1606
  register ssize_t
 
1607
    i,
 
1608
    x;
 
1609
 
 
1610
  ssize_t
 
1611
    knot_count,
 
1612
    selector,
 
1613
    y;
 
1614
 
 
1615
  path=AcquireString((char *) NULL);
 
1616
  if (path == (char *) NULL)
 
1617
    return((char *) NULL);
 
1618
  message=AcquireString((char *) NULL);
 
1619
  (void) FormatMagickString(message,MaxTextExtent,"/ClipImage\n");
 
1620
  (void) ConcatenateString(&path,message);
 
1621
  (void) FormatMagickString(message,MaxTextExtent,"{\n");
 
1622
  (void) ConcatenateString(&path,message);
 
1623
  (void) FormatMagickString(message,MaxTextExtent,"  /c {curveto} bind def\n");
 
1624
  (void) ConcatenateString(&path,message);
 
1625
  (void) FormatMagickString(message,MaxTextExtent,"  /l {lineto} bind def\n");
 
1626
  (void) ConcatenateString(&path,message);
 
1627
  (void) FormatMagickString(message,MaxTextExtent,"  /m {moveto} bind def\n");
 
1628
  (void) ConcatenateString(&path,message);
 
1629
  (void) FormatMagickString(message,MaxTextExtent,
 
1630
    "  /v {currentpoint 6 2 roll curveto} bind def\n");
 
1631
  (void) ConcatenateString(&path,message);
 
1632
  (void) FormatMagickString(message,MaxTextExtent,
 
1633
    "  /y {2 copy curveto} bind def\n");
 
1634
  (void) ConcatenateString(&path,message);
 
1635
  (void) FormatMagickString(message,MaxTextExtent,
 
1636
    "  /z {closepath} bind def\n");
 
1637
  (void) ConcatenateString(&path,message);
 
1638
  (void) FormatMagickString(message,MaxTextExtent,"  newpath\n");
 
1639
  (void) ConcatenateString(&path,message);
 
1640
  /*
 
1641
    The clipping path format is defined in "Adobe Photoshop File
 
1642
    Formats Specification" version 6.0 downloadable from adobe.com.
 
1643
  */
 
1644
  (void) ResetMagickMemory(point,0,sizeof(point));
 
1645
  (void) ResetMagickMemory(first,0,sizeof(first));
 
1646
  (void) ResetMagickMemory(last,0,sizeof(last));
 
1647
  knot_count=0;
 
1648
  in_subpath=MagickFalse;
 
1649
  while (length > 0)
 
1650
  {
 
1651
    selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
 
1652
    switch (selector)
 
1653
    {
 
1654
      case 0:
 
1655
      case 3:
 
1656
      {
 
1657
        if (knot_count != 0)
 
1658
          {
 
1659
            blob+=24;
 
1660
            length-=MagickMin(24,(ssize_t) length);
 
1661
            break;
 
1662
          }
 
1663
        /*
 
1664
          Expected subpath length record.
 
1665
        */
 
1666
        knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
 
1667
        blob+=22;
 
1668
        length-=MagickMin(22,(ssize_t) length);
 
1669
        break;
 
1670
      }
 
1671
      case 1:
 
1672
      case 2:
 
1673
      case 4:
 
1674
      case 5:
 
1675
      {
 
1676
        if (knot_count == 0)
 
1677
          {
 
1678
            /*
 
1679
              Unexpected subpath knot
 
1680
            */
 
1681
            blob+=24;
 
1682
            length-=MagickMin(24,(ssize_t) length);
 
1683
            break;
 
1684
          }
 
1685
        /*
 
1686
          Add sub-path knot
 
1687
        */
 
1688
        for (i=0; i < 3; i++)
 
1689
        {
 
1690
          size_t
 
1691
            xx,
 
1692
            yy;
 
1693
 
 
1694
          yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
 
1695
          xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
 
1696
          x=(ssize_t) xx;
 
1697
          if (xx > 2147483647)
 
1698
            x=(ssize_t) xx-4294967295-1;
 
1699
          y=(ssize_t) yy;
 
1700
          if (yy > 2147483647)
 
1701
            y=(ssize_t) yy-4294967295-1;
 
1702
          point[i].x=(double) x/4096/4096;
 
1703
          point[i].y=1.0-(double) y/4096/4096;
 
1704
        }
 
1705
        if (in_subpath == MagickFalse)
 
1706
          {
 
1707
            (void) FormatMagickString(message,MaxTextExtent,"  %g %g m\n",
 
1708
              point[1].x,point[1].y);
 
1709
            for (i=0; i < 3; i++)
 
1710
            {
 
1711
              first[i]=point[i];
 
1712
              last[i]=point[i];
 
1713
            }
 
1714
          }
 
1715
        else
 
1716
          {
 
1717
            /*
 
1718
              Handle special cases when Bezier curves are used to describe
 
1719
              corners and straight lines.
 
1720
            */
 
1721
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
 
1722
                (point[0].x == point[1].x) && (point[0].y == point[1].y))
 
1723
              (void) FormatMagickString(message,MaxTextExtent,
 
1724
                "  %g %g l\n",point[1].x,point[1].y);
 
1725
            else
 
1726
              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
 
1727
                (void) FormatMagickString(message,MaxTextExtent,
 
1728
                  "  %g %g %g %g v\n",point[0].x,point[0].y,
 
1729
                  point[1].x,point[1].y);
 
1730
              else
 
1731
                if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
 
1732
                  (void) FormatMagickString(message,MaxTextExtent,
 
1733
                    "  %g %g %g %g y\n",last[2].x,last[2].y,
 
1734
                    point[1].x,point[1].y);
 
1735
                else
 
1736
                  (void) FormatMagickString(message,MaxTextExtent,
 
1737
                    "  %g %g %g %g %g %g c\n",last[2].x,
 
1738
                    last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
 
1739
            for (i=0; i < 3; i++)
 
1740
              last[i]=point[i];
 
1741
          }
 
1742
        (void) ConcatenateString(&path,message);
 
1743
        in_subpath=MagickTrue;
 
1744
        knot_count--;
 
1745
        /*
 
1746
          Close the subpath if there are no more knots.
 
1747
        */
 
1748
        if (knot_count == 0)
 
1749
          {
 
1750
            /*
 
1751
              Same special handling as above except we compare to the
 
1752
              first point in the path and close the path.
 
1753
            */
 
1754
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
 
1755
                (first[0].x == first[1].x) && (first[0].y == first[1].y))
 
1756
              (void) FormatMagickString(message,MaxTextExtent,
 
1757
                "  %g %g l z\n",first[1].x,first[1].y);
 
1758
            else
 
1759
              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
 
1760
                (void) FormatMagickString(message,MaxTextExtent,
 
1761
                  "  %g %g %g %g v z\n",first[0].x,first[0].y,
 
1762
                  first[1].x,first[1].y);
 
1763
              else
 
1764
                if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
 
1765
                  (void) FormatMagickString(message,MaxTextExtent,
 
1766
                    "  %g %g %g %g y z\n",last[2].x,last[2].y,
 
1767
                    first[1].x,first[1].y);
 
1768
                else
 
1769
                  (void) FormatMagickString(message,MaxTextExtent,
 
1770
                    "  %g %g %g %g %g %g c z\n",last[2].x,
 
1771
                    last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
 
1772
            (void) ConcatenateString(&path,message);
 
1773
            in_subpath=MagickFalse;
 
1774
          }
 
1775
        break;
 
1776
      }
 
1777
      case 6:
 
1778
      case 7:
 
1779
      case 8:
 
1780
      default:
 
1781
      {
 
1782
        blob+=24;
 
1783
        length-=MagickMin(24,(ssize_t) length);
 
1784
        break;
 
1785
      }
 
1786
    }
 
1787
  }
 
1788
  /*
 
1789
    Returns an empty PS path if the path has no knots.
 
1790
  */
 
1791
  (void) FormatMagickString(message,MaxTextExtent,"  eoclip\n");
 
1792
  (void) ConcatenateString(&path,message);
 
1793
  (void) FormatMagickString(message,MaxTextExtent,"} bind def");
 
1794
  (void) ConcatenateString(&path,message);
 
1795
  message=DestroyString(message);
 
1796
  return(path);
 
1797
}
 
1798
 
 
1799
static char *TraceSVGClippath(const unsigned char *blob,size_t length,
 
1800
  const size_t columns,const size_t rows)
 
1801
{
 
1802
  char
 
1803
    *path,
 
1804
    *message;
 
1805
 
 
1806
  MagickBooleanType
 
1807
    in_subpath;
 
1808
 
 
1809
  PointInfo
 
1810
    first[3],
 
1811
    last[3],
 
1812
    point[3];
 
1813
 
 
1814
  register ssize_t
 
1815
    i;
 
1816
 
 
1817
  ssize_t
 
1818
    knot_count,
 
1819
    selector,
 
1820
    x,
 
1821
    y;
 
1822
 
 
1823
  path=AcquireString((char *) NULL);
 
1824
  if (path == (char *) NULL)
 
1825
    return((char *) NULL);
 
1826
  message=AcquireString((char *) NULL);
 
1827
  (void) FormatMagickString(message,MaxTextExtent,
 
1828
    "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
 
1829
  (void) ConcatenateString(&path,message);
 
1830
  (void) FormatMagickString(message,MaxTextExtent,
 
1831
    "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
 
1832
  (void) ConcatenateString(&path,message);
 
1833
  (void) FormatMagickString(message,MaxTextExtent,"<g>\n");
 
1834
  (void) ConcatenateString(&path,message);
 
1835
  (void) FormatMagickString(message,MaxTextExtent,
 
1836
    "<path style=\"fill:#00000000;stroke:#00000000;");
 
1837
  (void) ConcatenateString(&path,message);
 
1838
  (void) FormatMagickString(message,MaxTextExtent,
 
1839
    "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
 
1840
  (void) ConcatenateString(&path,message);
 
1841
  (void) ResetMagickMemory(point,0,sizeof(point));
 
1842
  (void) ResetMagickMemory(first,0,sizeof(first));
 
1843
  (void) ResetMagickMemory(last,0,sizeof(last));
 
1844
  knot_count=0;
 
1845
  in_subpath=MagickFalse;
 
1846
  while (length != 0)
 
1847
  {
 
1848
    selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
 
1849
    switch (selector)
 
1850
    {
 
1851
      case 0:
 
1852
      case 3:
 
1853
      {
 
1854
        if (knot_count != 0)
 
1855
          {
 
1856
            blob+=24;
 
1857
            length-=MagickMin(24,(ssize_t) length);
 
1858
            break;
 
1859
          }
 
1860
        /*
 
1861
          Expected subpath length record.
 
1862
        */
 
1863
        knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
 
1864
        blob+=22;
 
1865
        length-=MagickMin(22,(ssize_t) length);
 
1866
        break;
 
1867
      }
 
1868
      case 1:
 
1869
      case 2:
 
1870
      case 4:
 
1871
      case 5:
 
1872
      {
 
1873
        if (knot_count == 0)
 
1874
          {
 
1875
            /*
 
1876
              Unexpected subpath knot.
 
1877
            */
 
1878
            blob+=24;
 
1879
            length-=MagickMin(24,(ssize_t) length);
 
1880
            break;
 
1881
          }
 
1882
        /*
 
1883
          Add sub-path knot
 
1884
        */
 
1885
        for (i=0; i < 3; i++)
 
1886
        {
 
1887
          size_t
 
1888
            xx,
 
1889
            yy;
 
1890
 
 
1891
          yy=ReadPropertyMSBLong(&blob,&length);
 
1892
          xx=ReadPropertyMSBLong(&blob,&length);
 
1893
          x=(ssize_t) xx;
 
1894
          if (xx > 2147483647)
 
1895
            x=(ssize_t) xx-4294967295-1;
 
1896
          y=(ssize_t) yy;
 
1897
          if (yy > 2147483647)
 
1898
            y=(ssize_t) yy-4294967295-1;
 
1899
          point[i].x=(double) x*columns/4096/4096;
 
1900
          point[i].y=(double) y*rows/4096/4096;
 
1901
        }
 
1902
        if (in_subpath == MagickFalse)
 
1903
          {
 
1904
            (void) FormatMagickString(message,MaxTextExtent,"M %g,%g\n",
 
1905
              point[1].x,point[1].y);
 
1906
            for (i=0; i < 3; i++)
 
1907
            {
 
1908
              first[i]=point[i];
 
1909
              last[i]=point[i];
 
1910
            }
 
1911
          }
 
1912
        else
 
1913
          {
 
1914
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
 
1915
                (point[0].x == point[1].x) && (point[0].y == point[1].y))
 
1916
              (void) FormatMagickString(message,MaxTextExtent,"L %g,%g\n",
 
1917
                point[1].x,point[1].y);
 
1918
            else
 
1919
              (void) FormatMagickString(message,MaxTextExtent,
 
1920
                "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
 
1921
                point[0].x,point[0].y,point[1].x,point[1].y);
 
1922
            for (i=0; i < 3; i++)
 
1923
              last[i]=point[i];
 
1924
          }
 
1925
        (void) ConcatenateString(&path,message);
 
1926
        in_subpath=MagickTrue;
 
1927
        knot_count--;
 
1928
        /*
 
1929
          Close the subpath if there are no more knots.
 
1930
        */
 
1931
        if (knot_count == 0)
 
1932
          {
 
1933
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
 
1934
                (first[0].x == first[1].x) && (first[0].y == first[1].y))
 
1935
              (void) FormatMagickString(message,MaxTextExtent,
 
1936
                "L %g,%g Z\n",first[1].x,first[1].y);
 
1937
            else
 
1938
              {
 
1939
                (void) FormatMagickString(message,MaxTextExtent,
 
1940
                  "C %g,%g %g,%g %g,%g Z\n",last[2].x,
 
1941
                  last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
 
1942
                (void) ConcatenateString(&path,message);
 
1943
              }
 
1944
            in_subpath=MagickFalse;
 
1945
          }
 
1946
        break;
 
1947
      }
 
1948
      case 6:
 
1949
      case 7:
 
1950
      case 8:
 
1951
      default:
 
1952
      {
 
1953
        blob+=24;
 
1954
        length-=MagickMin(24,(ssize_t) length);
 
1955
        break;
 
1956
      }
 
1957
    }
 
1958
  }
 
1959
  /*
 
1960
    Return an empty SVG image if the path does not have knots.
 
1961
  */
 
1962
  (void) FormatMagickString(message,MaxTextExtent,"\"/>\n");
 
1963
  (void) ConcatenateString(&path,message);
 
1964
  (void) FormatMagickString(message,MaxTextExtent,"</g>\n");
 
1965
  (void) ConcatenateString(&path,message);
 
1966
  (void) FormatMagickString(message,MaxTextExtent,"</svg>\n");
 
1967
  (void) ConcatenateString(&path,message);
 
1968
  message=DestroyString(message);
 
1969
  return(path);
 
1970
}
 
1971
 
 
1972
MagickExport const char *GetImageProperty(const Image *image,
 
1973
  const char *property)
 
1974
{
 
1975
  ExceptionInfo
 
1976
    *exception;
 
1977
 
 
1978
  FxInfo
 
1979
    *fx_info;
 
1980
 
 
1981
  MagickRealType
 
1982
    alpha;
 
1983
 
 
1984
  MagickStatusType
 
1985
    status;
 
1986
 
 
1987
  register const char
 
1988
    *p;
 
1989
 
 
1990
  assert(image != (Image *) NULL);
 
1991
  assert(image->signature == MagickSignature);
 
1992
  if (image->debug != MagickFalse)
 
1993
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
1994
  p=(const char *) NULL;
 
1995
  if (image->properties != (void *) NULL)
 
1996
    {
 
1997
      if (property == (const char *) NULL)
 
1998
        {
 
1999
          ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
 
2000
          p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
 
2001
            image->properties);
 
2002
          return(p);
 
2003
        }
 
2004
      if (LocaleNCompare("fx:",property,3) != 0)
 
2005
        {
 
2006
          p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
2007
            image->properties,property);
 
2008
          if (p != (const char *) NULL)
 
2009
            return(p);
 
2010
        }
 
2011
    }
 
2012
  if ((property == (const char *) NULL) ||
 
2013
      (strchr(property,':') == (char *) NULL))
 
2014
    return(p);
 
2015
  exception=(&((Image *) image)->exception);
 
2016
  switch (*property)
 
2017
  {
 
2018
    case '8':
 
2019
    {
 
2020
      if (LocaleNCompare("8bim:",property,5) == 0)
 
2021
        {
 
2022
          if ((Get8BIMProperty(image,property) != MagickFalse) &&
 
2023
              (image->properties != (void *) NULL))
 
2024
            {
 
2025
              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
2026
                image->properties,property);
 
2027
              return(p);
 
2028
            }
 
2029
        }
 
2030
      break;
 
2031
    }
 
2032
    case 'E':
 
2033
    case 'e':
 
2034
    {
 
2035
      if (LocaleNCompare("exif:",property,5) == 0)
 
2036
        {
 
2037
          if ((GetEXIFProperty(image,property) != MagickFalse) &&
 
2038
              (image->properties != (void *) NULL))
 
2039
            {
 
2040
              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
2041
                image->properties,property);
 
2042
              return(p);
 
2043
            }
 
2044
        }
 
2045
      break;
 
2046
    }
 
2047
    case 'F':
 
2048
    case 'f':
 
2049
    {
 
2050
      if (LocaleNCompare("fx:",property,3) == 0)
 
2051
        {
 
2052
          fx_info=AcquireFxInfo(image,property+3);
 
2053
          status=FxEvaluateChannelExpression(fx_info,DefaultChannels,0,0,&alpha,
 
2054
            exception);
 
2055
          fx_info=DestroyFxInfo(fx_info);
 
2056
          if (status != MagickFalse)
 
2057
            {
 
2058
              char
 
2059
                value[MaxTextExtent];
 
2060
 
 
2061
              (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2062
                GetMagickPrecision(),(double) alpha);
 
2063
              (void) SetImageProperty((Image *) image,property,value);
 
2064
            }
 
2065
          if (image->properties != (void *) NULL)
 
2066
            {
 
2067
              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
2068
                image->properties,property);
 
2069
              return(p);
 
2070
            }
 
2071
        }
 
2072
      break;
 
2073
    }
 
2074
    case 'I':
 
2075
    case 'i':
 
2076
    {
 
2077
      if (LocaleNCompare("iptc:",property,5) == 0)
 
2078
        {
 
2079
          if ((GetIPTCProperty(image,property) != MagickFalse) &&
 
2080
              (image->properties != (void *) NULL))
 
2081
            {
 
2082
              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
2083
                image->properties,property);
 
2084
              return(p);
 
2085
            }
 
2086
        }
 
2087
      break;
 
2088
    }
 
2089
    case 'P':
 
2090
    case 'p':
 
2091
    {
 
2092
      if (LocaleNCompare("pixel:",property,6) == 0)
 
2093
        {
 
2094
          MagickPixelPacket
 
2095
            pixel;
 
2096
 
 
2097
          GetMagickPixelPacket(image,&pixel);
 
2098
          fx_info=AcquireFxInfo(image,property+6);
 
2099
          status=FxEvaluateChannelExpression(fx_info,RedChannel,0,0,&alpha,
 
2100
            exception);
 
2101
          pixel.red=(MagickRealType) QuantumRange*alpha;
 
2102
          status|=FxEvaluateChannelExpression(fx_info,GreenChannel,0,0,&alpha,
 
2103
            exception);
 
2104
          pixel.green=(MagickRealType) QuantumRange*alpha;
 
2105
          status|=FxEvaluateChannelExpression(fx_info,BlueChannel,0,0,&alpha,
 
2106
            exception);
 
2107
          pixel.blue=(MagickRealType) QuantumRange*alpha;
 
2108
          status|=FxEvaluateChannelExpression(fx_info,OpacityChannel,0,0,&alpha,
 
2109
            exception);
 
2110
          pixel.opacity=(MagickRealType) QuantumRange*(1.0-alpha);
 
2111
          if (image->colorspace == CMYKColorspace)
 
2112
            {
 
2113
              status|=FxEvaluateChannelExpression(fx_info,BlackChannel,0,0,
 
2114
                &alpha,exception);
 
2115
              pixel.index=(MagickRealType) QuantumRange*alpha;
 
2116
            }
 
2117
          fx_info=DestroyFxInfo(fx_info);
 
2118
          if (status != MagickFalse)
 
2119
            {
 
2120
              char
 
2121
                name[MaxTextExtent];
 
2122
 
 
2123
              (void) QueryMagickColorname(image,&pixel,SVGCompliance,name,
 
2124
                exception);
 
2125
              (void) SetImageProperty((Image *) image,property,name);
 
2126
              return(GetImageProperty(image,property));
 
2127
            }
 
2128
        }
 
2129
      break;
 
2130
    }
 
2131
    case 'X':
 
2132
    case 'x':
 
2133
    {
 
2134
      if (LocaleNCompare("xmp:",property,4) == 0)
 
2135
        {
 
2136
          if ((GetXMPProperty(image,property) != MagickFalse) &&
 
2137
              (image->properties != (void *) NULL))
 
2138
            {
 
2139
              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
 
2140
                image->properties,property);
 
2141
              return(p);
 
2142
            }
 
2143
        }
 
2144
      break;
 
2145
    }
 
2146
    default:
 
2147
      break;
 
2148
  }
 
2149
  return(p);
 
2150
}
 
2151
 
 
2152
/*
 
2153
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2154
%                                                                             %
 
2155
%                                                                             %
 
2156
%                                                                             %
 
2157
+   G e t M a g i c k P r o p e r t y                                         %
 
2158
%                                                                             %
 
2159
%                                                                             %
 
2160
%                                                                             %
 
2161
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2162
%
 
2163
%  GetMagickProperty() gets a value associated with an image property.
 
2164
%
 
2165
%  The format of the GetMagickProperty method is:
 
2166
%
 
2167
%      const char *GetMagickProperty(const ImageInfo *image_info,
 
2168
%        Image *image,const char *key)
 
2169
%
 
2170
%  A description of each parameter follows:
 
2171
%
 
2172
%    o image_info: the image info.
 
2173
%
 
2174
%    o image: the image.
 
2175
%
 
2176
%    o key: the key.
 
2177
%
 
2178
*/
 
2179
MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
 
2180
  Image *image,const char *property)
 
2181
{
 
2182
  char
 
2183
    value[MaxTextExtent],
 
2184
    filename[MaxTextExtent];
 
2185
 
 
2186
  *value='\0';
 
2187
  switch (*property)
 
2188
  {
 
2189
    case 'b':
 
2190
    {
 
2191
      if (LocaleNCompare("base",property,4) == 0)
 
2192
        {
 
2193
          GetPathComponent(image->magick_filename,BasePath,filename);
 
2194
          (void) CopyMagickString(value,filename,MaxTextExtent);
 
2195
          break;
 
2196
        }
 
2197
      break;
 
2198
    }
 
2199
    case 'c':
 
2200
    {
 
2201
      if (LocaleNCompare("channels",property,8) == 0)
 
2202
        {
 
2203
          /*
 
2204
            Image channels.
 
2205
          */
 
2206
          (void) FormatMagickString(value,MaxTextExtent,"%s",
 
2207
            CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
 
2208
            image->colorspace));
 
2209
          LocaleLower(value);
 
2210
          if (image->matte != MagickFalse)
 
2211
            (void) ConcatenateMagickString(value,"a",MaxTextExtent);
 
2212
          break;
 
2213
        }
 
2214
      if (LocaleNCompare("colorspace",property,10) == 0)
 
2215
        {
 
2216
          ColorspaceType
 
2217
            colorspace;
 
2218
 
 
2219
          /*
 
2220
            Image storage class and colorspace.
 
2221
          */
 
2222
          colorspace=image->colorspace;
 
2223
          if (IsGrayImage(image,&image->exception) != MagickFalse)
 
2224
            colorspace=GRAYColorspace;
 
2225
          (void) FormatMagickString(value,MaxTextExtent,"%s",
 
2226
            CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
 
2227
            colorspace));
 
2228
          break;
 
2229
        }
 
2230
      if (LocaleNCompare("copyright",property,9) == 0)
 
2231
        {
 
2232
          (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
 
2233
          break;
 
2234
        }
 
2235
      break;
 
2236
    }
 
2237
    case 'd':
 
2238
    {
 
2239
      if (LocaleNCompare("depth",property,5) == 0)
 
2240
        {
 
2241
          (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
 
2242
            image->depth);
 
2243
          break;
 
2244
        }
 
2245
      if (LocaleNCompare("directory",property,9) == 0)
 
2246
        {
 
2247
          GetPathComponent(image->magick_filename,HeadPath,filename);
 
2248
          (void) CopyMagickString(value,filename,MaxTextExtent);
 
2249
          break;
 
2250
        }
 
2251
      break;
 
2252
    }
 
2253
    case 'e':
 
2254
    {
 
2255
      if (LocaleNCompare("extension",property,9) == 0)
 
2256
        {
 
2257
          GetPathComponent(image->magick_filename,ExtensionPath,filename);
 
2258
          (void) CopyMagickString(value,filename,MaxTextExtent);
 
2259
          break;
 
2260
        }
 
2261
      break;
 
2262
    }
 
2263
    case 'g':
 
2264
    {
 
2265
      if (LocaleNCompare("group",property,5) == 0)
 
2266
        {
 
2267
          (void) FormatMagickString(value,MaxTextExtent,"0x%lx",
 
2268
            (unsigned long) image_info->group);
 
2269
          break;
 
2270
        }
 
2271
      break;
 
2272
    }
 
2273
    case 'h':
 
2274
    {
 
2275
      if (LocaleNCompare("height",property,6) == 0)
 
2276
        {
 
2277
          (void) FormatMagickString(value,MaxTextExtent,"%.20g",
 
2278
            image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
 
2279
          break;
 
2280
        }
 
2281
      break;
 
2282
    }
 
2283
    case 'i':
 
2284
    {
 
2285
      if (LocaleNCompare("input",property,5) == 0)
 
2286
        {
 
2287
          (void) CopyMagickString(value,image->filename,MaxTextExtent);
 
2288
          break;
 
2289
        }
 
2290
      break;
 
2291
    }
 
2292
    case 'k':
 
2293
    {
 
2294
      if (LocaleNCompare("kurtosis",property,8) == 0)
 
2295
        {
 
2296
          double
 
2297
            kurtosis,
 
2298
            skewness;
 
2299
 
 
2300
          (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
 
2301
            &skewness,&image->exception);
 
2302
          (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2303
            GetMagickPrecision(),kurtosis);
 
2304
          break;
 
2305
        }
 
2306
      break;
 
2307
    }
 
2308
    case 'm':
 
2309
    {
 
2310
      if (LocaleNCompare("magick",property,6) == 0)
 
2311
        {
 
2312
          (void) CopyMagickString(value,image->magick,MaxTextExtent);
 
2313
          break;
 
2314
        }
 
2315
      if (LocaleNCompare("max",property,3) == 0)
 
2316
        {
 
2317
          double
 
2318
            maximum,
 
2319
            minimum;
 
2320
 
 
2321
          (void) GetImageChannelRange(image,image_info->channel,&minimum,
 
2322
            &maximum,&image->exception);
 
2323
          (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2324
            GetMagickPrecision(),maximum);
 
2325
          break;
 
2326
        }
 
2327
      if (LocaleNCompare("mean",property,4) == 0)
 
2328
        {
 
2329
          double
 
2330
            mean,
 
2331
            standard_deviation;
 
2332
 
 
2333
          (void) GetImageChannelMean(image,image_info->channel,&mean,
 
2334
            &standard_deviation,&image->exception);
 
2335
          (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2336
            GetMagickPrecision(),mean);
 
2337
          break;
 
2338
        }
 
2339
      if (LocaleNCompare("min",property,3) == 0)
 
2340
        {
 
2341
          double
 
2342
            maximum,
 
2343
            minimum;
 
2344
 
 
2345
          (void) GetImageChannelRange(image,image_info->channel,&minimum,
 
2346
            &maximum,&image->exception);
 
2347
          (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2348
            GetMagickPrecision(),minimum);
 
2349
          break;
 
2350
        }
 
2351
      break;
 
2352
    }
 
2353
    case 'n':
 
2354
    {
 
2355
      if (LocaleNCompare("name",property,4) == 0)
 
2356
        {
 
2357
          (void) CopyMagickString(value,filename,MaxTextExtent);
 
2358
          break;
 
2359
        }
 
2360
     break;
 
2361
    }
 
2362
    case 'o':
 
2363
    {
 
2364
      if (LocaleNCompare("opaque",property,6) == 0)
 
2365
        {
 
2366
          MagickBooleanType
 
2367
            opaque;
 
2368
 
 
2369
          opaque=IsOpaqueImage(image,&image->exception);
 
2370
          (void) CopyMagickString(value,opaque == MagickFalse ? "false" :
 
2371
            "true",MaxTextExtent);
 
2372
          break;
 
2373
        }
 
2374
      if (LocaleNCompare("output",property,6) == 0)
 
2375
        {
 
2376
          (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
 
2377
          break;
 
2378
        }
 
2379
     break;
 
2380
    }
 
2381
    case 'p':
 
2382
    {
 
2383
      if (LocaleNCompare("page",property,4) == 0)
 
2384
        {
 
2385
          (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
 
2386
              GetImageIndexInList(image)+1);
 
2387
          break;
 
2388
        }
 
2389
      break;
 
2390
    }
 
2391
    case 's':
 
2392
    {
 
2393
      if (LocaleNCompare("size",property,4) == 0)
 
2394
        {
 
2395
          char
 
2396
            format[MaxTextExtent];
 
2397
 
 
2398
          (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
 
2399
          (void) FormatMagickString(value,MaxTextExtent,"%sB",format);
 
2400
          break;
 
2401
        }
 
2402
      if (LocaleNCompare("scenes",property,6) == 0)
 
2403
        {
 
2404
          (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
 
2405
            GetImageListLength(image));
 
2406
          break;
 
2407
        }
 
2408
      if (LocaleNCompare("scene",property,5) == 0)
 
2409
        {
 
2410
          (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
 
2411
            image->scene);
 
2412
          if (image_info->number_scenes != 0)
 
2413
            (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
 
2414
              image_info->scene);
 
2415
          break;
 
2416
        }
 
2417
      if (LocaleNCompare("skewness",property,8) == 0)
 
2418
        {
 
2419
          double
 
2420
            kurtosis,
 
2421
            skewness;
 
2422
 
 
2423
          (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
 
2424
            &skewness,&image->exception);
 
2425
          (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2426
            GetMagickPrecision(),skewness);
 
2427
          break;
 
2428
        }
 
2429
      if ((LocaleNCompare("standard-deviation",property,18) == 0) ||
 
2430
          (LocaleNCompare("standard_deviation",property,18) == 0))
 
2431
        {
 
2432
          double
 
2433
            mean,
 
2434
            standard_deviation;
 
2435
 
 
2436
          (void) GetImageChannelMean(image,image_info->channel,&mean,
 
2437
            &standard_deviation,&image->exception);
 
2438
          (void) FormatMagickString(value,MaxTextExtent,"%.*g",
 
2439
            GetMagickPrecision(),standard_deviation);
 
2440
          break;
 
2441
        }
 
2442
       break;
 
2443
    }
 
2444
    case 'u':
 
2445
    {
 
2446
      if (LocaleNCompare("unique",property,6) == 0)
 
2447
        {
 
2448
          (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
 
2449
          (void) CopyMagickString(value,filename,MaxTextExtent);
 
2450
          break;
 
2451
        }
 
2452
      break;
 
2453
    }
 
2454
    case 'v':
 
2455
    {
 
2456
      if (LocaleNCompare("version",property,7) == 0)
 
2457
        {
 
2458
          (void) CopyMagickString(value,GetMagickVersion((size_t *) NULL),
 
2459
            MaxTextExtent);
 
2460
          break;
 
2461
        }
 
2462
      break;
 
2463
    }
 
2464
    case 'w':
 
2465
    {
 
2466
      if (LocaleNCompare("width",property,5) == 0)
 
2467
        {
 
2468
          (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
 
2469
            (image->magick_columns != 0 ? image->magick_columns : 256));
 
2470
          break;
 
2471
        }
 
2472
      break;
 
2473
    }
 
2474
    case 'x':
 
2475
    {
 
2476
      if (LocaleNCompare("xresolution",property,11) == 0)
 
2477
        {
 
2478
          (void) FormatMagickString(value,MaxTextExtent,"%g",
 
2479
            image->x_resolution);
 
2480
          break;
 
2481
        }
 
2482
      break;
 
2483
    }
 
2484
    case 'y':
 
2485
    {
 
2486
      if (LocaleNCompare("yresolution",property,11) == 0)
 
2487
        {
 
2488
          (void) FormatMagickString(value,MaxTextExtent,"%g",
 
2489
            image->y_resolution);
 
2490
          break;
 
2491
        }
 
2492
      break;
 
2493
    }
 
2494
    case 'z':
 
2495
    {
 
2496
      if (LocaleNCompare("zero",property,4) == 0)
 
2497
        {
 
2498
          (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
 
2499
          (void) CopyMagickString(value,filename,MaxTextExtent);
 
2500
          break;
 
2501
        }
 
2502
      break;
 
2503
    }
 
2504
  }
 
2505
  if (*value != '\0')
 
2506
   {
 
2507
     if (image->properties == (void *) NULL)
 
2508
       image->properties=NewSplayTree(CompareSplayTreeString,
 
2509
         RelinquishMagickMemory,RelinquishMagickMemory);
 
2510
     (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
2511
       ConstantString(property),ConstantString(value));
 
2512
   }
 
2513
  return(GetImageProperty(image,property));
 
2514
}
 
2515
 
 
2516
/*
 
2517
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2518
%                                                                             %
 
2519
%                                                                             %
 
2520
%                                                                             %
 
2521
%   G e t N e x t I m a g e P r o p e r t y                                   %
 
2522
%                                                                             %
 
2523
%                                                                             %
 
2524
%                                                                             %
 
2525
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2526
%
 
2527
%  GetNextImageProperty() gets the next image property value.
 
2528
%
 
2529
%  The format of the GetNextImageProperty method is:
 
2530
%
 
2531
%      char *GetNextImageProperty(const Image *image)
 
2532
%
 
2533
%  A description of each parameter follows:
 
2534
%
 
2535
%    o image: the image.
 
2536
%
 
2537
*/
 
2538
MagickExport char *GetNextImageProperty(const Image *image)
 
2539
{
 
2540
  assert(image != (Image *) NULL);
 
2541
  assert(image->signature == MagickSignature);
 
2542
  if (image->debug != MagickFalse)
 
2543
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
2544
      image->filename);
 
2545
  if (image->properties == (void *) NULL)
 
2546
    return((char *) NULL);
 
2547
  return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
 
2548
}
 
2549
 
 
2550
/*
 
2551
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2552
%                                                                             %
 
2553
%                                                                             %
 
2554
%                                                                             %
 
2555
%   I n t e r p r e t I m a g e P r o p e r t i e s                           %
 
2556
%                                                                             %
 
2557
%                                                                             %
 
2558
%                                                                             %
 
2559
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2560
%
 
2561
%  InterpretImageProperties() replaces any embedded formatting characters with
 
2562
%  the appropriate image property and returns the interpretted text.
 
2563
%
 
2564
%  The format of the InterpretImageProperties method is:
 
2565
%
 
2566
%      char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
 
2567
%        const char *embed_text)
 
2568
%
 
2569
%  A description of each parameter follows:
 
2570
%
 
2571
%    o image_info: the image info.
 
2572
%
 
2573
%    o image: the image.
 
2574
%
 
2575
%    o embed_text: the address of a character string containing the embedded
 
2576
%      formatting characters.
 
2577
%
 
2578
*/
 
2579
MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
 
2580
  Image *image,const char *embed_text)
 
2581
{
 
2582
  char
 
2583
    filename[MaxTextExtent],
 
2584
    *interpret_text,
 
2585
    *text;
 
2586
 
 
2587
  const char
 
2588
    *value;
 
2589
 
 
2590
  register char
 
2591
    *q;
 
2592
 
 
2593
  register const char
 
2594
    *p;
 
2595
 
 
2596
  register ssize_t
 
2597
    i;
 
2598
 
 
2599
  size_t
 
2600
    extent,
 
2601
    length;
 
2602
 
 
2603
  assert(image != (Image *) NULL);
 
2604
  assert(image->signature == MagickSignature);
 
2605
  if (image->debug != MagickFalse)
 
2606
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
2607
  if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
 
2608
    return((char *) NULL);
 
2609
  text=(char *) embed_text;
 
2610
  if ((*text == '@') && ((*(text+1) == '-') ||
 
2611
      (IsPathAccessible(text+1) != MagickFalse)))
 
2612
    return(FileToString(embed_text+1,~0,&image->exception));
 
2613
  /*
 
2614
    Translate any embedded format characters.
 
2615
  */
 
2616
  interpret_text=AcquireString(text);
 
2617
  extent=MaxTextExtent;
 
2618
  p=text;
 
2619
  for (q=interpret_text; *p != '\0'; p++)
 
2620
  {
 
2621
    *q='\0';
 
2622
    if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
 
2623
      {
 
2624
        extent+=MaxTextExtent;
 
2625
        interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
 
2626
          MaxTextExtent+1,sizeof(*interpret_text));
 
2627
        if (interpret_text == (char *) NULL)
 
2628
          break;
 
2629
        q=interpret_text+strlen(interpret_text);
 
2630
      }
 
2631
    /*
 
2632
      Process formatting characters in text.
 
2633
    */
 
2634
    if ((*p == '\\') && (*(p+1) == 'r'))
 
2635
      {
 
2636
        *q++='\r';
 
2637
        p++;
 
2638
        continue;
 
2639
      }
 
2640
    if ((*p == '\\') && (*(p+1) == 'n'))
 
2641
      {
 
2642
        *q++='\n';
 
2643
        p++;
 
2644
        continue;
 
2645
      }
 
2646
    if (*p == '\\')
 
2647
      {
 
2648
        p++;
 
2649
        *q++=(*p);
 
2650
        continue;
 
2651
      }
 
2652
    if (*p != '%')
 
2653
      {
 
2654
        *q++=(*p);
 
2655
        continue;
 
2656
      }
 
2657
    p++;
 
2658
    switch (*p)
 
2659
    {
 
2660
      case 'b':
 
2661
      {
 
2662
        char
 
2663
          format[MaxTextExtent];
 
2664
 
 
2665
        /*
 
2666
          File size.
 
2667
        */
 
2668
        (void) FormatMagickString(format,MaxTextExtent,"%.20g",(double)
 
2669
          image->extent);
 
2670
        if (image->extent != (MagickSizeType) ((size_t) image->extent))
 
2671
          (void) FormatMagickSize(image->extent,MagickFalse,format);
 
2672
        q+=ConcatenateMagickString(q,format,extent);
 
2673
        q+=ConcatenateMagickString(q,"B",extent);
 
2674
        break;
 
2675
      }
 
2676
      case 'c':
 
2677
      {
 
2678
        /*
 
2679
          Image comment.
 
2680
        */
 
2681
        value=GetImageProperty(image,"comment");
 
2682
        if (value == (const char *) NULL)
 
2683
          break;
 
2684
        length=strlen(value);
 
2685
        if ((size_t) (q-interpret_text+length+1) >= extent)
 
2686
          {
 
2687
            extent+=length;
 
2688
            interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
 
2689
              MaxTextExtent,sizeof(*interpret_text));
 
2690
            if (interpret_text == (char *) NULL)
 
2691
              break;
 
2692
            q=interpret_text+strlen(interpret_text);
 
2693
          }
 
2694
        (void) CopyMagickString(q,value,extent);
 
2695
        q+=length;
 
2696
        break;
 
2697
      }
 
2698
      case 'd':
 
2699
      case 'e':
 
2700
      case 'f':
 
2701
      case 't':
 
2702
      {
 
2703
        /*
 
2704
          Label segment is the base of the filename.
 
2705
        */
 
2706
        if (*image->magick_filename == '\0')
 
2707
          break;
 
2708
        switch (*p)
 
2709
        {
 
2710
          case 'd':
 
2711
          {
 
2712
            /*
 
2713
              Directory.
 
2714
            */
 
2715
            GetPathComponent(image->magick_filename,HeadPath,filename);
 
2716
            q+=CopyMagickString(q,filename,extent);
 
2717
            break;
 
2718
          }
 
2719
          case 'e':
 
2720
          {
 
2721
            /*
 
2722
              Filename extension.
 
2723
            */
 
2724
            GetPathComponent(image->magick_filename,ExtensionPath,filename);
 
2725
            q+=CopyMagickString(q,filename,extent);
 
2726
            break;
 
2727
          }
 
2728
          case 'f':
 
2729
          {
 
2730
            /*
 
2731
              Filename.
 
2732
            */
 
2733
            GetPathComponent(image->magick_filename,TailPath,filename);
 
2734
            q+=CopyMagickString(q,filename,extent);
 
2735
            break;
 
2736
          }
 
2737
          case 't':
 
2738
          {
 
2739
            /*
 
2740
              Base filename.
 
2741
            */
 
2742
            GetPathComponent(image->magick_filename,BasePath,filename);
 
2743
            q+=CopyMagickString(q,filename,extent);
 
2744
            break;
 
2745
          }
 
2746
        }
 
2747
        break;
 
2748
      }
 
2749
      case 'g':
 
2750
      {
 
2751
        /*
 
2752
          Image geometry.
 
2753
        */
 
2754
        q+=FormatMagickString(q,extent,"%.20gx%.20g%+.20g%+.20g",(double)
 
2755
          image->page.width,(double) image->page.height,(double) image->page.x,
 
2756
          (double) image->page.y);
 
2757
        break;
 
2758
      }
 
2759
      case 'h':
 
2760
      {
 
2761
        /*
 
2762
          Image height.
 
2763
        */
 
2764
        q+=FormatMagickString(q,extent,"%.20g",(double) (image->rows != 0 ?
 
2765
          image->rows : image->magick_rows));
 
2766
        break;
 
2767
      }
 
2768
      case 'i':
 
2769
      {
 
2770
        /*
 
2771
          Image filename.
 
2772
        */
 
2773
        q+=CopyMagickString(q,image->filename,extent);
 
2774
        break;
 
2775
      }
 
2776
      case 'k':
 
2777
      {
 
2778
        /*
 
2779
          Number of unique colors.
 
2780
        */
 
2781
        q+=FormatMagickString(q,extent,"%.20g",(double) GetNumberColors(image,
 
2782
          (FILE *) NULL,&image->exception));
 
2783
        break;
 
2784
      }
 
2785
      case 'l':
 
2786
      {
 
2787
        /*
 
2788
          Image label.
 
2789
        */
 
2790
        value=GetImageProperty(image,"label");
 
2791
        if (value == (const char *) NULL)
 
2792
          break;
 
2793
        length=strlen(value);
 
2794
        if ((size_t) (q-interpret_text+length+1) >= extent)
 
2795
          {
 
2796
            extent+=length;
 
2797
            interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
 
2798
              MaxTextExtent,sizeof(*interpret_text));
 
2799
            if (interpret_text == (char *) NULL)
 
2800
              break;
 
2801
            q=interpret_text+strlen(interpret_text);
 
2802
          }
 
2803
        q+=CopyMagickString(q,value,extent);
 
2804
        break;
 
2805
      }
 
2806
      case 'm':
 
2807
      {
 
2808
        /*
 
2809
          Image format.
 
2810
        */
 
2811
        q+=CopyMagickString(q,image->magick,extent);
 
2812
        break;
 
2813
      }
 
2814
      case 'M':
 
2815
      {
 
2816
        /*
 
2817
          Image magick filename.
 
2818
        */
 
2819
        q+=CopyMagickString(q,image->magick_filename,extent);
 
2820
        break;
 
2821
      }
 
2822
      case 'n':
 
2823
      {
 
2824
        /*
 
2825
          Number of images in the list.
 
2826
        */
 
2827
        q+=FormatMagickString(q,extent,"%.20g",(double)
 
2828
             GetImageListLength(image));
 
2829
        break;
 
2830
      }
 
2831
      case 'o':
 
2832
      {
 
2833
        /*
 
2834
          Image output filename.
 
2835
        */
 
2836
        q+=CopyMagickString(q,image_info->filename,extent);
 
2837
        break;
 
2838
      }
 
2839
      case 'p':
 
2840
      {
 
2841
        /*
 
2842
          Image index in list.
 
2843
        */
 
2844
        q+=FormatMagickString(q,extent,"%.20g",(double)
 
2845
            GetImageIndexInList(image));
 
2846
        break;
 
2847
      }
 
2848
      case 'q':
 
2849
      {
 
2850
        /*
 
2851
          Image depth.
 
2852
        */
 
2853
        q+=FormatMagickString(q,extent,"%.20g",(double)
 
2854
          MAGICKCORE_QUANTUM_DEPTH);
 
2855
        break;
 
2856
      }
 
2857
      case 'r':
 
2858
      {
 
2859
        ColorspaceType
 
2860
          colorspace;
 
2861
 
 
2862
        /*
 
2863
          Image storage class and colorspace.
 
2864
        */
 
2865
        colorspace=image->colorspace;
 
2866
        if (IsGrayImage(image,&image->exception) != MagickFalse)
 
2867
          colorspace=GRAYColorspace;
 
2868
        q+=FormatMagickString(q,extent,"%s%s%s",CommandOptionToMnemonic(
 
2869
          MagickClassOptions,(ssize_t) image->storage_class),
 
2870
          CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
 
2871
          image->matte != MagickFalse ? "Matte" : "");
 
2872
        break;
 
2873
      }
 
2874
      case 's':
 
2875
      {
 
2876
        /*
 
2877
          Image scene number.
 
2878
        */
 
2879
        if (image_info->number_scenes == 0)
 
2880
          q+=FormatMagickString(q,extent,"%.20g",(double) image->scene);
 
2881
        else
 
2882
          q+=FormatMagickString(q,extent,"%.20g",(double) image_info->scene);
 
2883
        break;
 
2884
      }
 
2885
      case 'u':
 
2886
      {
 
2887
        /*
 
2888
          Unique filename.
 
2889
        */
 
2890
        (void) CopyMagickString(filename,image_info->unique,extent);
 
2891
        q+=CopyMagickString(q,filename,extent);
 
2892
        break;
 
2893
      }
 
2894
      case 'w':
 
2895
      {
 
2896
        /*
 
2897
          Image width.
 
2898
        */
 
2899
        q+=FormatMagickString(q,extent,"%.20g",(double) (image->columns != 0 ?
 
2900
          image->columns : image->magick_columns));
 
2901
        break;
 
2902
      }
 
2903
      case 'x':
 
2904
      {
 
2905
        /*
 
2906
          Image horizontal resolution.
 
2907
        */
 
2908
        q+=FormatMagickString(q,extent,"%g %s",image->x_resolution,
 
2909
          CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
 
2910
            image->units));
 
2911
        break;
 
2912
      }
 
2913
      case 'y':
 
2914
      {
 
2915
        /*
 
2916
          Image vertical resolution.
 
2917
        */
 
2918
        q+=FormatMagickString(q,extent,"%g %s",image->y_resolution,
 
2919
          CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
 
2920
          image->units));
 
2921
        break;
 
2922
      }
 
2923
      case 'z':
 
2924
      {
 
2925
        /*
 
2926
          Image depth.
 
2927
        */
 
2928
        q+=FormatMagickString(q,extent,"%.20g",(double) image->depth);
 
2929
        break;
 
2930
      }
 
2931
      case 'A':
 
2932
      {
 
2933
        /*
 
2934
          Image alpha channel.
 
2935
        */
 
2936
        q+=FormatMagickString(q,extent,"%s",CommandOptionToMnemonic(
 
2937
          MagickBooleanOptions,(ssize_t) image->matte));
 
2938
        break;
 
2939
      }
 
2940
      case 'C':
 
2941
      {
 
2942
        /*
 
2943
          Image compression method.
 
2944
        */
 
2945
        q+=FormatMagickString(q,extent,"%s",CommandOptionToMnemonic(
 
2946
          MagickCompressOptions,(ssize_t) image->compression));
 
2947
        break;
 
2948
      }
 
2949
      case 'D':
 
2950
      {
 
2951
        /*
 
2952
          Image dispose method.
 
2953
        */
 
2954
        q+=FormatMagickString(q,extent,"%s",CommandOptionToMnemonic(
 
2955
          MagickDisposeOptions,(ssize_t) image->dispose));
 
2956
        break;
 
2957
      }
 
2958
      case 'G':
 
2959
      {
 
2960
        q+=FormatMagickString(q,extent,"%.20gx%.20g",(double)
 
2961
          image->magick_columns,(double) image->magick_rows);
 
2962
        break;
 
2963
      }
 
2964
      case 'H':
 
2965
      {
 
2966
        q+=FormatMagickString(q,extent,"%.20g",(double) image->page.height);
 
2967
        break;
 
2968
      }
 
2969
      case 'O':
 
2970
      {
 
2971
        q+=FormatMagickString(q,extent,"%+ld%+ld",(long) image->page.x,(long)
 
2972
          image->page.y);
 
2973
        break;
 
2974
      }
 
2975
      case 'P':
 
2976
      {
 
2977
        q+=FormatMagickString(q,extent,"%.20gx%.20g",(double) image->page.width,
 
2978
          (double) image->page.height);
 
2979
        break;
 
2980
      }
 
2981
      case 'Q':
 
2982
      {
 
2983
        q+=FormatMagickString(q,extent,"%.20g",(double) image->quality);
 
2984
        break;
 
2985
      }
 
2986
      case 'S':
 
2987
      {
 
2988
        /*
 
2989
          Image scenes.
 
2990
        */
 
2991
        if (image_info->number_scenes == 0)
 
2992
          q+=CopyMagickString(q,"2147483647",extent);
 
2993
        else
 
2994
          q+=FormatMagickString(q,extent,"%.20g",(double) (image_info->scene+
 
2995
            image_info->number_scenes));
 
2996
        break;
 
2997
      }
 
2998
      case 'T':
 
2999
      {
 
3000
        q+=FormatMagickString(q,extent,"%.20g",(double) image->delay);
 
3001
        break;
 
3002
      }
 
3003
      case 'W':
 
3004
      {
 
3005
        q+=FormatMagickString(q,extent,"%.20g",(double) image->page.width);
 
3006
        break;
 
3007
      }
 
3008
      case 'X':
 
3009
      {
 
3010
        q+=FormatMagickString(q,extent,"%+.20g",(double) image->page.x);
 
3011
        break;
 
3012
      }
 
3013
      case 'Y':
 
3014
      {
 
3015
        q+=FormatMagickString(q,extent,"%+.20g",(double) image->page.y);
 
3016
        break;
 
3017
      }
 
3018
      case 'Z':
 
3019
      {
 
3020
        /*
 
3021
          Unique filename.
 
3022
        */
 
3023
        (void) CopyMagickString(filename,image_info->zero,extent);
 
3024
        q+=CopyMagickString(q,filename,extent);
 
3025
        break;
 
3026
      }
 
3027
      case '[':
 
3028
      {
 
3029
        char
 
3030
          pattern[MaxTextExtent];
 
3031
 
 
3032
        const char
 
3033
          *key,
 
3034
          *value;
 
3035
 
 
3036
        ssize_t
 
3037
          depth;
 
3038
 
 
3039
        /*
 
3040
          Image value.
 
3041
        */
 
3042
        if (strchr(p,']') == (char *) NULL)
 
3043
          break;
 
3044
        depth=1;
 
3045
        p++;
 
3046
        for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
 
3047
        {
 
3048
          if (*p == '[')
 
3049
            depth++;
 
3050
          if (*p == ']')
 
3051
            depth--;
 
3052
          if (depth <= 0)
 
3053
            break;
 
3054
          pattern[i]=(*p++);
 
3055
        }
 
3056
        pattern[i]='\0';
 
3057
        value=GetImageProperty(image,pattern);
 
3058
        if (value != (const char *) NULL)
 
3059
          {
 
3060
            length=strlen(value);
 
3061
            if ((size_t) (q-interpret_text+length+1) >= extent)
 
3062
              {
 
3063
                extent+=length;
 
3064
                interpret_text=(char *) ResizeQuantumMemory(interpret_text,
 
3065
                  extent+MaxTextExtent,sizeof(*interpret_text));
 
3066
                if (interpret_text == (char *) NULL)
 
3067
                  break;
 
3068
                q=interpret_text+strlen(interpret_text);
 
3069
              }
 
3070
            (void) CopyMagickString(q,value,extent);
 
3071
            q+=length;
 
3072
            break;
 
3073
          }
 
3074
        else
 
3075
          if (IsGlob(pattern) != MagickFalse)
 
3076
            {
 
3077
              /*
 
3078
                Iterate over image properties.
 
3079
              */
 
3080
              ResetImagePropertyIterator(image);
 
3081
              key=GetNextImageProperty(image);
 
3082
              while (key != (const char *) NULL)
 
3083
              {
 
3084
                if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
 
3085
                  {
 
3086
                    value=GetImageProperty(image,key);
 
3087
                    if (value != (const char *) NULL)
 
3088
                      {
 
3089
                        length=strlen(key)+strlen(value)+2;
 
3090
                        if ((size_t) (q-interpret_text+length+1) >= extent)
 
3091
                          {
 
3092
                            extent+=length;
 
3093
                            interpret_text=(char *) ResizeQuantumMemory(
 
3094
                              interpret_text,extent+MaxTextExtent,
 
3095
                              sizeof(*interpret_text));
 
3096
                            if (interpret_text == (char *) NULL)
 
3097
                              break;
 
3098
                            q=interpret_text+strlen(interpret_text);
 
3099
                          }
 
3100
                        q+=FormatMagickString(q,extent,"%s=%s\n",key,value);
 
3101
                      }
 
3102
                  }
 
3103
                key=GetNextImageProperty(image);
 
3104
              }
 
3105
            }
 
3106
        value=GetMagickProperty(image_info,image,pattern);
 
3107
        if (value != (const char *) NULL)
 
3108
          {
 
3109
            length=strlen(value);
 
3110
            if ((size_t) (q-interpret_text+length+1) >= extent)
 
3111
              {
 
3112
                extent+=length;
 
3113
                interpret_text=(char *) ResizeQuantumMemory(interpret_text,
 
3114
                  extent+MaxTextExtent,sizeof(*interpret_text));
 
3115
                if (interpret_text == (char *) NULL)
 
3116
                  break;
 
3117
                q=interpret_text+strlen(interpret_text);
 
3118
              }
 
3119
            (void) CopyMagickString(q,value,extent);
 
3120
            q+=length;
 
3121
            break;
 
3122
          }
 
3123
        if (image_info == (ImageInfo *) NULL)
 
3124
          break;
 
3125
        value=GetImageOption(image_info,pattern);
 
3126
        if (value != (char *) NULL)
 
3127
          {
 
3128
            length=strlen(value);
 
3129
            if ((size_t) (q-interpret_text+length+1) >= extent)
 
3130
              {
 
3131
                extent+=length;
 
3132
                interpret_text=(char *) ResizeQuantumMemory(interpret_text,
 
3133
                  extent+MaxTextExtent,sizeof(*interpret_text));
 
3134
                if (interpret_text == (char *) NULL)
 
3135
                  break;
 
3136
                q=interpret_text+strlen(interpret_text);
 
3137
              }
 
3138
            (void) CopyMagickString(q,value,extent);
 
3139
            q+=length;
 
3140
            break;
 
3141
          }
 
3142
        break;
 
3143
      }
 
3144
      case '@':
 
3145
      {
 
3146
        RectangleInfo
 
3147
          page;
 
3148
 
 
3149
        /*
 
3150
          Image bounding box.
 
3151
        */
 
3152
        page=GetImageBoundingBox(image,&image->exception);
 
3153
        q+=FormatMagickString(q,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
 
3154
          (double) page.width,(double) page.height,(double) page.x,(double)
 
3155
          page.y);
 
3156
        break;
 
3157
      }
 
3158
      case '#':
 
3159
      {
 
3160
        /*
 
3161
          Image signature.
 
3162
        */
 
3163
        (void) SignatureImage(image);
 
3164
        value=GetImageProperty(image,"signature");
 
3165
        if (value == (const char *) NULL)
 
3166
          break;
 
3167
        q+=CopyMagickString(q,value,extent);
 
3168
        break;
 
3169
      }
 
3170
      case '%':
 
3171
      {
 
3172
        *q++=(*p);
 
3173
        break;
 
3174
      }
 
3175
      default:
 
3176
      {
 
3177
        *q++='%';
 
3178
        *q++=(*p);
 
3179
        break;
 
3180
      }
 
3181
    }
 
3182
  }
 
3183
  *q='\0';
 
3184
  if (text != (const char *) embed_text)
 
3185
    text=DestroyString(text);
 
3186
  (void) SubstituteString(&interpret_text,"&lt;","<");
 
3187
  (void) SubstituteString(&interpret_text,"&gt;",">");
 
3188
  return(interpret_text);
 
3189
}
 
3190
 
 
3191
/*
 
3192
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3193
%                                                                             %
 
3194
%                                                                             %
 
3195
%                                                                             %
 
3196
%   R e m o v e I m a g e P r o p e r t y                                     %
 
3197
%                                                                             %
 
3198
%                                                                             %
 
3199
%                                                                             %
 
3200
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3201
%
 
3202
%  RemoveImageProperty() removes a property from the image and returns its
 
3203
%  value.
 
3204
%
 
3205
%  The format of the RemoveImageProperty method is:
 
3206
%
 
3207
%      char *RemoveImageProperty(Image *image,const char *property)
 
3208
%
 
3209
%  A description of each parameter follows:
 
3210
%
 
3211
%    o image: the image.
 
3212
%
 
3213
%    o property: the image property.
 
3214
%
 
3215
*/
 
3216
MagickExport char *RemoveImageProperty(Image *image,
 
3217
  const char *property)
 
3218
{
 
3219
  char
 
3220
    *value;
 
3221
 
 
3222
  assert(image != (Image *) NULL);
 
3223
  assert(image->signature == MagickSignature);
 
3224
  if (image->debug != MagickFalse)
 
3225
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
3226
      image->filename);
 
3227
  if (image->properties == (void *) NULL)
 
3228
    return((char *) NULL);
 
3229
  value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
 
3230
    property);
 
3231
  return(value);
 
3232
}
 
3233
 
 
3234
/*
 
3235
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3236
%                                                                             %
 
3237
%                                                                             %
 
3238
%                                                                             %
 
3239
%   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                       %
 
3240
%                                                                             %
 
3241
%                                                                             %
 
3242
%                                                                             %
 
3243
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3244
%
 
3245
%  ResetImagePropertyIterator() resets the image properties iterator.  Use it
 
3246
%  in conjunction with GetNextImageProperty() to iterate over all the values
 
3247
%  associated with an image property.
 
3248
%
 
3249
%  The format of the ResetImagePropertyIterator method is:
 
3250
%
 
3251
%      ResetImagePropertyIterator(Image *image)
 
3252
%
 
3253
%  A description of each parameter follows:
 
3254
%
 
3255
%    o image: the image.
 
3256
%
 
3257
*/
 
3258
MagickExport void ResetImagePropertyIterator(const Image *image)
 
3259
{
 
3260
  assert(image != (Image *) NULL);
 
3261
  assert(image->signature == MagickSignature);
 
3262
  if (image->debug != MagickFalse)
 
3263
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
3264
      image->filename);
 
3265
  if (image->properties == (void *) NULL)
 
3266
    return;
 
3267
  ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
 
3268
}
 
3269
 
 
3270
/*
 
3271
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3272
%                                                                             %
 
3273
%                                                                             %
 
3274
%                                                                             %
 
3275
%   S e t I m a g e P r o p e r t y                                           %
 
3276
%                                                                             %
 
3277
%                                                                             %
 
3278
%                                                                             %
 
3279
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3280
%
 
3281
%  SetImageProperty() associates an value with an image property.
 
3282
%
 
3283
%  The format of the SetImageProperty method is:
 
3284
%
 
3285
%      MagickBooleanType SetImageProperty(Image *image,const char *property,
 
3286
%        const char *value)
 
3287
%
 
3288
%  A description of each parameter follows:
 
3289
%
 
3290
%    o image: the image.
 
3291
%
 
3292
%    o property: the image property.
 
3293
%
 
3294
%    o values: the image property values.
 
3295
%
 
3296
*/
 
3297
MagickExport MagickBooleanType SetImageProperty(Image *image,
 
3298
  const char *property,const char *value)
 
3299
{
 
3300
  ExceptionInfo
 
3301
    *exception;
 
3302
 
 
3303
  MagickBooleanType
 
3304
    status;
 
3305
 
 
3306
  MagickStatusType
 
3307
    flags;
 
3308
 
 
3309
  assert(image != (Image *) NULL);
 
3310
  assert(image->signature == MagickSignature);
 
3311
  if (image->debug != MagickFalse)
 
3312
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
 
3313
      image->filename);
 
3314
  if (image->properties == (void *) NULL)
 
3315
    image->properties=NewSplayTree(CompareSplayTreeString,
 
3316
      RelinquishMagickMemory,RelinquishMagickMemory);
 
3317
  if ((value == (const char *) NULL) || (*value == '\0'))
 
3318
    return(DeleteImageProperty(image,property));
 
3319
  status=MagickTrue;
 
3320
  exception=(&image->exception);
 
3321
  switch (*property)
 
3322
  {
 
3323
    case 'B':
 
3324
    case 'b':
 
3325
    {
 
3326
      if (LocaleCompare(property,"background") == 0)
 
3327
        {
 
3328
          (void) QueryColorDatabase(value,&image->background_color,exception);
 
3329
          break;
 
3330
        }
 
3331
      if (LocaleCompare(property,"bias") == 0)
 
3332
        {
 
3333
          image->bias=SiPrefixToDouble(value,QuantumRange);
 
3334
          break;
 
3335
        }
 
3336
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3337
        ConstantString(property),ConstantString(value));
 
3338
      break;
 
3339
    }
 
3340
    case 'C':
 
3341
    case 'c':
 
3342
    {
 
3343
      if (LocaleCompare(property,"colorspace") == 0)
 
3344
        {
 
3345
          ssize_t
 
3346
            colorspace;
 
3347
 
 
3348
          colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
 
3349
            value);
 
3350
          if (colorspace < 0)
 
3351
            break;
 
3352
          (void) SetImageColorspace(image,(ColorspaceType) colorspace);
 
3353
          break;
 
3354
        }
 
3355
      if (LocaleCompare(property,"compose") == 0)
 
3356
        {
 
3357
          ssize_t
 
3358
            compose;
 
3359
 
 
3360
          compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
 
3361
          if (compose < 0)
 
3362
            break;
 
3363
          image->compose=(CompositeOperator) compose;
 
3364
          break;
 
3365
        }
 
3366
      if (LocaleCompare(property,"compress") == 0)
 
3367
        {
 
3368
          ssize_t
 
3369
            compression;
 
3370
 
 
3371
          compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
 
3372
            value);
 
3373
          if (compression < 0)
 
3374
            break;
 
3375
          image->compression=(CompressionType) compression;
 
3376
          break;
 
3377
        }
 
3378
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3379
        ConstantString(property),ConstantString(value));
 
3380
      break;
 
3381
    }
 
3382
    case 'D':
 
3383
    case 'd':
 
3384
    {
 
3385
      if (LocaleCompare(property,"delay") == 0)
 
3386
        {
 
3387
          GeometryInfo
 
3388
            geometry_info;
 
3389
 
 
3390
          flags=ParseGeometry(value,&geometry_info);
 
3391
          if ((flags & GreaterValue) != 0)
 
3392
            {
 
3393
              if (image->delay > (size_t) floor(geometry_info.rho+0.5))
 
3394
                image->delay=(size_t) floor(geometry_info.rho+0.5);
 
3395
            }
 
3396
          else
 
3397
            if ((flags & LessValue) != 0)
 
3398
              {
 
3399
                if (image->delay < (size_t) floor(geometry_info.rho+0.5))
 
3400
                  image->ticks_per_second=(ssize_t)
 
3401
                    floor(geometry_info.sigma+0.5);
 
3402
              }
 
3403
            else
 
3404
              image->delay=(size_t) floor(geometry_info.rho+0.5);
 
3405
          if ((flags & SigmaValue) != 0)
 
3406
            image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
 
3407
          break;
 
3408
        }
 
3409
      if (LocaleCompare(property,"density") == 0)
 
3410
        {
 
3411
          GeometryInfo
 
3412
            geometry_info;
 
3413
 
 
3414
          flags=ParseGeometry(value,&geometry_info);
 
3415
          image->x_resolution=geometry_info.rho;
 
3416
          image->y_resolution=geometry_info.sigma;
 
3417
          if ((flags & SigmaValue) == 0)
 
3418
            image->y_resolution=image->x_resolution;
 
3419
        }
 
3420
      if (LocaleCompare(property,"depth") == 0)
 
3421
        {
 
3422
          image->depth=StringToUnsignedLong(value);
 
3423
          break;
 
3424
        }
 
3425
      if (LocaleCompare(property,"dispose") == 0)
 
3426
        {
 
3427
          ssize_t
 
3428
            dispose;
 
3429
 
 
3430
          dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
 
3431
          if (dispose < 0)
 
3432
            break;
 
3433
          image->dispose=(DisposeType) dispose;
 
3434
          break;
 
3435
        }
 
3436
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3437
        ConstantString(property),ConstantString(value));
 
3438
      break;
 
3439
    }
 
3440
    case 'G':
 
3441
    case 'g':
 
3442
    {
 
3443
      if (LocaleCompare(property,"gravity") == 0)
 
3444
        {
 
3445
          ssize_t
 
3446
            gravity;
 
3447
 
 
3448
          gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
 
3449
          if (gravity < 0)
 
3450
            break;
 
3451
          image->gravity=(GravityType) gravity;
 
3452
          break;
 
3453
        }
 
3454
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3455
        ConstantString(property),ConstantString(value));
 
3456
      break;
 
3457
    }
 
3458
    case 'I':
 
3459
    case 'i':
 
3460
    {
 
3461
      if (LocaleCompare(property,"intent") == 0)
 
3462
        {
 
3463
          ssize_t
 
3464
            rendering_intent;
 
3465
 
 
3466
          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
 
3467
            value);
 
3468
          if (rendering_intent < 0)
 
3469
            break;
 
3470
          image->rendering_intent=(RenderingIntent) rendering_intent;
 
3471
          break;
 
3472
        }
 
3473
      if (LocaleCompare(property,"interpolate") == 0)
 
3474
        {
 
3475
          ssize_t
 
3476
            interpolate;
 
3477
 
 
3478
          interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
 
3479
            value);
 
3480
          if (interpolate < 0)
 
3481
            break;
 
3482
          image->interpolate=(InterpolatePixelMethod) interpolate;
 
3483
          break;
 
3484
        }
 
3485
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3486
        ConstantString(property),ConstantString(value));
 
3487
      break;
 
3488
    }
 
3489
    case 'L':
 
3490
    case 'l':
 
3491
    {
 
3492
      if (LocaleCompare(property,"loop") == 0)
 
3493
        {
 
3494
          image->iterations=StringToUnsignedLong(value);
 
3495
          break;
 
3496
        }
 
3497
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3498
        ConstantString(property),ConstantString(value));
 
3499
      break;
 
3500
    }
 
3501
    case 'P':
 
3502
    case 'p':
 
3503
    {
 
3504
      if (LocaleCompare(property,"page") == 0)
 
3505
        {
 
3506
          char
 
3507
            *geometry;
 
3508
 
 
3509
          geometry=GetPageGeometry(value);
 
3510
          flags=ParseAbsoluteGeometry(geometry,&image->page);
 
3511
          geometry=DestroyString(geometry);
 
3512
          break;
 
3513
        }
 
3514
      if (LocaleCompare(property,"profile") == 0)
 
3515
        {
 
3516
          ImageInfo
 
3517
            *image_info;
 
3518
 
 
3519
          StringInfo
 
3520
            *profile;
 
3521
 
 
3522
          image_info=AcquireImageInfo();
 
3523
          (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
 
3524
          (void) SetImageInfo(image_info,1,exception);
 
3525
          profile=FileToStringInfo(image_info->filename,~0UL,exception);
 
3526
          if (profile != (StringInfo *) NULL)
 
3527
            status=SetImageProfile(image,image_info->magick,profile);
 
3528
          image_info=DestroyImageInfo(image_info);
 
3529
          break;
 
3530
        }
 
3531
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3532
        ConstantString(property),ConstantString(value));
 
3533
      break;
 
3534
    }
 
3535
    case 'R':
 
3536
    case 'r':
 
3537
    {
 
3538
      if (LocaleCompare(property,"rendering-intent") == 0)
 
3539
        {
 
3540
          ssize_t
 
3541
            rendering_intent;
 
3542
 
 
3543
          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
 
3544
            value);
 
3545
          if (rendering_intent < 0)
 
3546
            break;
 
3547
          image->rendering_intent=(RenderingIntent) rendering_intent;
 
3548
          break;
 
3549
        }
 
3550
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3551
        ConstantString(property),ConstantString(value));
 
3552
      break;
 
3553
    }
 
3554
    case 'T':
 
3555
    case 't':
 
3556
    {
 
3557
      if (LocaleCompare(property,"tile-offset") == 0)
 
3558
        {
 
3559
          char
 
3560
            *geometry;
 
3561
 
 
3562
          geometry=GetPageGeometry(value);
 
3563
          flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
 
3564
          geometry=DestroyString(geometry);
 
3565
          break;
 
3566
        }
 
3567
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3568
        ConstantString(property),ConstantString(value));
 
3569
      break;
 
3570
    }
 
3571
    case 'U':
 
3572
    case 'u':
 
3573
    {
 
3574
      if (LocaleCompare(property,"units") == 0)
 
3575
        {
 
3576
          ssize_t
 
3577
            units;
 
3578
 
 
3579
          units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
 
3580
          if (units < 0)
 
3581
            break;
 
3582
          image->units=(ResolutionType) units;
 
3583
          break;
 
3584
        }
 
3585
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3586
        ConstantString(property),ConstantString(value));
 
3587
      break;
 
3588
    }
 
3589
    default:
 
3590
    {
 
3591
      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
 
3592
        ConstantString(property),ConstantString(value));
 
3593
      break;
 
3594
    }
 
3595
  }
 
3596
  return(status);
 
3597
}