~ubuntu-branches/ubuntu/quantal/imagemagick/quantal-updates

« back to all changes in this revision

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