Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/property.c
Line
Count
Source
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
%                                   Cristy                                    %
17
%                                 March 2000                                  %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 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
%    https://imagemagick.org/license/                                         %
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 "MagickCore/studio.h"
44
#include "MagickCore/artifact.h"
45
#include "MagickCore/attribute.h"
46
#include "MagickCore/cache.h"
47
#include "MagickCore/cache-private.h"
48
#include "MagickCore/color.h"
49
#include "MagickCore/color-private.h"
50
#include "MagickCore/colorspace-private.h"
51
#include "MagickCore/compare.h"
52
#include "MagickCore/constitute.h"
53
#include "MagickCore/draw.h"
54
#include "MagickCore/effect.h"
55
#include "MagickCore/exception.h"
56
#include "MagickCore/exception-private.h"
57
#include "MagickCore/fx.h"
58
#include "MagickCore/fx-private.h"
59
#include "MagickCore/gem.h"
60
#include "MagickCore/geometry.h"
61
#include "MagickCore/histogram.h"
62
#include "MagickCore/image.h"
63
#include "MagickCore/layer.h"
64
#include "MagickCore/locale-private.h"
65
#include "MagickCore/list.h"
66
#include "MagickCore/magick.h"
67
#include "MagickCore/memory_.h"
68
#include "MagickCore/monitor.h"
69
#include "MagickCore/montage.h"
70
#include "MagickCore/option.h"
71
#include "MagickCore/policy.h"
72
#include "MagickCore/profile.h"
73
#include "MagickCore/property.h"
74
#include "MagickCore/quantum.h"
75
#include "MagickCore/resource_.h"
76
#include "MagickCore/splay-tree.h"
77
#include "MagickCore/signature.h"
78
#include "MagickCore/statistic.h"
79
#include "MagickCore/string_.h"
80
#include "MagickCore/string-private.h"
81
#include "MagickCore/token.h"
82
#include "MagickCore/token-private.h"
83
#include "MagickCore/utility.h"
84
#include "MagickCore/utility-private.h"
85
#include "MagickCore/version.h"
86
#include "MagickCore/xml-tree.h"
87
#include "MagickCore/xml-tree-private.h"
88
#if defined(MAGICKCORE_LCMS_DELEGATE)
89
#if defined(MAGICKCORE_HAVE_LCMS2_LCMS2_H)
90
#include <lcms2/lcms2.h>
91
#elif defined(MAGICKCORE_HAVE_LCMS2_H)
92
#include "lcms2.h"
93
#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
94
#include <lcms/lcms.h>
95
#else
96
#include "lcms.h"
97
#endif
98
#endif
99

100
/*
101
  Define declarations.
102
*/
103
#if defined(MAGICKCORE_LCMS_DELEGATE)
104
#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
105
#define cmsUInt32Number  DWORD
106
#endif
107
#endif
108

109
/*
110
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111
%                                                                             %
112
%                                                                             %
113
%                                                                             %
114
%   C l o n e I m a g e P r o p e r t i e s                                   %
115
%                                                                             %
116
%                                                                             %
117
%                                                                             %
118
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119
%
120
%  CloneImageProperties() clones all the image properties to another image.
121
%
122
%  The format of the CloneImageProperties method is:
123
%
124
%      MagickBooleanType CloneImageProperties(Image *image,
125
%        const Image *clone_image)
126
%
127
%  A description of each parameter follows:
128
%
129
%    o image: the image.
130
%
131
%    o clone_image: the clone image.
132
%
133
*/
134
135
typedef char
136
  *(*CloneKeyFunc)(const char *),
137
  *(*CloneValueFunc)(const char *);
138
139
static inline void *ClonePropertyKey(void *key)
140
314k
{
141
314k
  return((void *) ((CloneKeyFunc) ConstantString)((const char *) key));
142
314k
}
143
144
static inline void *ClonePropertyValue(void *value)
145
314k
{
146
314k
  return((void *) ((CloneValueFunc) ConstantString)((const char *) value));
147
314k
}
148
149
MagickExport MagickBooleanType CloneImageProperties(Image *image,
150
  const Image *clone_image)
151
126k
{
152
126k
  assert(image != (Image *) NULL);
153
126k
  assert(image->signature == MagickCoreSignature);
154
126k
  assert(clone_image != (const Image *) NULL);
155
126k
  assert(clone_image->signature == MagickCoreSignature);
156
126k
  if (IsEventLogging() != MagickFalse)
157
0
    {
158
0
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
159
0
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
160
0
        clone_image->filename);
161
0
    }
162
126k
  (void) CopyMagickString(image->filename,clone_image->filename,
163
126k
    MagickPathExtent);
164
126k
  (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
165
126k
    MagickPathExtent);
166
126k
  image->compression=clone_image->compression;
167
126k
  image->quality=clone_image->quality;
168
126k
  image->depth=clone_image->depth;
169
126k
  image->matte_color=clone_image->matte_color;
170
126k
  image->background_color=clone_image->background_color;
171
126k
  image->border_color=clone_image->border_color;
172
126k
  image->transparent_color=clone_image->transparent_color;
173
126k
  image->gamma=clone_image->gamma;
174
126k
  image->chromaticity=clone_image->chromaticity;
175
126k
  image->rendering_intent=clone_image->rendering_intent;
176
126k
  image->black_point_compensation=clone_image->black_point_compensation;
177
126k
  image->units=clone_image->units;
178
126k
  image->montage=(char *) NULL;
179
126k
  image->directory=(char *) NULL;
180
126k
  (void) CloneString(&image->geometry,clone_image->geometry);
181
126k
  image->offset=clone_image->offset;
182
126k
  image->resolution.x=clone_image->resolution.x;
183
126k
  image->resolution.y=clone_image->resolution.y;
184
126k
  image->page=clone_image->page;
185
126k
  image->tile_offset=clone_image->tile_offset;
186
126k
  image->extract_info=clone_image->extract_info;
187
126k
  image->filter=clone_image->filter;
188
126k
  image->fuzz=clone_image->fuzz;
189
126k
  image->intensity=clone_image->intensity;
190
126k
  image->interlace=clone_image->interlace;
191
126k
  image->interpolate=clone_image->interpolate;
192
126k
  image->endian=clone_image->endian;
193
126k
  image->gravity=clone_image->gravity;
194
126k
  image->compose=clone_image->compose;
195
126k
  image->orientation=clone_image->orientation;
196
126k
  image->scene=clone_image->scene;
197
126k
  image->dispose=clone_image->dispose;
198
126k
  image->delay=clone_image->delay;
199
126k
  image->ticks_per_second=clone_image->ticks_per_second;
200
126k
  image->iterations=clone_image->iterations;
201
126k
  image->total_colors=clone_image->total_colors;
202
126k
  image->taint=clone_image->taint;
203
126k
  image->progress_monitor=clone_image->progress_monitor;
204
126k
  image->client_data=clone_image->client_data;
205
126k
  image->start_loop=clone_image->start_loop;
206
126k
  image->error=clone_image->error;
207
126k
  image->signature=clone_image->signature;
208
126k
  if (clone_image->properties != (void *) NULL)
209
44.0k
    {
210
44.0k
      if (image->properties != (void *) NULL)
211
1.21k
        DestroyImageProperties(image);
212
44.0k
      image->properties=CloneSplayTree((SplayTreeInfo *)
213
44.0k
        clone_image->properties,ClonePropertyKey,ClonePropertyValue);
214
44.0k
    }
215
126k
  return(MagickTrue);
216
126k
}
217

218
/*
219
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220
%                                                                             %
221
%                                                                             %
222
%                                                                             %
223
%   D e f i n e I m a g e P r o p e r t y                                     %
224
%                                                                             %
225
%                                                                             %
226
%                                                                             %
227
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228
%
229
%  DefineImageProperty() associates an assignment string of the form
230
%  "key=value" with an artifact or options. It is equivalent to
231
%  SetImageProperty().
232
%
233
%  The format of the DefineImageProperty method is:
234
%
235
%      MagickBooleanType DefineImageProperty(Image *image,const char *property,
236
%        ExceptionInfo *exception)
237
%
238
%  A description of each parameter follows:
239
%
240
%    o image: the image.
241
%
242
%    o property: the image property.
243
%
244
%    o exception: return any errors or warnings in this structure.
245
%
246
*/
247
MagickExport MagickBooleanType DefineImageProperty(Image *image,
248
  const char *property,ExceptionInfo *exception)
249
0
{
250
0
  char
251
0
    key[MagickPathExtent],
252
0
    value[MagickPathExtent];
253
254
0
  char
255
0
    *p;
256
257
0
  assert(image != (Image *) NULL);
258
0
  assert(property != (const char *) NULL);
259
0
  (void) CopyMagickString(key,property,MagickPathExtent-1);
260
0
  for (p=key; *p != '\0'; p++)
261
0
    if (*p == '=')
262
0
      break;
263
0
  *value='\0';
264
0
  if (*p == '=')
265
0
    (void) CopyMagickString(value,p+1,MagickPathExtent);
266
0
  *p='\0';
267
0
  return(SetImageProperty(image,key,value,exception));
268
0
}
269

270
/*
271
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272
%                                                                             %
273
%                                                                             %
274
%                                                                             %
275
%   D e l e t e I m a g e P r o p e r t y                                     %
276
%                                                                             %
277
%                                                                             %
278
%                                                                             %
279
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280
%
281
%  DeleteImageProperty() deletes an image property.
282
%
283
%  The format of the DeleteImageProperty method is:
284
%
285
%      MagickBooleanType DeleteImageProperty(Image *image,const char *property)
286
%
287
%  A description of each parameter follows:
288
%
289
%    o image: the image.
290
%
291
%    o property: the image property.
292
%
293
*/
294
MagickExport MagickBooleanType DeleteImageProperty(Image *image,
295
  const char *property)
296
30.7k
{
297
30.7k
  assert(image != (Image *) NULL);
298
30.7k
  assert(image->signature == MagickCoreSignature);
299
30.7k
  if (IsEventLogging() != MagickFalse)
300
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
301
30.7k
  if (image->properties == (void *) NULL)
302
1.27k
    return(MagickFalse);
303
29.4k
  return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
304
30.7k
}
305

306
/*
307
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308
%                                                                             %
309
%                                                                             %
310
%                                                                             %
311
%   D e s t r o y I m a g e P r o p e r t i e s                               %
312
%                                                                             %
313
%                                                                             %
314
%                                                                             %
315
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316
%
317
%  DestroyImageProperties() destroys all properties and associated memory
318
%  attached to the given image.
319
%
320
%  The format of the DestroyDefines method is:
321
%
322
%      void DestroyImageProperties(Image *image)
323
%
324
%  A description of each parameter follows:
325
%
326
%    o image: the image.
327
%
328
*/
329
MagickExport void DestroyImageProperties(Image *image)
330
2.99M
{
331
2.99M
  assert(image != (Image *) NULL);
332
2.99M
  assert(image->signature == MagickCoreSignature);
333
2.99M
  if (IsEventLogging() != MagickFalse)
334
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
335
2.99M
  if (image->properties != (void *) NULL)
336
473k
    image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
337
473k
      image->properties);
338
2.99M
}
339

340
/*
341
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342
%                                                                             %
343
%                                                                             %
344
%                                                                             %
345
%  F o r m a t I m a g e P r o p e r t y                                      %
346
%                                                                             %
347
%                                                                             %
348
%                                                                             %
349
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350
%
351
%  FormatImageProperty() permits formatted property/value pairs to be saved as
352
%  an image property.
353
%
354
%  The format of the FormatImageProperty method is:
355
%
356
%      MagickBooleanType FormatImageProperty(Image *image,const char *property,
357
%        const char *format,...)
358
%
359
%  A description of each parameter follows.
360
%
361
%   o  image:  The image.
362
%
363
%   o  property:  The attribute property.
364
%
365
%   o  format:  A string describing the format to use to write the remaining
366
%      arguments.
367
%
368
*/
369
MagickExport MagickBooleanType FormatImageProperty(Image *image,
370
  const char *property,const char *format,...)
371
220k
{
372
220k
  char
373
220k
    value[MagickPathExtent];
374
375
220k
  ExceptionInfo
376
220k
    *exception;
377
378
220k
  MagickBooleanType
379
220k
    status;
380
381
220k
  ssize_t
382
220k
    n;
383
384
220k
  va_list
385
220k
    operands;
386
387
220k
  va_start(operands,format);
388
220k
  n=FormatLocaleStringList(value,MagickPathExtent,format,operands);
389
220k
  (void) n;
390
220k
  va_end(operands);
391
220k
  exception=AcquireExceptionInfo();
392
220k
  status=SetImageProperty(image,property,value,exception);
393
220k
  exception=DestroyExceptionInfo(exception);
394
220k
  return(status);
395
220k
}
396

397
/*
398
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399
%                                                                             %
400
%                                                                             %
401
%                                                                             %
402
%   G e t I m a g e P r o p e r t y                                           %
403
%                                                                             %
404
%                                                                             %
405
%                                                                             %
406
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
407
%
408
%  GetImageProperty() gets a value associated with an image property.
409
%
410
%  This includes,  profile prefixes, such as "exif:", "iptc:" and "8bim:"
411
%  It does not handle non-profile prefixes, such as "fx:", "option:", or
412
%  "artifact:".
413
%
414
%  The returned string is stored as a prosperity of the same name for faster
415
%  lookup later. It should NOT be freed by the caller.
416
%
417
%  The format of the GetImageProperty method is:
418
%
419
%      const char *GetImageProperty(const Image *image,const char *key,
420
%        ExceptionInfo *exception)
421
%
422
%  A description of each parameter follows:
423
%
424
%    o image: the image.
425
%
426
%    o key: the key.
427
%
428
%    o exception: return any errors or warnings in this structure.
429
%
430
*/
431
432
static char
433
  *TracePSClippath(const unsigned char *,size_t),
434
  *TraceSVGClippath(const unsigned char *,size_t,const size_t,
435
    const size_t);
436
437
static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
438
  ExceptionInfo *exception)
439
221k
{
440
221k
  char
441
221k
    *attribute;
442
443
221k
  const StringInfo
444
221k
    *profile;
445
446
221k
  long
447
221k
    count,
448
221k
    dataset,
449
221k
    record;
450
451
221k
  ssize_t
452
221k
    i;
453
454
221k
  profile=GetImageProfile(image,"iptc");
455
221k
  if (profile == (StringInfo *) NULL)
456
221k
    profile=GetImageProfile(image,"8bim");
457
221k
  if (profile == (StringInfo *) NULL)
458
209k
    return(MagickFalse);
459
12.7k
  count=MagickSscanf(key,"IPTC:%ld:%ld",&dataset,&record);
460
12.7k
  if (count != 2)
461
12.7k
    return(MagickFalse);
462
0
  attribute=(char *) NULL;
463
0
  for (i=0; i < (ssize_t) GetStringInfoLength(profile)-5; )
464
0
  {
465
0
    const unsigned char *p = GetStringInfoDatum(profile)+i;
466
  
467
0
    if (p[0] != 0x1c)  /* Look for IPTC marker */
468
0
      {
469
0
        i++;
470
0
        continue;
471
0
      }
472
    /*
473
      Dataset and record.
474
    */
475
0
    if (((long) p[1] == dataset) && ((long) p[2] == record))
476
0
      {
477
0
        char
478
0
          *message = (char *) NULL;
479
480
0
        size_t declared = ((size_t) p[3] << 8) | (size_t) p[4];
481
0
        size_t remaining = GetStringInfoLength(profile)-(i+5);
482
0
        size_t length = MagickMin(declared,remaining);
483
0
        if (~length >= 1)
484
0
          message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
485
0
        if (message != (char *) NULL)
486
0
          {
487
            /*
488
              Copy only the clamped length.
489
            */
490
0
            (void) memcpy(message,p+5,length);
491
0
            message[length]='\0';
492
0
            (void) ConcatenateString(&attribute,message);
493
0
            (void) ConcatenateString(&attribute,";");
494
0
            message=DestroyString(message);
495
0
          }
496
0
      }
497
    /*
498
      Advance past this record header + data.
499
    */
500
0
    i+=(((size_t) p[3] << 8) | (size_t) p[4])+5;
501
0
  }
502
0
  if ((attribute == (char *) NULL) || (*attribute == ';'))
503
0
    {
504
0
      if (attribute != (char *) NULL)
505
0
        attribute=DestroyString(attribute);
506
0
      return(MagickFalse);
507
0
    }
508
0
  attribute[strlen(attribute)-1]='\0';
509
0
  (void) SetImageProperty((Image *) image,key,(const char *) attribute,
510
0
    exception);
511
0
  attribute=DestroyString(attribute);
512
0
  return(MagickTrue);
513
0
}
514
515
static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
516
5.56M
{
517
5.56M
  int
518
5.56M
    c;
519
520
5.56M
  if (*length < 1)
521
2.38k
    return(EOF);
522
5.56M
  c=(int) (*(*p)++);
523
5.56M
  (*length)--;
524
5.56M
  return(c);
525
5.56M
}
526
527
static inline signed int ReadPropertyMSBLong(const unsigned char **p,
528
  size_t *length)
529
547
{
530
547
  union
531
547
  {
532
547
    unsigned int
533
547
      unsigned_value;
534
535
547
    signed int
536
547
      signed_value;
537
547
  } quantum;
538
539
547
  int
540
547
    c;
541
542
547
  ssize_t
543
547
    i;
544
545
547
  unsigned char
546
547
    buffer[4];
547
548
547
  unsigned int
549
547
    value;
550
551
547
  if (*length < 4)
552
33
    return(-1);
553
2.57k
  for (i=0; i < 4; i++)
554
2.05k
  {
555
2.05k
    c=(int) (*(*p)++);
556
2.05k
    (*length)--;
557
2.05k
    buffer[i]=(unsigned char) c;
558
2.05k
  }
559
514
  value=(unsigned int) buffer[0] << 24;
560
514
  value|=(unsigned int) buffer[1] << 16;
561
514
  value|=(unsigned int) buffer[2] << 8;
562
514
  value|=(unsigned int) buffer[3];
563
514
  quantum.unsigned_value=value & 0xffffffff;
564
514
  return(quantum.signed_value);
565
547
}
566
567
static inline signed short ReadPropertyMSBShort(const unsigned char **p,
568
  size_t *length)
569
2.73k
{
570
2.73k
  union
571
2.73k
  {
572
2.73k
    unsigned short
573
2.73k
      unsigned_value;
574
575
2.73k
    signed short
576
2.73k
      signed_value;
577
2.73k
  } quantum;
578
579
2.73k
  int
580
2.73k
    c;
581
582
2.73k
  ssize_t
583
2.73k
    i;
584
585
2.73k
  unsigned char
586
2.73k
    buffer[2];
587
588
2.73k
  unsigned short
589
2.73k
    value;
590
591
2.73k
  if (*length < 2)
592
8
    return(-1);
593
8.17k
  for (i=0; i < 2; i++)
594
5.45k
  {
595
5.45k
    c=(int) (*(*p)++);
596
5.45k
    (*length)--;
597
5.45k
    buffer[i]=(unsigned char) c;
598
5.45k
  }
599
2.72k
  value=(unsigned short) buffer[0] << 8;
600
2.72k
  value|=(unsigned short) buffer[1];
601
2.72k
  quantum.unsigned_value=value & 0xffff;
602
2.72k
  return(quantum.signed_value);
603
2.73k
}
604
605
static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
606
  ExceptionInfo *exception)
607
5.73k
{
608
5.73k
  char
609
5.73k
    *attribute,
610
5.73k
    format[MagickPathExtent],
611
5.73k
    *macroman_resource = (char *) NULL,
612
5.73k
    name[MagickPathExtent],
613
5.73k
    *resource = (char *) NULL;
614
615
5.73k
  const StringInfo
616
5.73k
    *profile;
617
618
5.73k
  const unsigned char
619
5.73k
    *info;
620
621
5.73k
  long
622
5.73k
    start,
623
5.73k
    stop;
624
625
5.73k
  MagickBooleanType
626
5.73k
    status;
627
628
5.73k
  size_t
629
5.73k
    length;
630
631
5.73k
  ssize_t
632
5.73k
    count,
633
5.73k
    i,
634
5.73k
    id,
635
5.73k
    sub_number;
636
637
  /*
638
    There are no newlines in path names, so it's safe as terminator.
639
  */
640
5.73k
  profile=GetImageProfile(image,"8bim");
641
5.73k
  if (profile == (StringInfo *) NULL)
642
5.33k
    return(MagickFalse);
643
404
  count=(ssize_t) MagickSscanf(key,"8BIM:%ld,%ld:%1024[^\n]\n%1024[^\n]",
644
404
    &start,&stop,name,format);
645
404
  if ((count != 2) && (count != 3) && (count != 4))
646
0
    return(MagickFalse);
647
404
  if (count < 4)
648
404
    (void) CopyMagickString(format,"SVG",MagickPathExtent);
649
404
  if (count < 3)
650
0
    *name='\0';
651
404
  sub_number=1;
652
404
  if (*name == '#')
653
404
    sub_number=(ssize_t) StringToLong(&name[1]);
654
404
  sub_number=MagickMax(sub_number,1L);
655
404
  status=MagickFalse;
656
404
  length=GetStringInfoLength(profile);
657
404
  info=GetStringInfoDatum(profile);
658
88.1k
  while ((length > 0) && (status == MagickFalse))
659
87.7k
  {
660
87.7k
    if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
661
85.8k
      continue;
662
1.92k
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
663
392
      continue;
664
1.53k
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
665
238
      continue;
666
1.29k
    if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
667
103
      continue;
668
1.19k
    id=(ssize_t) ReadPropertyMSBShort(&info,&length);
669
1.19k
    if (id < (ssize_t) start)
670
743
      continue;
671
452
    if (id > (ssize_t) stop)
672
343
      continue;
673
109
    if (macroman_resource != (char *) NULL)
674
0
      macroman_resource=DestroyString(macroman_resource);
675
109
    if (resource != (char *) NULL)
676
0
      resource=DestroyString(resource);
677
109
    count=(ssize_t) ReadPropertyByte(&info,&length);
678
109
    if ((count != 0) && ((size_t) count <= length))
679
41
      {
680
41
        if (~((size_t) count) >= (MagickPathExtent-1))
681
41
          resource=(char *) AcquireQuantumMemory((size_t) count+
682
41
            MagickPathExtent,sizeof(*resource));
683
41
        if (resource != (char *) NULL)
684
41
          {
685
3.02k
            for (i=0; i < (ssize_t) count; i++)
686
2.98k
              resource[i]=(char) ReadPropertyByte(&info,&length);
687
41
            resource[count]='\0';
688
41
          }
689
41
      }
690
109
    if ((count & 0x01) == 0)
691
95
      (void) ReadPropertyByte(&info,&length);
692
109
    count=(ssize_t) ReadPropertyMSBLong(&info,&length);
693
109
    if ((count < 0) || ((size_t) count > length))
694
21
      {
695
21
        length=0;
696
21
        continue;
697
21
      }
698
88
    if (resource != (char *) NULL)
699
31
      macroman_resource=(char *) ConvertMacRomanToUTF8((unsigned char *)
700
31
        resource);
701
88
    if ((*name != '\0') && (*name != '#'))
702
0
      if ((resource == (char *) NULL) || (macroman_resource == (char *) NULL) ||
703
0
          ((LocaleCompare(name,resource) != 0) &&
704
0
           (LocaleCompare(name,macroman_resource) != 0)))
705
0
        {
706
          /*
707
            No name match, scroll forward and try next.
708
          */
709
0
          info+=count;
710
0
          length=(size_t) ((ssize_t) length-MagickMin(count,(ssize_t) length));
711
0
          continue;
712
0
        }
713
88
    if ((*name == '#') && (sub_number != 1))
714
0
      {
715
        /*
716
          No numbered match, scroll forward and try next.
717
        */
718
0
        sub_number--;
719
0
        info+=count;
720
0
        length=(size_t) ((ssize_t) length-MagickMin(count,(ssize_t) length));
721
0
        continue;
722
0
      }
723
    /*
724
      We have the resource of interest.
725
    */
726
88
    attribute=(char *) NULL;
727
88
    if (~((size_t) count) >= (MagickPathExtent-1))
728
88
      attribute=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent,
729
88
        sizeof(*attribute));
730
88
    if (attribute != (char *) NULL)
731
88
      {
732
88
        (void) memcpy(attribute,(char *) info,(size_t) count);
733
88
        attribute[count]='\0';
734
88
        info+=count;
735
88
        length=(size_t) ((ssize_t) length-MagickMin(count,(ssize_t) length));
736
88
        if ((id <= 1999) || (id >= 2999))
737
4
          (void) SetImageProperty((Image *) image,key,(const char *) attribute,
738
4
            exception);
739
84
        else
740
84
          {
741
84
            char
742
84
              *path;
743
744
84
            if (LocaleCompare(format,"svg") == 0)
745
84
              path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
746
84
                image->columns,image->rows);
747
0
            else
748
0
              path=TracePSClippath((unsigned char *) attribute,(size_t) count);
749
84
            (void) SetImageProperty((Image *) image,key,(const char *) path,
750
84
              exception);
751
84
            path=DestroyString(path);
752
84
          }
753
88
        attribute=DestroyString(attribute);
754
88
        status=MagickTrue;
755
88
      }
756
88
  }
757
404
  if (macroman_resource != (char *) NULL)
758
31
    macroman_resource=DestroyString(macroman_resource);
759
404
  if (resource != (char *) NULL)
760
41
    resource=DestroyString(resource);
761
404
  return(MagickTrue);
762
404
}
763
764
static inline signed int ReadPropertySignedLong(const EndianType endian,
765
  const unsigned char *buffer)
766
2.45M
{
767
2.45M
  union
768
2.45M
  {
769
2.45M
    unsigned int
770
2.45M
      unsigned_value;
771
772
2.45M
    signed int
773
2.45M
      signed_value;
774
2.45M
  } quantum;
775
776
2.45M
  unsigned int
777
2.45M
    value;
778
779
2.45M
  if (endian == LSBEndian)
780
950k
    {
781
950k
      value=(unsigned int) buffer[3] << 24;
782
950k
      value|=(unsigned int) buffer[2] << 16;
783
950k
      value|=(unsigned int) buffer[1] << 8;
784
950k
      value|=(unsigned int) buffer[0];
785
950k
      quantum.unsigned_value=value & 0xffffffff;
786
950k
      return(quantum.signed_value);
787
950k
    }
788
1.50M
  value=(unsigned int) buffer[0] << 24;
789
1.50M
  value|=(unsigned int) buffer[1] << 16;
790
1.50M
  value|=(unsigned int) buffer[2] << 8;
791
1.50M
  value|=(unsigned int) buffer[3];
792
1.50M
  quantum.unsigned_value=value & 0xffffffff;
793
1.50M
  return(quantum.signed_value);
794
2.45M
}
795
796
static inline unsigned int ReadPropertyUnsignedLong(const EndianType endian,
797
  const unsigned char *buffer)
798
687k
{
799
687k
  unsigned int
800
687k
    value;
801
802
687k
  if (endian == LSBEndian)
803
282k
    {
804
282k
      value=(unsigned int) buffer[3] << 24;
805
282k
      value|=(unsigned int) buffer[2] << 16;
806
282k
      value|=(unsigned int) buffer[1] << 8;
807
282k
      value|=(unsigned int) buffer[0];
808
282k
      return(value & 0xffffffff);
809
282k
    }
810
405k
  value=(unsigned int) buffer[0] << 24;
811
405k
  value|=(unsigned int) buffer[1] << 16;
812
405k
  value|=(unsigned int) buffer[2] << 8;
813
405k
  value|=(unsigned int) buffer[3];
814
405k
  return(value & 0xffffffff);
815
687k
}
816
817
static inline signed short ReadPropertySignedShort(const EndianType endian,
818
  const unsigned char *buffer)
819
482k
{
820
482k
  union
821
482k
  {
822
482k
    unsigned short
823
482k
      unsigned_value;
824
825
482k
    signed short
826
482k
      signed_value;
827
482k
  } quantum;
828
829
482k
  unsigned short
830
482k
    value;
831
832
482k
  if (endian == LSBEndian)
833
338k
    {
834
338k
      value=(unsigned short) buffer[1] << 8;
835
338k
      value|=(unsigned short) buffer[0];
836
338k
      quantum.unsigned_value=value & 0xffff;
837
338k
      return(quantum.signed_value);
838
338k
    }
839
144k
  value=(unsigned short) buffer[0] << 8;
840
144k
  value|=(unsigned short) buffer[1];
841
144k
  quantum.unsigned_value=value & 0xffff;
842
144k
  return(quantum.signed_value);
843
482k
}
844
845
static inline unsigned short ReadPropertyUnsignedShort(const EndianType endian,
846
  const unsigned char *buffer)
847
2.77M
{
848
2.77M
  unsigned short
849
2.77M
    value;
850
851
2.77M
  if (endian == LSBEndian)
852
1.33M
    {
853
1.33M
      value=(unsigned short) buffer[1] << 8;
854
1.33M
      value|=(unsigned short) buffer[0];
855
1.33M
      return(value & 0xffff);
856
1.33M
    }
857
1.44M
  value=(unsigned short) buffer[0] << 8;
858
1.44M
  value|=(unsigned short) buffer[1];
859
1.44M
  return(value & 0xffff);
860
2.77M
}
861
862
static MagickBooleanType GetEXIFProperty(const Image *image,
863
  const char *property,ExceptionInfo *exception)
864
873k
{
865
873k
#define MaxDirectoryStack  16
866
873k
#define EXIF_DELIMITER  "\n"
867
873k
#define EXIF_NUM_FORMATS  12
868
873k
#define EXIF_FMT_BYTE  1
869
873k
#define EXIF_FMT_STRING  2
870
873k
#define EXIF_FMT_USHORT  3
871
873k
#define EXIF_FMT_ULONG  4
872
873k
#define EXIF_FMT_URATIONAL  5
873
873k
#define EXIF_FMT_SBYTE  6
874
873k
#define EXIF_FMT_UNDEFINED  7
875
873k
#define EXIF_FMT_SSHORT  8
876
873k
#define EXIF_FMT_SLONG  9
877
873k
#define EXIF_FMT_SRATIONAL  10
878
873k
#define EXIF_FMT_SINGLE  11
879
873k
#define EXIF_FMT_DOUBLE  12
880
873k
#define GPS_LATITUDE  0x10002
881
873k
#define GPS_LONGITUDE  0x10004
882
873k
#define GPS_TIMESTAMP  0x10007
883
873k
#define TAG_EXIF_OFFSET  0x8769
884
873k
#define TAG_GPS_OFFSET  0x8825
885
873k
#define TAG_INTEROP_OFFSET  0xa005
886
887
873k
#define EXIFGPSFractions(format,arg1,arg2,arg3,arg4,arg5,arg6) \
888
234
{ \
889
234
   size_t \
890
234
     extent = 0; \
891
234
 \
892
234
   ssize_t \
893
234
     component = 0; \
894
234
 \
895
468
   for ( ; component < components; component++) \
896
234
   { \
897
234
     if (component != 0) \
898
234
       extent+=(size_t) FormatLocaleString(buffer+extent,MagickPathExtent- \
899
0
         extent,", "); \
900
234
     extent+=(size_t) FormatLocaleString(buffer+extent,MagickPathExtent- \
901
234
       extent,format,(arg1),(arg2),(arg3),(arg4),(arg5),(arg6)); \
902
234
     if (extent >= (MagickPathExtent-1)) \
903
234
       extent=MagickPathExtent-1; \
904
234
   } \
905
234
   buffer[extent]='\0'; \
906
234
   value=AcquireString(buffer); \
907
234
}
908
909
873k
#define EXIFMultipleValues(format,arg) \
910
69.4k
{ \
911
69.4k
   size_t \
912
69.4k
     extent = 0; \
913
69.4k
 \
914
69.4k
   ssize_t \
915
69.4k
     component = 0; \
916
69.4k
 \
917
2.89M
   for ( ; component < components; component++) \
918
2.82M
   { \
919
2.82M
     if (component != 0) \
920
2.82M
       extent+=(size_t) FormatLocaleString(buffer+extent,MagickPathExtent- \
921
2.78M
         extent,", "); \
922
2.82M
     extent+=(size_t) FormatLocaleString(buffer+extent,MagickPathExtent- \
923
2.82M
       extent,format,arg); \
924
2.82M
     if (extent >= (MagickPathExtent-1)) \
925
2.82M
       extent=MagickPathExtent-1; \
926
2.82M
   } \
927
69.4k
   buffer[extent]='\0'; \
928
69.4k
   value=AcquireString(buffer); \
929
69.4k
}
930
931
873k
#define EXIFMultipleFractions(format,arg1,arg2) \
932
17.7k
{ \
933
17.7k
   size_t \
934
17.7k
     extent = 0; \
935
17.7k
 \
936
17.7k
   ssize_t \
937
17.7k
     component = 0; \
938
17.7k
 \
939
386k
   for ( ; component < components; component++) \
940
369k
   { \
941
369k
     if (component != 0) \
942
369k
       extent+=(size_t) FormatLocaleString(buffer+extent,MagickPathExtent-\
943
358k
         extent,", "); \
944
369k
     extent+=(size_t) FormatLocaleString(buffer+extent,MagickPathExtent- \
945
369k
       extent,format,(arg1),(arg2)); \
946
369k
     if (extent >= (MagickPathExtent-1)) \
947
369k
       extent=MagickPathExtent-1; \
948
369k
   } \
949
17.7k
   buffer[extent]='\0'; \
950
17.7k
   value=AcquireString(buffer); \
951
17.7k
}
952
953
873k
  typedef struct _DirectoryInfo
954
873k
  {
955
873k
    const unsigned char
956
873k
      *directory;
957
958
873k
    size_t
959
873k
      entry;
960
961
873k
    ssize_t
962
873k
      offset;
963
873k
  } DirectoryInfo;
964
965
873k
  typedef struct _TagInfo
966
873k
  {
967
873k
    size_t
968
873k
      tag;
969
970
873k
    const char
971
873k
      description[36];
972
873k
  } TagInfo;
973
974
873k
  static const TagInfo
975
873k
    EXIFTag[] =
976
873k
    {
977
873k
      {  0x001, "exif:InteroperabilityIndex" },
978
873k
      {  0x002, "exif:InteroperabilityVersion" },
979
873k
      {  0x100, "exif:ImageWidth" },
980
873k
      {  0x101, "exif:ImageLength" },
981
873k
      {  0x102, "exif:BitsPerSample" },
982
873k
      {  0x103, "exif:Compression" },
983
873k
      {  0x106, "exif:PhotometricInterpretation" },
984
873k
      {  0x10a, "exif:FillOrder" },
985
873k
      {  0x10d, "exif:DocumentName" },
986
873k
      {  0x10e, "exif:ImageDescription" },
987
873k
      {  0x10f, "exif:Make" },
988
873k
      {  0x110, "exif:Model" },
989
873k
      {  0x111, "exif:StripOffsets" },
990
873k
      {  0x112, "exif:Orientation" },
991
873k
      {  0x115, "exif:SamplesPerPixel" },
992
873k
      {  0x116, "exif:RowsPerStrip" },
993
873k
      {  0x117, "exif:StripByteCounts" },
994
873k
      {  0x11a, "exif:XResolution" },
995
873k
      {  0x11b, "exif:YResolution" },
996
873k
      {  0x11c, "exif:PlanarConfiguration" },
997
873k
      {  0x11d, "exif:PageName" },
998
873k
      {  0x11e, "exif:XPosition" },
999
873k
      {  0x11f, "exif:YPosition" },
1000
873k
      {  0x118, "exif:MinSampleValue" },
1001
873k
      {  0x119, "exif:MaxSampleValue" },
1002
873k
      {  0x120, "exif:FreeOffsets" },
1003
873k
      {  0x121, "exif:FreeByteCounts" },
1004
873k
      {  0x122, "exif:GrayResponseUnit" },
1005
873k
      {  0x123, "exif:GrayResponseCurve" },
1006
873k
      {  0x124, "exif:T4Options" },
1007
873k
      {  0x125, "exif:T6Options" },
1008
873k
      {  0x128, "exif:ResolutionUnit" },
1009
873k
      {  0x12d, "exif:TransferFunction" },
1010
873k
      {  0x131, "exif:Software" },
1011
873k
      {  0x132, "exif:DateTime" },
1012
873k
      {  0x13b, "exif:Artist" },
1013
873k
      {  0x13e, "exif:WhitePoint" },
1014
873k
      {  0x13f, "exif:PrimaryChromaticities" },
1015
873k
      {  0x140, "exif:ColorMap" },
1016
873k
      {  0x141, "exif:HalfToneHints" },
1017
873k
      {  0x142, "exif:TileWidth" },
1018
873k
      {  0x143, "exif:TileLength" },
1019
873k
      {  0x144, "exif:TileOffsets" },
1020
873k
      {  0x145, "exif:TileByteCounts" },
1021
873k
      {  0x14a, "exif:SubIFD" },
1022
873k
      {  0x14c, "exif:InkSet" },
1023
873k
      {  0x14d, "exif:InkNames" },
1024
873k
      {  0x14e, "exif:NumberOfInks" },
1025
873k
      {  0x150, "exif:DotRange" },
1026
873k
      {  0x151, "exif:TargetPrinter" },
1027
873k
      {  0x152, "exif:ExtraSample" },
1028
873k
      {  0x153, "exif:SampleFormat" },
1029
873k
      {  0x154, "exif:SMinSampleValue" },
1030
873k
      {  0x155, "exif:SMaxSampleValue" },
1031
873k
      {  0x156, "exif:TransferRange" },
1032
873k
      {  0x157, "exif:ClipPath" },
1033
873k
      {  0x158, "exif:XClipPathUnits" },
1034
873k
      {  0x159, "exif:YClipPathUnits" },
1035
873k
      {  0x15a, "exif:Indexed" },
1036
873k
      {  0x15b, "exif:JPEGTables" },
1037
873k
      {  0x15f, "exif:OPIProxy" },
1038
873k
      {  0x200, "exif:JPEGProc" },
1039
873k
      {  0x201, "exif:JPEGInterchangeFormat" },
1040
873k
      {  0x202, "exif:JPEGInterchangeFormatLength" },
1041
873k
      {  0x203, "exif:JPEGRestartInterval" },
1042
873k
      {  0x205, "exif:JPEGLosslessPredictors" },
1043
873k
      {  0x206, "exif:JPEGPointTransforms" },
1044
873k
      {  0x207, "exif:JPEGQTables" },
1045
873k
      {  0x208, "exif:JPEGDCTables" },
1046
873k
      {  0x209, "exif:JPEGACTables" },
1047
873k
      {  0x211, "exif:YCbCrCoefficients" },
1048
873k
      {  0x212, "exif:YCbCrSubSampling" },
1049
873k
      {  0x213, "exif:YCbCrPositioning" },
1050
873k
      {  0x214, "exif:ReferenceBlackWhite" },
1051
873k
      {  0x2bc, "exif:ExtensibleMetadataPlatform" },
1052
873k
      {  0x301, "exif:Gamma" },
1053
873k
      {  0x302, "exif:ICCProfileDescriptor" },
1054
873k
      {  0x303, "exif:SRGBRenderingIntent" },
1055
873k
      {  0x320, "exif:ImageTitle" },
1056
873k
      {  0x5001, "exif:ResolutionXUnit" },
1057
873k
      {  0x5002, "exif:ResolutionYUnit" },
1058
873k
      {  0x5003, "exif:ResolutionXLengthUnit" },
1059
873k
      {  0x5004, "exif:ResolutionYLengthUnit" },
1060
873k
      {  0x5005, "exif:PrintFlags" },
1061
873k
      {  0x5006, "exif:PrintFlagsVersion" },
1062
873k
      {  0x5007, "exif:PrintFlagsCrop" },
1063
873k
      {  0x5008, "exif:PrintFlagsBleedWidth" },
1064
873k
      {  0x5009, "exif:PrintFlagsBleedWidthScale" },
1065
873k
      {  0x500A, "exif:HalftoneLPI" },
1066
873k
      {  0x500B, "exif:HalftoneLPIUnit" },
1067
873k
      {  0x500C, "exif:HalftoneDegree" },
1068
873k
      {  0x500D, "exif:HalftoneShape" },
1069
873k
      {  0x500E, "exif:HalftoneMisc" },
1070
873k
      {  0x500F, "exif:HalftoneScreen" },
1071
873k
      {  0x5010, "exif:JPEGQuality" },
1072
873k
      {  0x5011, "exif:GridSize" },
1073
873k
      {  0x5012, "exif:ThumbnailFormat" },
1074
873k
      {  0x5013, "exif:ThumbnailWidth" },
1075
873k
      {  0x5014, "exif:ThumbnailHeight" },
1076
873k
      {  0x5015, "exif:ThumbnailColorDepth" },
1077
873k
      {  0x5016, "exif:ThumbnailPlanes" },
1078
873k
      {  0x5017, "exif:ThumbnailRawBytes" },
1079
873k
      {  0x5018, "exif:ThumbnailSize" },
1080
873k
      {  0x5019, "exif:ThumbnailCompressedSize" },
1081
873k
      {  0x501a, "exif:ColorTransferFunction" },
1082
873k
      {  0x501b, "exif:ThumbnailData" },
1083
873k
      {  0x5020, "exif:ThumbnailImageWidth" },
1084
873k
      {  0x5021, "exif:ThumbnailImageHeight" },
1085
873k
      {  0x5022, "exif:ThumbnailBitsPerSample" },
1086
873k
      {  0x5023, "exif:ThumbnailCompression" },
1087
873k
      {  0x5024, "exif:ThumbnailPhotometricInterp" },
1088
873k
      {  0x5025, "exif:ThumbnailImageDescription" },
1089
873k
      {  0x5026, "exif:ThumbnailEquipMake" },
1090
873k
      {  0x5027, "exif:ThumbnailEquipModel" },
1091
873k
      {  0x5028, "exif:ThumbnailStripOffsets" },
1092
873k
      {  0x5029, "exif:ThumbnailOrientation" },
1093
873k
      {  0x502a, "exif:ThumbnailSamplesPerPixel" },
1094
873k
      {  0x502b, "exif:ThumbnailRowsPerStrip" },
1095
873k
      {  0x502c, "exif:ThumbnailStripBytesCount" },
1096
873k
      {  0x502d, "exif:ThumbnailResolutionX" },
1097
873k
      {  0x502e, "exif:ThumbnailResolutionY" },
1098
873k
      {  0x502f, "exif:ThumbnailPlanarConfig" },
1099
873k
      {  0x5030, "exif:ThumbnailResolutionUnit" },
1100
873k
      {  0x5031, "exif:ThumbnailTransferFunction" },
1101
873k
      {  0x5032, "exif:ThumbnailSoftwareUsed" },
1102
873k
      {  0x5033, "exif:ThumbnailDateTime" },
1103
873k
      {  0x5034, "exif:ThumbnailArtist" },
1104
873k
      {  0x5035, "exif:ThumbnailWhitePoint" },
1105
873k
      {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
1106
873k
      {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
1107
873k
      {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
1108
873k
      {  0x5039, "exif:ThumbnailYCbCrPositioning" },
1109
873k
      {  0x503A, "exif:ThumbnailRefBlackWhite" },
1110
873k
      {  0x503B, "exif:ThumbnailCopyRight" },
1111
873k
      {  0x5090, "exif:LuminanceTable" },
1112
873k
      {  0x5091, "exif:ChrominanceTable" },
1113
873k
      {  0x5100, "exif:FrameDelay" },
1114
873k
      {  0x5101, "exif:LoopCount" },
1115
873k
      {  0x5110, "exif:PixelUnit" },
1116
873k
      {  0x5111, "exif:PixelPerUnitX" },
1117
873k
      {  0x5112, "exif:PixelPerUnitY" },
1118
873k
      {  0x5113, "exif:PaletteHistogram" },
1119
873k
      {  0x1000, "exif:RelatedImageFileFormat" },
1120
873k
      {  0x1001, "exif:RelatedImageLength" },
1121
873k
      {  0x1002, "exif:RelatedImageWidth" },
1122
873k
      {  0x800d, "exif:ImageID" },
1123
873k
      {  0x80e3, "exif:Matteing" },
1124
873k
      {  0x80e4, "exif:DataType" },
1125
873k
      {  0x80e5, "exif:ImageDepth" },
1126
873k
      {  0x80e6, "exif:TileDepth" },
1127
873k
      {  0x828d, "exif:CFARepeatPatternDim" },
1128
873k
      {  0x828e, "exif:CFAPattern2" },
1129
873k
      {  0x828f, "exif:BatteryLevel" },
1130
873k
      {  0x8298, "exif:Copyright" },
1131
873k
      {  0x829a, "exif:ExposureTime" },
1132
873k
      {  0x829d, "exif:FNumber" },
1133
873k
      {  0x83bb, "exif:IPTC/NAA" },
1134
873k
      {  0x84e3, "exif:IT8RasterPadding" },
1135
873k
      {  0x84e5, "exif:IT8ColorTable" },
1136
873k
      {  0x8649, "exif:ImageResourceInformation" },
1137
873k
      {  0x8769, "exif:ExifOffset" },  /* specs as "Exif IFD Pointer"? */
1138
873k
      {  0x8773, "exif:InterColorProfile" },
1139
873k
      {  0x8822, "exif:ExposureProgram" },
1140
873k
      {  0x8824, "exif:SpectralSensitivity" },
1141
873k
      {  0x8825, "exif:GPSInfo" }, /* specs as "GPSInfo IFD Pointer"? */
1142
873k
      {  0x8827, "exif:PhotographicSensitivity" },
1143
873k
      {  0x8828, "exif:OECF" },
1144
873k
      {  0x8829, "exif:Interlace" },
1145
873k
      {  0x882a, "exif:TimeZoneOffset" },
1146
873k
      {  0x882b, "exif:SelfTimerMode" },
1147
873k
      {  0x8830, "exif:SensitivityType" },
1148
873k
      {  0x8831, "exif:StandardOutputSensitivity" },
1149
873k
      {  0x8832, "exif:RecommendedExposureIndex" },
1150
873k
      {  0x8833, "exif:ISOSpeed" },
1151
873k
      {  0x8834, "exif:ISOSpeedLatitudeyyy" },
1152
873k
      {  0x8835, "exif:ISOSpeedLatitudezzz" },
1153
873k
      {  0x9000, "exif:ExifVersion" },
1154
873k
      {  0x9003, "exif:DateTimeOriginal" },
1155
873k
      {  0x9004, "exif:DateTimeDigitized" },
1156
873k
      {  0x9010, "exif:OffsetTime" },
1157
873k
      {  0x9011, "exif:OffsetTimeOriginal" },
1158
873k
      {  0x9012, "exif:OffsetTimeDigitized" },
1159
873k
      {  0x9101, "exif:ComponentsConfiguration" },
1160
873k
      {  0x9102, "exif:CompressedBitsPerPixel" },
1161
873k
      {  0x9201, "exif:ShutterSpeedValue" },
1162
873k
      {  0x9202, "exif:ApertureValue" },
1163
873k
      {  0x9203, "exif:BrightnessValue" },
1164
873k
      {  0x9204, "exif:ExposureBiasValue" },
1165
873k
      {  0x9205, "exif:MaxApertureValue" },
1166
873k
      {  0x9206, "exif:SubjectDistance" },
1167
873k
      {  0x9207, "exif:MeteringMode" },
1168
873k
      {  0x9208, "exif:LightSource" },
1169
873k
      {  0x9209, "exif:Flash" },
1170
873k
      {  0x920a, "exif:FocalLength" },
1171
873k
      {  0x920b, "exif:FlashEnergy" },
1172
873k
      {  0x920c, "exif:SpatialFrequencyResponse" },
1173
873k
      {  0x920d, "exif:Noise" },
1174
873k
      {  0x9214, "exif:SubjectArea" },
1175
873k
      {  0x9290, "exif:SubSecTime" },
1176
873k
      {  0x9291, "exif:SubSecTimeOriginal" },
1177
873k
      {  0x9292, "exif:SubSecTimeDigitized" },
1178
873k
      {  0x9211, "exif:ImageNumber" },
1179
873k
      {  0x9212, "exif:SecurityClassification" },
1180
873k
      {  0x9213, "exif:ImageHistory" },
1181
873k
      {  0x9214, "exif:SubjectArea" },
1182
873k
      {  0x9215, "exif:ExposureIndex" },
1183
873k
      {  0x9216, "exif:TIFF-EPStandardID" },
1184
873k
      {  0x927c, "exif:MakerNote" },
1185
873k
      {  0x9286, "exif:UserComment" },
1186
873k
      {  0x9290, "exif:SubSecTime" },
1187
873k
      {  0x9291, "exif:SubSecTimeOriginal" },
1188
873k
      {  0x9292, "exif:SubSecTimeDigitized" },
1189
873k
      {  0x9400, "exif:Temperature" },
1190
873k
      {  0x9401, "exif:Humidity" },
1191
873k
      {  0x9402, "exif:Pressure" },
1192
873k
      {  0x9403, "exif:WaterDepth" },
1193
873k
      {  0x9404, "exif:Acceleration" },
1194
873k
      {  0x9405, "exif:CameraElevationAngle" },
1195
873k
      {  0x9C9b, "exif:WinXP-Title" },
1196
873k
      {  0x9C9c, "exif:WinXP-Comments" },
1197
873k
      {  0x9C9d, "exif:WinXP-Author" },
1198
873k
      {  0x9C9e, "exif:WinXP-Keywords" },
1199
873k
      {  0x9C9f, "exif:WinXP-Subject" },
1200
873k
      {  0xa000, "exif:FlashPixVersion" },
1201
873k
      {  0xa001, "exif:ColorSpace" },
1202
873k
      {  0xa002, "exif:PixelXDimension" },
1203
873k
      {  0xa003, "exif:PixelYDimension" },
1204
873k
      {  0xa004, "exif:RelatedSoundFile" },
1205
873k
      {  0xa005, "exif:InteroperabilityOffset" },
1206
873k
      {  0xa20b, "exif:FlashEnergy" },
1207
873k
      {  0xa20c, "exif:SpatialFrequencyResponse" },
1208
873k
      {  0xa20d, "exif:Noise" },
1209
873k
      {  0xa20e, "exif:FocalPlaneXResolution" },
1210
873k
      {  0xa20f, "exif:FocalPlaneYResolution" },
1211
873k
      {  0xa210, "exif:FocalPlaneResolutionUnit" },
1212
873k
      {  0xa214, "exif:SubjectLocation" },
1213
873k
      {  0xa215, "exif:ExposureIndex" },
1214
873k
      {  0xa216, "exif:TIFF/EPStandardID" },
1215
873k
      {  0xa217, "exif:SensingMethod" },
1216
873k
      {  0xa300, "exif:FileSource" },
1217
873k
      {  0xa301, "exif:SceneType" },
1218
873k
      {  0xa302, "exif:CFAPattern" },
1219
873k
      {  0xa401, "exif:CustomRendered" },
1220
873k
      {  0xa402, "exif:ExposureMode" },
1221
873k
      {  0xa403, "exif:WhiteBalance" },
1222
873k
      {  0xa404, "exif:DigitalZoomRatio" },
1223
873k
      {  0xa405, "exif:FocalLengthIn35mmFilm" },
1224
873k
      {  0xa406, "exif:SceneCaptureType" },
1225
873k
      {  0xa407, "exif:GainControl" },
1226
873k
      {  0xa408, "exif:Contrast" },
1227
873k
      {  0xa409, "exif:Saturation" },
1228
873k
      {  0xa40a, "exif:Sharpness" },
1229
873k
      {  0xa40b, "exif:DeviceSettingDescription" },
1230
873k
      {  0xa40c, "exif:SubjectDistanceRange" },
1231
873k
      {  0xa420, "exif:ImageUniqueID" },
1232
873k
      {  0xa430, "exif:CameraOwnerName" },
1233
873k
      {  0xa431, "exif:BodySerialNumber" },
1234
873k
      {  0xa432, "exif:LensSpecification" },
1235
873k
      {  0xa433, "exif:LensMake" },
1236
873k
      {  0xa434, "exif:LensModel" },
1237
873k
      {  0xa435, "exif:LensSerialNumber" },
1238
873k
      {  0xc4a5, "exif:PrintImageMatching" },
1239
873k
      {  0xa500, "exif:Gamma" },
1240
873k
      {  0xc640, "exif:CR2Slice" },
1241
873k
      { 0x10000, "exif:GPSVersionID" },
1242
873k
      { 0x10001, "exif:GPSLatitudeRef" },
1243
873k
      { 0x10002, "exif:GPSLatitude" },
1244
873k
      { 0x10003, "exif:GPSLongitudeRef" },
1245
873k
      { 0x10004, "exif:GPSLongitude" },
1246
873k
      { 0x10005, "exif:GPSAltitudeRef" },
1247
873k
      { 0x10006, "exif:GPSAltitude" },
1248
873k
      { 0x10007, "exif:GPSTimeStamp" },
1249
873k
      { 0x10008, "exif:GPSSatellites" },
1250
873k
      { 0x10009, "exif:GPSStatus" },
1251
873k
      { 0x1000a, "exif:GPSMeasureMode" },
1252
873k
      { 0x1000b, "exif:GPSDop" },
1253
873k
      { 0x1000c, "exif:GPSSpeedRef" },
1254
873k
      { 0x1000d, "exif:GPSSpeed" },
1255
873k
      { 0x1000e, "exif:GPSTrackRef" },
1256
873k
      { 0x1000f, "exif:GPSTrack" },
1257
873k
      { 0x10010, "exif:GPSImgDirectionRef" },
1258
873k
      { 0x10011, "exif:GPSImgDirection" },
1259
873k
      { 0x10012, "exif:GPSMapDatum" },
1260
873k
      { 0x10013, "exif:GPSDestLatitudeRef" },
1261
873k
      { 0x10014, "exif:GPSDestLatitude" },
1262
873k
      { 0x10015, "exif:GPSDestLongitudeRef" },
1263
873k
      { 0x10016, "exif:GPSDestLongitude" },
1264
873k
      { 0x10017, "exif:GPSDestBearingRef" },
1265
873k
      { 0x10018, "exif:GPSDestBearing" },
1266
873k
      { 0x10019, "exif:GPSDestDistanceRef" },
1267
873k
      { 0x1001a, "exif:GPSDestDistance" },
1268
873k
      { 0x1001b, "exif:GPSProcessingMethod" },
1269
873k
      { 0x1001c, "exif:GPSAreaInformation" },
1270
873k
      { 0x1001d, "exif:GPSDateStamp" },
1271
873k
      { 0x1001e, "exif:GPSDifferential" },
1272
873k
      { 0x1001f, "exif:GPSHPositioningError" },
1273
873k
      { 0x00000, "" }
1274
873k
    };  /* https://cipa.jp/std/documents/download_e.html?DC-008-Translation-2019-E */
1275
1276
873k
  const StringInfo
1277
873k
    *profile;
1278
1279
873k
  const unsigned char
1280
873k
    *directory,
1281
873k
    *exif;
1282
1283
873k
  DirectoryInfo
1284
873k
    directory_stack[MaxDirectoryStack] = { { 0, 0, 0 } };
1285
1286
873k
  EndianType
1287
873k
    endian;
1288
1289
873k
  size_t
1290
873k
    entry,
1291
873k
    length,
1292
873k
    number_entries,
1293
873k
    tag,
1294
873k
    tag_value;
1295
1296
873k
  SplayTreeInfo
1297
873k
    *exif_resources;
1298
1299
873k
  ssize_t
1300
873k
    all,
1301
873k
    i,
1302
873k
    id,
1303
873k
    level,
1304
873k
    offset,
1305
873k
    tag_offset;
1306
1307
873k
  static int
1308
873k
    tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1309
1310
  /*
1311
    If EXIF data exists, then try to parse the request for a tag.
1312
  */
1313
873k
  profile=GetImageProfile(image,"exif");
1314
873k
  if (profile == (const StringInfo *) NULL)
1315
772k
    return(MagickFalse);
1316
101k
  if ((property == (const char *) NULL) || (*property == '\0'))
1317
0
    return(MagickFalse);
1318
101k
  while (isspace((int) ((unsigned char) *property)) != 0)
1319
0
    property++;
1320
101k
  if (strlen(property) <= 5)
1321
0
    return(MagickFalse);
1322
101k
  all=0;
1323
101k
  tag=(~0UL);
1324
101k
  switch (*(property+5))
1325
101k
  {
1326
28.7k
    case '*':
1327
28.7k
    {
1328
      /*
1329
        Caller has asked for all the tags in the EXIF data.
1330
      */
1331
28.7k
      tag=0;
1332
28.7k
      all=1; /* return the data in description=value format */
1333
28.7k
      break;
1334
0
    }
1335
0
    case '!':
1336
0
    {
1337
0
      tag=0;
1338
0
      all=2; /* return the data in tagid=value format */
1339
0
      break;
1340
0
    }
1341
0
    case '#':
1342
0
    case '@':
1343
0
    {
1344
0
      int
1345
0
        c;
1346
1347
0
      size_t
1348
0
        n;
1349
1350
      /*
1351
        Check for a hex based tag specification first.
1352
      */
1353
0
      tag=(*(property+5) == '@') ? 1UL : 0UL;
1354
0
      property+=6;
1355
0
      n=strlen(property);
1356
0
      if (n != 4)
1357
0
        return(MagickFalse);
1358
      /*
1359
        Parse tag specification as a hex number.
1360
      */
1361
0
      n/=4;
1362
0
      do
1363
0
      {
1364
0
        for (i=(ssize_t) n-1L; i >= 0; i--)
1365
0
        {
1366
0
          c=(*property++);
1367
0
          tag<<=4;
1368
0
          if ((c >= '0') && (c <= '9'))
1369
0
            tag|=(size_t) (c-'0');
1370
0
          else
1371
0
            if ((c >= 'A') && (c <= 'F'))
1372
0
              tag|=(size_t) (c-('A'-10));
1373
0
            else
1374
0
              if ((c >= 'a') && (c <= 'f'))
1375
0
                tag|=(size_t) (c-('a'-10));
1376
0
              else
1377
0
                return(MagickFalse);
1378
0
        }
1379
0
      } while (*property != '\0');
1380
0
      break;
1381
0
    }
1382
72.8k
    default:
1383
72.8k
    {
1384
      /*
1385
        Try to match the text with a tag name instead.
1386
      */
1387
1.23M
      for (i=0; ; i++)
1388
1.30M
      {
1389
1.30M
        if (EXIFTag[i].tag == 0)
1390
0
          break;
1391
1.30M
        if (LocaleCompare(EXIFTag[i].description,property) == 0)
1392
72.8k
          {
1393
72.8k
            tag=(size_t) EXIFTag[i].tag;
1394
72.8k
            break;
1395
72.8k
          }
1396
1.30M
      }
1397
72.8k
      break;
1398
0
    }
1399
101k
  }
1400
101k
  if (tag == (~0UL))
1401
0
    return(MagickFalse);
1402
101k
  length=GetStringInfoLength(profile);
1403
101k
  if (length < 6)
1404
901
    return(MagickFalse);
1405
100k
  exif=GetStringInfoDatum(profile);
1406
4.73M
  while (length != 0)
1407
4.72M
  {
1408
4.72M
    if (ReadPropertyByte(&exif,&length) != 0x45)
1409
4.51M
      continue;
1410
209k
    if (ReadPropertyByte(&exif,&length) != 0x78)
1411
51.1k
      continue;
1412
158k
    if (ReadPropertyByte(&exif,&length) != 0x69)
1413
15.7k
      continue;
1414
142k
    if (ReadPropertyByte(&exif,&length) != 0x66)
1415
15.0k
      continue;
1416
127k
    if (ReadPropertyByte(&exif,&length) != 0x00)
1417
19.9k
      continue;
1418
107k
    if (ReadPropertyByte(&exif,&length) != 0x00)
1419
13.4k
      continue;
1420
94.1k
    break;
1421
107k
  }
1422
100k
  if (length < 16)
1423
7.58k
    return(MagickFalse);
1424
93.0k
  id=(ssize_t) ReadPropertySignedShort(LSBEndian,exif);
1425
93.0k
  endian=LSBEndian;
1426
93.0k
  if (id == 0x4949)
1427
50.5k
    endian=LSBEndian;
1428
42.5k
  else
1429
42.5k
    if (id == 0x4D4D)
1430
38.8k
      endian=MSBEndian;
1431
3.70k
    else
1432
3.70k
      return(MagickFalse);
1433
89.3k
  if (ReadPropertyUnsignedShort(endian,exif+2) != 0x002a)
1434
1.59k
    return(MagickFalse);
1435
  /*
1436
    This the offset to the first IFD.
1437
  */
1438
87.7k
  offset=(ssize_t) ReadPropertySignedLong(endian,exif+4);
1439
87.7k
  if ((offset < 0) || (size_t) offset >= length)
1440
5.11k
    return(MagickFalse);
1441
  /*
1442
    Set the pointer to the first IFD and follow it were it leads.
1443
  */
1444
82.6k
  directory=exif+offset;
1445
82.6k
  level=0;
1446
82.6k
  entry=0;
1447
82.6k
  tag_offset=0;
1448
82.6k
  exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1449
82.6k
    (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1450
82.6k
  do
1451
270k
  {
1452
    /*
1453
      If there is anything on the stack then pop it off.
1454
    */
1455
270k
    if (level > 0)
1456
188k
      {
1457
188k
        level--;
1458
188k
        directory=directory_stack[level].directory;
1459
188k
        entry=directory_stack[level].entry;
1460
188k
        tag_offset=directory_stack[level].offset;
1461
188k
      }
1462
270k
    if ((directory < exif) || (directory > (exif+length-2)))
1463
737
      break;
1464
    /*
1465
      Determine how many entries there are in the current IFD.
1466
    */
1467
269k
    number_entries=(size_t) ReadPropertyUnsignedShort(endian,directory);
1468
910k
    for ( ; entry < number_entries; entry++)
1469
887k
    {
1470
887k
      size_t
1471
887k
        format;
1472
1473
887k
      ssize_t
1474
887k
        components,
1475
887k
        number_bytes;
1476
1477
887k
      unsigned char
1478
887k
        *p,
1479
887k
        *q;
1480
1481
887k
      q=(unsigned char *) (directory+(12*entry)+2);
1482
887k
      if (q > (exif+length-12))
1483
28.8k
        break;  /* corrupt EXIF */
1484
858k
      if (GetValueFromSplayTree(exif_resources,q) == q)
1485
46.9k
        break;
1486
811k
      (void) AddValueToSplayTree(exif_resources,q,q);
1487
811k
      tag_value=(size_t) (ReadPropertyUnsignedShort(endian,q)+(ssize_t)
1488
811k
        tag_offset);
1489
811k
      format=(size_t) ReadPropertyUnsignedShort(endian,q+2);
1490
811k
      if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
1491
46.1k
        break;
1492
765k
      if (format == 0)
1493
26.9k
        break;  /* corrupt EXIF */
1494
738k
      components=(ssize_t) ReadPropertySignedLong(endian,q+4);
1495
738k
      if (components < 0)
1496
6.04k
        break;  /* corrupt EXIF */
1497
732k
      number_bytes=components*(ssize_t) tag_bytes[format];
1498
732k
      if (number_bytes < components)
1499
0
        break;  /* prevent overflow */
1500
732k
      if (number_bytes <= 4)
1501
292k
        p=q+8;
1502
439k
      else
1503
439k
        {
1504
439k
          ssize_t
1505
439k
            dir_offset;
1506
1507
          /*
1508
            The directory entry contains an offset.
1509
          */
1510
439k
          dir_offset=(ssize_t) ReadPropertySignedLong(endian,q+8);
1511
439k
          if ((dir_offset < 0) || (size_t) dir_offset >= length)
1512
206k
            continue;
1513
233k
          if (((size_t) dir_offset+(size_t) number_bytes) < (size_t) dir_offset)
1514
0
            continue;  /* prevent overflow */
1515
233k
          if ((size_t) (dir_offset+(ssize_t) number_bytes) > length)
1516
84.4k
            continue;
1517
148k
          p=(unsigned char *) (exif+dir_offset);
1518
148k
        }
1519
441k
      if ((all != 0) || (tag == (size_t) tag_value))
1520
134k
        {
1521
134k
          char
1522
134k
            buffer[6*sizeof(double)+MagickPathExtent],
1523
134k
            *value;
1524
1525
134k
          if ((p < exif) || (p > (exif+length-tag_bytes[format])))
1526
675
            break;
1527
133k
          value=(char *) NULL;
1528
133k
          *buffer='\0';
1529
133k
          switch (format)
1530
133k
          {
1531
23.0k
            case EXIF_FMT_BYTE:
1532
23.0k
            {
1533
23.0k
              value=(char *) NULL;
1534
23.0k
              if (~((size_t) number_bytes) >= 1)
1535
23.0k
                value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1536
23.0k
                  sizeof(*value));
1537
23.0k
              if (value != (char *) NULL)
1538
23.0k
                {
1539
714k
                  for (i=0; i < (ssize_t) number_bytes; i++)
1540
691k
                  {
1541
691k
                    value[i]='.';
1542
691k
                    if (isprint((int) p[i]) != 0)
1543
181k
                      value[i]=(char) p[i];
1544
691k
                  }
1545
23.0k
                  value[i]='\0';
1546
23.0k
                }
1547
23.0k
              break;
1548
0
            }
1549
7.90k
            case EXIF_FMT_SBYTE:
1550
7.90k
            {
1551
7.90k
              EXIFMultipleValues("%.17g",(double) (*(signed char *) p));
1552
7.90k
              break;
1553
0
            }
1554
10.1k
            case EXIF_FMT_SSHORT:
1555
10.1k
            {
1556
10.1k
              EXIFMultipleValues("%hd",ReadPropertySignedShort(endian,p));
1557
10.1k
              break;
1558
0
            }
1559
14.3k
            case EXIF_FMT_USHORT:
1560
14.3k
            {
1561
14.3k
              EXIFMultipleValues("%hu",ReadPropertyUnsignedShort(endian,p));
1562
14.3k
              break;
1563
0
            }
1564
13.6k
            case EXIF_FMT_ULONG:
1565
13.6k
            {
1566
13.6k
              EXIFMultipleValues("%.17g",(double)
1567
13.6k
                ReadPropertyUnsignedLong(endian,p));
1568
13.6k
              break;
1569
0
            }
1570
8.23k
            case EXIF_FMT_SLONG:
1571
8.23k
            {
1572
8.23k
              EXIFMultipleValues("%.17g",(double)
1573
8.23k
                ReadPropertySignedLong(endian,p));
1574
8.23k
              break;
1575
0
            }
1576
12.8k
            case EXIF_FMT_URATIONAL:
1577
12.8k
            {
1578
12.8k
              if ((tag_value == GPS_LATITUDE) || (tag_value == GPS_LONGITUDE) ||
1579
11.8k
                  (tag_value == GPS_TIMESTAMP))
1580
1.26k
                {
1581
1.26k
                  if (number_bytes < 24)
1582
1.02k
                    break;  /* reads three rationals */
1583
234
                  components=1;
1584
234
                  EXIFGPSFractions("%.17g/%.17g,%.17g/%.17g,%.17g/%.17g",
1585
234
                    (double) ReadPropertyUnsignedLong(endian,p),
1586
234
                    (double) ReadPropertyUnsignedLong(endian,p+4),
1587
234
                    (double) ReadPropertyUnsignedLong(endian,p+8),
1588
234
                    (double) ReadPropertyUnsignedLong(endian,p+12),
1589
234
                    (double) ReadPropertyUnsignedLong(endian,p+16),
1590
234
                    (double) ReadPropertyUnsignedLong(endian,p+20));
1591
234
                  break;
1592
1.26k
                }
1593
11.6k
              EXIFMultipleFractions("%.17g/%.17g",(double)
1594
11.6k
                ReadPropertyUnsignedLong(endian,p),(double)
1595
11.6k
                ReadPropertyUnsignedLong(endian,p+4));
1596
11.6k
              break;
1597
12.8k
            }
1598
6.10k
            case EXIF_FMT_SRATIONAL:
1599
6.10k
            {
1600
6.10k
              EXIFMultipleFractions("%.17g/%.17g",(double)
1601
6.10k
                ReadPropertySignedLong(endian,p),(double)
1602
6.10k
                ReadPropertySignedLong(endian,p+4));
1603
6.10k
              break;
1604
12.8k
            }
1605
7.00k
            case EXIF_FMT_SINGLE:
1606
7.00k
            {
1607
7.00k
              EXIFMultipleValues("%.17g",(double)
1608
7.00k
                ReadPropertySignedLong(endian,p));
1609
7.00k
              break;
1610
12.8k
            }
1611
8.20k
            case EXIF_FMT_DOUBLE:
1612
8.20k
            {
1613
8.20k
              EXIFMultipleValues("%.17g",(double)
1614
8.20k
                ReadPropertySignedLong(endian,p));
1615
8.20k
              break;
1616
12.8k
            }
1617
12.9k
            case EXIF_FMT_STRING:
1618
22.5k
            case EXIF_FMT_UNDEFINED:
1619
22.5k
            default:
1620
22.5k
            {
1621
22.5k
              if ((p < exif) || (p > (exif+length-number_bytes)))
1622
0
                break;
1623
22.5k
              value=(char *) NULL;
1624
22.5k
              if (~((size_t) number_bytes) >= 1)
1625
22.5k
                value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
1626
22.5k
                  sizeof(*value));
1627
22.5k
              if (value != (char *) NULL)
1628
22.5k
                {
1629
1.38M
                  for (i=0; i < (ssize_t) number_bytes; i++)
1630
1.36M
                  {
1631
1.36M
                    value[i]='.';
1632
1.36M
                    if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
1633
875k
                      value[i]=(char) p[i];
1634
1.36M
                  }
1635
22.5k
                  value[i]='\0';
1636
22.5k
                }
1637
22.5k
              break;
1638
22.5k
            }
1639
133k
          }
1640
133k
          if (value != (char *) NULL)
1641
132k
            {
1642
132k
              char
1643
132k
                *key;
1644
1645
132k
              key=AcquireString(property);
1646
132k
              switch (all)
1647
132k
              {
1648
128k
                case 1:
1649
128k
                {
1650
128k
                  const char
1651
128k
                    *description;
1652
1653
128k
                  description="unknown";
1654
21.7M
                  for (i=0; ; i++)
1655
21.8M
                  {
1656
21.8M
                    if (EXIFTag[i].tag == 0)
1657
50.2k
                      break;
1658
21.8M
                    if (EXIFTag[i].tag == tag_value)
1659
78.1k
                      {
1660
78.1k
                        description=EXIFTag[i].description;
1661
78.1k
                        break;
1662
78.1k
                      }
1663
21.8M
                  }
1664
128k
                  (void) FormatLocaleString(key,MagickPathExtent,"%s",
1665
128k
                    description);
1666
128k
                  if (level == 2)
1667
17.7k
                    (void) SubstituteString(&key,"exif:","exif:Thumbnail.");
1668
128k
                  break;
1669
0
                }
1670
0
                case 2:
1671
0
                {
1672
0
                  if (tag_value < 0x10000)
1673
0
                    (void) FormatLocaleString(key,MagickPathExtent,"#%04lx",
1674
0
                      (unsigned long) tag_value);
1675
0
                  else
1676
0
                    if (tag_value < 0x20000)
1677
0
                      (void) FormatLocaleString(key,MagickPathExtent,"@%04lx",
1678
0
                        (unsigned long) (tag_value & 0xffff));
1679
0
                    else
1680
0
                      (void) FormatLocaleString(key,MagickPathExtent,"unknown");
1681
0
                  break;
1682
0
                }
1683
4.53k
                default:
1684
4.53k
                {
1685
4.53k
                  if (level == 2)
1686
4.28k
                    (void) SubstituteString(&key,"exif:","exif:Thumbnail.");
1687
4.53k
                }
1688
132k
              }
1689
132k
              if ((image->properties == (void *) NULL) ||
1690
128k
                  (GetValueFromSplayTree((SplayTreeInfo *) image->properties,key) == (const void *) NULL))
1691
49.7k
                (void) SetImageProperty((Image *) image,key,value,exception);
1692
132k
              value=DestroyString(value);
1693
132k
              key=DestroyString(key);
1694
132k
            }
1695
133k
        }
1696
440k
        if ((tag_value == TAG_EXIF_OFFSET) ||
1697
402k
            (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
1698
91.1k
          {
1699
91.1k
            ssize_t
1700
91.1k
              tag_offset1;
1701
1702
91.1k
            tag_offset1=(ssize_t) ReadPropertySignedLong(endian,p);
1703
91.1k
            if (((size_t) tag_offset1 < length) &&
1704
83.3k
                (level < (MaxDirectoryStack-2)))
1705
83.3k
              {
1706
83.3k
                ssize_t
1707
83.3k
                  tag_offset2;
1708
1709
83.3k
                tag_offset2=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
1710
83.3k
                  0);
1711
83.3k
                directory_stack[level].directory=directory;
1712
83.3k
                entry++;
1713
83.3k
                directory_stack[level].entry=entry;
1714
83.3k
                directory_stack[level].offset=tag_offset;
1715
83.3k
                level++;
1716
                /*
1717
                  Check for duplicate tag.
1718
                */
1719
187k
                for (i=0; i < level; i++)
1720
110k
                  if (directory_stack[i].directory == (exif+tag_offset1))
1721
5.95k
                    break;
1722
83.3k
                if (i < level)
1723
5.95k
                  break;  /* duplicate tag */
1724
77.4k
                directory_stack[level].directory=exif+tag_offset1;
1725
77.4k
                directory_stack[level].offset=tag_offset2;
1726
77.4k
                directory_stack[level].entry=0;
1727
77.4k
                level++;
1728
77.4k
                if ((directory+2+(12*number_entries)+4) > (exif+length))
1729
24.8k
                  break;
1730
52.6k
                tag_offset1=(ssize_t) ReadPropertySignedLong(endian,directory+
1731
52.6k
                  2+(12*number_entries));
1732
52.6k
                if ((tag_offset1 != 0) && ((size_t) tag_offset1 < length) &&
1733
27.8k
                    (level < (MaxDirectoryStack-2)))
1734
27.8k
                  {
1735
27.8k
                    directory_stack[level].directory=exif+tag_offset1;
1736
27.8k
                    directory_stack[level].entry=0;
1737
27.8k
                    directory_stack[level].offset=tag_offset2;
1738
27.8k
                    level++;
1739
27.8k
                  }
1740
52.6k
              }
1741
60.3k
            break;
1742
91.1k
          }
1743
440k
    }
1744
269k
  } while (level > 0);
1745
82.6k
  exif_resources=DestroySplayTree(exif_resources);
1746
82.6k
  return(MagickTrue);
1747
82.6k
}
1748
1749
#if defined(MAGICKCORE_LCMS_DELEGATE)
1750
static MagickBooleanType GetICCProperty(const Image *image,
1751
  ExceptionInfo *exception)
1752
221k
{
1753
1754
221k
  const StringInfo
1755
221k
    *profile;
1756
1757
221k
  cmsHPROFILE
1758
221k
    icc_profile;
1759
1760
  /*
1761
    Return ICC profile property.
1762
  */
1763
221k
  profile=GetImageProfile(image,"icc");
1764
221k
  if (profile == (StringInfo *) NULL)
1765
205k
    profile=GetImageProfile(image,"icm");
1766
221k
  if (profile == (StringInfo *) NULL)
1767
205k
    return(MagickFalse);
1768
16.8k
  if (GetStringInfoLength(profile) < 128)
1769
394
    return(MagickFalse);  /* minimum ICC profile length */
1770
16.4k
  icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
1771
16.4k
    (cmsUInt32Number) GetStringInfoLength(profile));
1772
16.4k
  if (icc_profile != (cmsHPROFILE *) NULL)
1773
11.2k
    {
1774
#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
1775
      const char
1776
        *name;
1777
1778
      name=cmsTakeProductName(icc_profile);
1779
      if (name != (const char *) NULL)
1780
        (void) SetImageProperty((Image *) image,"icc:name",name,exception);
1781
#else
1782
11.2k
      StringInfo
1783
11.2k
        *info;
1784
1785
11.2k
      unsigned int
1786
11.2k
        extent;
1787
1788
11.2k
      info=AcquireStringInfo(0);
1789
11.2k
      extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,"en","US",
1790
11.2k
        NULL,0);
1791
11.2k
      if (extent != 0)
1792
5.22k
        {
1793
5.22k
          SetStringInfoLength(info,extent+1);
1794
5.22k
          extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,"en",
1795
5.22k
            "US",(char *) GetStringInfoDatum(info),extent);
1796
5.22k
          if (extent != 0)
1797
5.22k
            (void) SetImageProperty((Image *) image,"icc:description",
1798
5.22k
              (char *) GetStringInfoDatum(info),exception);
1799
5.22k
        }
1800
11.2k
      extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,"en","US",
1801
11.2k
        NULL,0);
1802
11.2k
      if (extent != 0)
1803
725
        {
1804
725
          SetStringInfoLength(info,extent+1);
1805
725
          extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,"en",
1806
725
            "US",(char *) GetStringInfoDatum(info),extent);
1807
725
          if (extent != 0)
1808
725
            (void) SetImageProperty((Image *) image,"icc:manufacturer",
1809
725
              (char *) GetStringInfoDatum(info),exception);
1810
725
        }
1811
11.2k
      extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en","US",
1812
11.2k
        NULL,0);
1813
11.2k
      if (extent != 0)
1814
971
        {
1815
971
          SetStringInfoLength(info,extent+1);
1816
971
          extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en","US",
1817
971
            (char *) GetStringInfoDatum(info),extent);
1818
971
          if (extent != 0)
1819
971
            (void) SetImageProperty((Image *) image,"icc:model",
1820
971
              (char *) GetStringInfoDatum(info),exception);
1821
971
        }
1822
11.2k
      extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,"en","US",
1823
11.2k
        NULL,0);
1824
11.2k
      if (extent != 0)
1825
4.41k
        {
1826
4.41k
          SetStringInfoLength(info,extent+1);
1827
4.41k
          extent=cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,"en",
1828
4.41k
            "US",(char *) GetStringInfoDatum(info),extent);
1829
4.41k
          if (extent != 0)
1830
4.41k
            (void) SetImageProperty((Image *) image,"icc:copyright",
1831
4.41k
              (char *) GetStringInfoDatum(info),exception);
1832
4.41k
        }
1833
11.2k
      info=DestroyStringInfo(info);
1834
11.2k
#endif
1835
11.2k
      (void) cmsCloseProfile(icc_profile);
1836
11.2k
    }
1837
16.4k
  return(MagickTrue);
1838
16.8k
}
1839
#endif
1840
1841
static MagickBooleanType SkipXMPValue(const char *value)
1842
0
{
1843
0
  if (value == (const char*) NULL)
1844
0
    return(MagickTrue);
1845
0
  while (*value != '\0')
1846
0
  {
1847
0
    if (isspace((int) ((unsigned char) *value)) == 0)
1848
0
      return(MagickFalse);
1849
0
    value++;
1850
0
  }
1851
0
  return(MagickTrue);
1852
0
}
1853
1854
static MagickBooleanType GetXMPProperty(const Image *image,const char *property,
1855
 ExceptionInfo *exception)
1856
221k
{
1857
221k
  char
1858
221k
    *xmp_profile;
1859
1860
221k
  const char
1861
221k
    *content,
1862
221k
    *p;
1863
1864
221k
  const StringInfo
1865
221k
    *profile;
1866
1867
221k
  XMLTreeInfo
1868
221k
    *child,
1869
221k
    *description,
1870
221k
    *node,
1871
221k
    *rdf,
1872
221k
    *xmp;
1873
1874
221k
  profile=GetImageProfile(image,"xmp");
1875
221k
  if (profile == (StringInfo *) NULL)
1876
221k
    return(MagickFalse);
1877
0
  if (GetStringInfoLength(profile) < 17)
1878
0
    return(MagickFalse);
1879
0
  if ((property == (const char *) NULL) || (*property == '\0'))
1880
0
    return(MagickFalse);
1881
0
  xmp_profile=StringInfoToString(profile);
1882
0
  if (xmp_profile == (char *) NULL)
1883
0
    return(MagickFalse);
1884
0
  for (p=xmp_profile; *p != '\0'; p++)
1885
0
    if ((*p == '<') && (*(p+1) == 'x'))
1886
0
      break;
1887
0
  xmp=NewXMLTree((char *) p,exception);
1888
0
  xmp_profile=DestroyString(xmp_profile);
1889
0
  if (xmp == (XMLTreeInfo *) NULL)
1890
0
    return(MagickFalse);
1891
0
  rdf=GetXMLTreeChild(xmp,"rdf:RDF");
1892
0
  if (rdf != (XMLTreeInfo *) NULL)
1893
0
    {
1894
0
      if (image->properties == (void *) NULL)
1895
0
        ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
1896
0
          RelinquishMagickMemory,RelinquishMagickMemory);
1897
0
      description=GetXMLTreeChild(rdf,"rdf:Description");
1898
0
      while (description != (XMLTreeInfo *) NULL)
1899
0
      {
1900
0
        char
1901
0
          *xmp_namespace;
1902
1903
0
        size_t
1904
0
          xmp_namespace_length;
1905
1906
0
        node=GetXMLTreeChild(description,(const char *) NULL);
1907
0
        while (node != (XMLTreeInfo *) NULL)
1908
0
        {
1909
0
          child=GetXMLTreeChild(node,(const char *) NULL);
1910
0
          content=GetXMLTreeContent(node);
1911
0
          if ((child == (XMLTreeInfo *) NULL) &&
1912
0
              (SkipXMPValue(content) == MagickFalse))
1913
0
            {
1914
0
              xmp_namespace=ConstantString(GetXMLTreeTag(node));
1915
0
              (void) SubstituteString(&xmp_namespace,"exif:","xmp:");
1916
0
              xmp_namespace_length=strlen(xmp_namespace);
1917
0
              if ((xmp_namespace_length <= 2) ||
1918
0
                  (*(xmp_namespace+(xmp_namespace_length-2)) != ':') ||
1919
0
                  (*(xmp_namespace+(xmp_namespace_length-1)) != '*'))
1920
0
                (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
1921
0
                 ConstantString(xmp_namespace),ConstantString(content));
1922
0
              xmp_namespace=DestroyString(xmp_namespace);
1923
0
            }
1924
0
          while (child != (XMLTreeInfo *) NULL)
1925
0
          {
1926
0
            content=GetXMLTreeContent(child);
1927
0
            if (SkipXMPValue(content) == MagickFalse)
1928
0
              {
1929
0
                xmp_namespace=ConstantString(GetXMLTreeTag(node));
1930
0
                (void) SubstituteString(&xmp_namespace,"exif:","xmp:");
1931
0
                xmp_namespace_length=strlen(xmp_namespace);
1932
0
                if ((xmp_namespace_length <= 2) ||
1933
0
                    (*(xmp_namespace+(xmp_namespace_length-2)) != ':') ||
1934
0
                    (*(xmp_namespace+(xmp_namespace_length-1)) != '*'))
1935
0
                  (void) AddValueToSplayTree((SplayTreeInfo *)
1936
0
                    image->properties,ConstantString(xmp_namespace),
1937
0
                    ConstantString(content));
1938
0
                xmp_namespace=DestroyString(xmp_namespace);
1939
0
              }
1940
0
            child=GetXMLTreeSibling(child);
1941
0
          }
1942
0
          node=GetXMLTreeSibling(node);
1943
0
        }
1944
0
        description=GetNextXMLTreeTag(description);
1945
0
      }
1946
0
    }
1947
0
  xmp=DestroyXMLTree(xmp);
1948
0
  return(MagickTrue);
1949
0
}
1950
1951
static char *TracePSClippath(const unsigned char *blob,size_t length)
1952
0
{
1953
0
  char
1954
0
    *path,
1955
0
    *message;
1956
1957
0
  MagickBooleanType
1958
0
    in_subpath;
1959
1960
0
  PointInfo
1961
0
    first[3],
1962
0
    last[3],
1963
0
    point[3];
1964
1965
0
  ssize_t
1966
0
    i,
1967
0
    x;
1968
1969
0
  ssize_t
1970
0
    knot_count,
1971
0
    selector,
1972
0
    y;
1973
1974
0
  path=AcquireString((char *) NULL);
1975
0
  if (path == (char *) NULL)
1976
0
    return((char *) NULL);
1977
0
  message=AcquireString((char *) NULL);
1978
0
  (void) FormatLocaleString(message,MagickPathExtent,"/ClipImage\n");
1979
0
  (void) ConcatenateString(&path,message);
1980
0
  (void) FormatLocaleString(message,MagickPathExtent,"{\n");
1981
0
  (void) ConcatenateString(&path,message);
1982
0
  (void) FormatLocaleString(message,MagickPathExtent,
1983
0
    "  /c {curveto} bind def\n");
1984
0
  (void) ConcatenateString(&path,message);
1985
0
  (void) FormatLocaleString(message,MagickPathExtent,
1986
0
    "  /l {lineto} bind def\n");
1987
0
  (void) ConcatenateString(&path,message);
1988
0
  (void) FormatLocaleString(message,MagickPathExtent,
1989
0
    "  /m {moveto} bind def\n");
1990
0
  (void) ConcatenateString(&path,message);
1991
0
  (void) FormatLocaleString(message,MagickPathExtent,
1992
0
    "  /v {currentpoint 6 2 roll curveto} bind def\n");
1993
0
  (void) ConcatenateString(&path,message);
1994
0
  (void) FormatLocaleString(message,MagickPathExtent,
1995
0
    "  /y {2 copy curveto} bind def\n");
1996
0
  (void) ConcatenateString(&path,message);
1997
0
  (void) FormatLocaleString(message,MagickPathExtent,
1998
0
    "  /z {closepath} bind def\n");
1999
0
  (void) ConcatenateString(&path,message);
2000
0
  (void) FormatLocaleString(message,MagickPathExtent,"  newpath\n");
2001
0
  (void) ConcatenateString(&path,message);
2002
  /*
2003
    The clipping path format is defined in "Adobe Photoshop File Formats
2004
    Specification" version 6.0 downloadable from adobe.com.
2005
  */
2006
0
  (void) memset(point,0,sizeof(point));
2007
0
  (void) memset(first,0,sizeof(first));
2008
0
  (void) memset(last,0,sizeof(last));
2009
0
  knot_count=0;
2010
0
  in_subpath=MagickFalse;
2011
0
  while (length > 0)
2012
0
  {
2013
0
    selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
2014
0
    switch (selector)
2015
0
    {
2016
0
      case 0:
2017
0
      case 3:
2018
0
      {
2019
0
        if (knot_count != 0)
2020
0
          {
2021
0
            blob+=24;
2022
0
            length=(size_t) ((ssize_t) length-MagickMin(24,(ssize_t) length));
2023
0
            break;
2024
0
          }
2025
        /*
2026
          Expected subpath length record.
2027
        */
2028
0
        knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
2029
0
        blob+=22;
2030
0
        length=(size_t) ((ssize_t) length-MagickMin(22,(ssize_t) length));
2031
0
        break;
2032
0
      }
2033
0
      case 1:
2034
0
      case 2:
2035
0
      case 4:
2036
0
      case 5:
2037
0
      {
2038
0
        if (knot_count == 0)
2039
0
          {
2040
            /*
2041
              Unexpected subpath knot
2042
            */
2043
0
            blob+=24;
2044
0
            length=(size_t) ((ssize_t) length-MagickMin(24,(ssize_t) length));
2045
0
            break;
2046
0
          }
2047
        /*
2048
          Add sub-path knot
2049
        */
2050
0
        for (i=0; i < 3; i++)
2051
0
        {
2052
0
          y=(ssize_t) ReadPropertyMSBLong(&blob,&length);
2053
0
          x=(ssize_t) ReadPropertyMSBLong(&blob,&length);
2054
0
          point[i].x=(double) x/4096.0/4096.0;
2055
0
          point[i].y=1.0-(double) y/4096.0/4096.0;
2056
0
        }
2057
0
        if (in_subpath == MagickFalse)
2058
0
          {
2059
0
            (void) FormatLocaleString(message,MagickPathExtent,"  %g %g m\n",
2060
0
              point[1].x,point[1].y);
2061
0
            for (i=0; i < 3; i++)
2062
0
            {
2063
0
              first[i]=point[i];
2064
0
              last[i]=point[i];
2065
0
            }
2066
0
          }
2067
0
        else
2068
0
          {
2069
            /*
2070
              Handle special cases when Bezier curves are used to describe
2071
              corners and straight lines.
2072
            */
2073
0
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2074
0
                (point[0].x == point[1].x) && (point[0].y == point[1].y))
2075
0
              (void) FormatLocaleString(message,MagickPathExtent,
2076
0
                "  %g %g l\n",point[1].x,point[1].y);
2077
0
            else
2078
0
              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
2079
0
                (void) FormatLocaleString(message,MagickPathExtent,
2080
0
                  "  %g %g %g %g v\n",point[0].x,point[0].y,
2081
0
                  point[1].x,point[1].y);
2082
0
              else
2083
0
                if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
2084
0
                  (void) FormatLocaleString(message,MagickPathExtent,
2085
0
                    "  %g %g %g %g y\n",last[2].x,last[2].y,
2086
0
                    point[1].x,point[1].y);
2087
0
                else
2088
0
                  (void) FormatLocaleString(message,MagickPathExtent,
2089
0
                    "  %g %g %g %g %g %g c\n",last[2].x,
2090
0
                    last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
2091
0
            for (i=0; i < 3; i++)
2092
0
              last[i]=point[i];
2093
0
          }
2094
0
        (void) ConcatenateString(&path,message);
2095
0
        in_subpath=MagickTrue;
2096
0
        knot_count--;
2097
        /*
2098
          Close the subpath if there are no more knots.
2099
        */
2100
0
        if (knot_count == 0)
2101
0
          {
2102
            /*
2103
              Same special handling as above except we compare to the
2104
              first point in the path and close the path.
2105
            */
2106
0
            if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
2107
0
                (first[0].x == first[1].x) && (first[0].y == first[1].y))
2108
0
              (void) FormatLocaleString(message,MagickPathExtent,
2109
0
                "  %g %g l z\n",first[1].x,first[1].y);
2110
0
            else
2111
0
              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
2112
0
                (void) FormatLocaleString(message,MagickPathExtent,
2113
0
                  "  %g %g %g %g v z\n",first[0].x,first[0].y,
2114
0
                  first[1].x,first[1].y);
2115
0
              else
2116
0
                if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
2117
0
                  (void) FormatLocaleString(message,MagickPathExtent,
2118
0
                    "  %g %g %g %g y z\n",last[2].x,last[2].y,
2119
0
                    first[1].x,first[1].y);
2120
0
                else
2121
0
                  (void) FormatLocaleString(message,MagickPathExtent,
2122
0
                    "  %g %g %g %g %g %g c z\n",last[2].x,
2123
0
                    last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
2124
0
            (void) ConcatenateString(&path,message);
2125
0
            in_subpath=MagickFalse;
2126
0
          }
2127
0
        break;
2128
0
      }
2129
0
      case 6:
2130
0
      case 7:
2131
0
      case 8:
2132
0
      default:
2133
0
      {
2134
0
        blob+=24;
2135
0
        length=(size_t) ((ssize_t) length-MagickMin(24,(ssize_t) length));
2136
0
        break;
2137
0
      }
2138
0
    }
2139
0
  }
2140
  /*
2141
    Returns an empty PS path if the path has no knots.
2142
  */
2143
0
  (void) FormatLocaleString(message,MagickPathExtent,"  eoclip\n");
2144
0
  (void) ConcatenateString(&path,message);
2145
0
  (void) FormatLocaleString(message,MagickPathExtent,"} bind def");
2146
0
  (void) ConcatenateString(&path,message);
2147
0
  message=DestroyString(message);
2148
0
  return(path);
2149
0
}
2150
2151
static inline void TraceBezierCurve(char *message,PointInfo *last,
2152
  PointInfo *point)
2153
44
{
2154
  /*
2155
    Handle special cases when Bezier curves are used to describe
2156
    corners and straight lines.
2157
  */
2158
44
  if (((last+1)->x == (last+2)->x) && ((last+1)->y == (last+2)->y) &&
2159
6
      (point->x == (point+1)->x) && (point->y == (point+1)->y))
2160
2
    (void) FormatLocaleString(message,MagickPathExtent,
2161
2
      "L %g %g\n",point[1].x,point[1].y);
2162
42
  else
2163
42
    (void) FormatLocaleString(message,MagickPathExtent,"C %g %g %g %g %g %g\n",
2164
42
      (last+2)->x,(last+2)->y,point->x,point->y,(point+1)->x,(point+1)->y);
2165
44
}
2166
2167
static char *TraceSVGClippath(const unsigned char *blob,size_t length,
2168
  const size_t columns,const size_t rows)
2169
84
{
2170
84
  char
2171
84
    *path,
2172
84
    *message;
2173
2174
84
  MagickBooleanType
2175
84
    in_subpath;
2176
2177
84
  PointInfo
2178
84
    first[3],
2179
84
    last[3],
2180
84
    point[3];
2181
2182
84
  ssize_t
2183
84
    i;
2184
2185
84
  ssize_t
2186
84
    knot_count,
2187
84
    selector,
2188
84
    x,
2189
84
    y;
2190
2191
84
  path=AcquireString((char *) NULL);
2192
84
  if (path == (char *) NULL)
2193
0
    return((char *) NULL);
2194
84
  message=AcquireString((char *) NULL);
2195
84
  (void) FormatLocaleString(message,MagickPathExtent,(
2196
84
    "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
2197
84
    "<svg xmlns=\"http://www.w3.org/2000/svg\""
2198
84
    " width=\"%.17g\" height=\"%.17g\">\n"
2199
84
    "<g>\n"
2200
84
    "<path fill-rule=\"evenodd\" style=\"fill:#000000;stroke:#000000;"
2201
84
    "stroke-width:0;shape-rendering:crispEdges\" d=\"\n"),(double) columns,
2202
84
    (double) rows);
2203
84
  (void) ConcatenateString(&path,message);
2204
84
  (void) memset(point,0,sizeof(point));
2205
84
  (void) memset(first,0,sizeof(first));
2206
84
  (void) memset(last,0,sizeof(last));
2207
84
  knot_count=0;
2208
84
  in_subpath=MagickFalse;
2209
1.39k
  while (length != 0)
2210
1.30k
  {
2211
1.30k
    selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
2212
1.30k
    switch (selector)
2213
1.30k
    {
2214
465
      case 0:
2215
475
      case 3:
2216
475
      {
2217
475
        if (knot_count != 0)
2218
244
          {
2219
244
            blob+=24;
2220
244
            length=(size_t) ((ssize_t) length-MagickMin(24,(ssize_t) length));
2221
244
            break;
2222
244
          }
2223
        /*
2224
          Expected subpath length record.
2225
        */
2226
231
        knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
2227
231
        blob+=22;
2228
231
        length=(size_t) ((ssize_t) length-MagickMin(22,(ssize_t) length));
2229
231
        break;
2230
475
      }
2231
51
      case 1:
2232
62
      case 2:
2233
97
      case 4:
2234
105
      case 5:
2235
105
      {
2236
105
        if (knot_count == 0)
2237
32
          {
2238
            /*
2239
              Unexpected subpath knot.
2240
            */
2241
32
            blob+=24;
2242
32
            length=(size_t) ((ssize_t) length-MagickMin(24,(ssize_t) length));
2243
32
            break;
2244
32
          }
2245
        /*
2246
          Add sub-path knot
2247
        */
2248
292
        for (i=0; i < 3; i++)
2249
219
        {
2250
219
          y=(ssize_t) ReadPropertyMSBLong(&blob,&length);
2251
219
          x=(ssize_t) ReadPropertyMSBLong(&blob,&length);
2252
219
          point[i].x=(double) x*columns/4096.0/4096.0;
2253
219
          point[i].y=(double) y*rows/4096.0/4096.0;
2254
219
        }
2255
73
        if (in_subpath == MagickFalse)
2256
41
          {
2257
41
            (void) FormatLocaleString(message,MagickPathExtent,"M %g %g\n",
2258
41
              point[1].x,point[1].y);
2259
164
            for (i=0; i < 3; i++)
2260
123
            {
2261
123
              first[i]=point[i];
2262
123
              last[i]=point[i];
2263
123
            }
2264
41
          }
2265
32
        else
2266
32
          {
2267
32
            TraceBezierCurve(message,last,point);
2268
128
            for (i=0; i < 3; i++)
2269
96
              last[i]=point[i];
2270
32
          }
2271
73
        (void) ConcatenateString(&path,message);
2272
73
        in_subpath=MagickTrue;
2273
73
        knot_count--;
2274
        /*
2275
          Close the subpath if there are no more knots.
2276
        */
2277
73
        if (knot_count == 0)
2278
12
          {
2279
12
            TraceBezierCurve(message,last,first);
2280
12
            (void) ConcatenateString(&path,message);
2281
12
            in_subpath=MagickFalse;
2282
12
          }
2283
73
        break;
2284
105
      }
2285
0
      case 6:
2286
0
      case 7:
2287
1
      case 8:
2288
728
      default:
2289
728
      {
2290
728
        blob+=24;
2291
728
        length=(size_t) ((ssize_t) length-MagickMin(24,(ssize_t) length));
2292
728
        break;
2293
1
      }
2294
1.30k
    }
2295
1.30k
  }
2296
  /*
2297
    Return an empty SVG image if the path does not have knots.
2298
  */
2299
84
  (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n");
2300
84
  message=DestroyString(message);
2301
84
  return(path);
2302
84
}
2303
2304
MagickExport const char *GetImageProperty(const Image *image,
2305
  const char *property,ExceptionInfo *exception)
2306
2.64M
{
2307
2.64M
  MagickBooleanType
2308
2.64M
    read_from_properties;
2309
2310
2.64M
  const char
2311
2.64M
    *p;
2312
2313
2.64M
  size_t
2314
2.64M
    property_length;
2315
2316
2.64M
  assert(image != (Image *) NULL);
2317
2.64M
  assert(image->signature == MagickCoreSignature);
2318
2.64M
  if (IsEventLogging() != MagickFalse)
2319
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2320
2.64M
  if ((property == (const char *) NULL) || (*property == '\0'))
2321
10
    return((const char *) NULL);
2322
2.64M
  read_from_properties=MagickTrue;
2323
2.64M
  property_length=strlen(property);
2324
2.64M
  if ((property_length > 2) && (*(property+(property_length-2)) == ':') &&
2325
888k
      (*(property+(property_length-1)) == '*'))
2326
887k
    read_from_properties=MagickFalse;
2327
2.64M
  if (read_from_properties != MagickFalse)
2328
1.75M
    {
2329
1.75M
      p=(const char *) NULL;
2330
1.75M
      if (image->properties != (void *) NULL)
2331
990k
        {
2332
990k
          p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2333
990k
            image->properties,property);
2334
990k
          if (p != (const char *) NULL)
2335
88.1k
            return(p);
2336
990k
        }
2337
1.66M
      if (strchr(property,':') == (char *) NULL)
2338
41.0k
        return(p);
2339
1.66M
    }
2340
2.51M
  switch (*property)
2341
2.51M
  {
2342
5.76k
    case '8':
2343
5.76k
    {
2344
5.76k
      if (LocaleNCompare("8bim:",property,5) == 0)
2345
5.73k
        {
2346
5.73k
          (void) Get8BIMProperty(image,property,exception);
2347
5.73k
          break;
2348
5.73k
        }
2349
31
      break;
2350
5.76k
    }
2351
31
    case 'E':
2352
873k
    case 'e':
2353
873k
    {
2354
873k
      if (LocaleNCompare("exif:",property,5) == 0)
2355
873k
        {
2356
873k
          (void) GetEXIFProperty(image,property,exception);
2357
873k
          break;
2358
873k
        }
2359
53
      break;
2360
873k
    }
2361
53
    case 'I':
2362
443k
    case 'i':
2363
443k
    {
2364
443k
      if ((LocaleNCompare("icc:",property,4) == 0) ||
2365
222k
          (LocaleNCompare("icm:",property,4) == 0))
2366
221k
        {
2367
221k
#if defined(MAGICKCORE_LCMS_DELEGATE)
2368
221k
          (void) GetICCProperty(image,exception);
2369
221k
#endif
2370
221k
          break;
2371
221k
        }
2372
222k
      if (LocaleNCompare("iptc:",property,5) == 0)
2373
221k
        {
2374
221k
          (void) GetIPTCProperty(image,property,exception);
2375
221k
          break;
2376
221k
        }
2377
53
      break;
2378
222k
    }
2379
53
    case 'X':
2380
231k
    case 'x':
2381
231k
    {
2382
231k
      if (LocaleNCompare("xmp:",property,4) == 0)
2383
221k
        {
2384
221k
          (void) GetXMPProperty(image,property,exception);
2385
221k
          break;
2386
221k
        }
2387
9.30k
      break;
2388
231k
    }
2389
961k
    default:
2390
961k
      break;
2391
2.51M
  }
2392
2.51M
  if ((image->properties != (void *) NULL) &&
2393
1.40M
      (read_from_properties != MagickFalse))
2394
864k
    {
2395
864k
      p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
2396
864k
        image->properties,property);
2397
864k
      return(p);
2398
864k
    }
2399
1.65M
  return((const char *) NULL);
2400
2.51M
}
2401

2402
/*
2403
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2404
%                                                                             %
2405
%                                                                             %
2406
%                                                                             %
2407
+   G e t M a g i c k P r o p e r t y                                         %
2408
%                                                                             %
2409
%                                                                             %
2410
%                                                                             %
2411
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2412
%
2413
%  GetMagickProperty() gets attributes or calculated values that is associated
2414
%  with a fixed known property name, or single letter property. It may be
2415
%  called if no image is defined (IMv7), in which case only global image_info
2416
%  values are available:
2417
%
2418
%    \n   newline
2419
%    \r   carriage return
2420
%    <    less-than character.
2421
%    >    greater-than character.
2422
%    &    ampersand character.
2423
%    %%   a percent sign
2424
%    %b   file size of image read in
2425
%    %c   comment meta-data property
2426
%    %d   directory component of path
2427
%    %e   filename extension or suffix
2428
%    %f   filename (including suffix)
2429
%    %g   layer canvas page geometry   (equivalent to "%Wx%H%X%Y")
2430
%    %h   current image height in pixels
2431
%    %i   image filename (note: becomes output filename for "info:")
2432
%    %k   CALCULATED: number of unique colors
2433
%    %l   label meta-data property
2434
%    %m   image file format (file magic)
2435
%    %n   number of images in current image sequence
2436
%    %o   output filename  (used for delegates)
2437
%    %p   index of image in current image list
2438
%    %q   quantum depth (compile-time constant)
2439
%    %r   image class and colorspace
2440
%    %s   scene number (from input unless re-assigned)
2441
%    %t   filename without directory or extension (suffix)
2442
%    %u   unique temporary filename (used for delegates)
2443
%    %w   current width in pixels
2444
%    %x   x resolution (density)
2445
%    %y   y resolution (density)
2446
%    %z   image depth (as read in unless modified, image save depth)
2447
%    %A   image transparency channel enabled (true/false)
2448
%    %B   file size of image in bytes
2449
%    %C   image compression type
2450
%    %D   image GIF dispose method
2451
%    %G   original image size (%wx%h; before any resizes)
2452
%    %H   page (canvas) height
2453
%    %M   Magick filename (original file exactly as given,  including read mods)
2454
%    %O   page (canvas) offset ( = %X%Y )
2455
%    %P   page (canvas) size ( = %Wx%H )
2456
%    %Q   image compression quality ( 0 = default )
2457
%    %S   ?? scenes ??
2458
%    %T   image time delay (in centi-seconds)
2459
%    %U   image resolution units
2460
%    %W   page (canvas) width
2461
%    %X   page (canvas) x offset (including sign)
2462
%    %Y   page (canvas) y offset (including sign)
2463
%    %Z   unique filename (used for delegates)
2464
%    %@   CALCULATED: trim bounding box (without actually trimming)
2465
%    %#   CALCULATED: 'signature' hash of image values
2466
%
2467
%  This routine only handles specifically known properties.  It does not
2468
%  handle special prefixed properties, profiles, or expressions. Nor does
2469
%  it return any free-form property strings.
2470
%
2471
%  The returned string is stored in a structure somewhere, and should not be
2472
%  directly freed.  If the string was generated (common) the string will be
2473
%  stored as as either as artifact or option 'magick-property'.  These may be
2474
%  deleted (cleaned up) when no longer required, but neither artifact or
2475
%  option is guaranteed to exist.
2476
%
2477
%  The format of the GetMagickProperty method is:
2478
%
2479
%      const char *GetMagickProperty(ImageInfo *image_info,Image *image,
2480
%        const char *property,ExceptionInfo *exception)
2481
%
2482
%  A description of each parameter follows:
2483
%
2484
%    o image_info: the image info (optional)
2485
%
2486
%    o image: the image (optional)
2487
%
2488
%    o key: the key.
2489
%
2490
%    o exception: return any errors or warnings in this structure.
2491
%
2492
*/
2493
static const char *GetMagickPropertyLetter(ImageInfo *image_info,
2494
  Image *image,const char letter,ExceptionInfo *exception)
2495
12.5k
{
2496
12.5k
#define WarnNoImageReturn(format,arg) \
2497
12.5k
  if (image == (Image *) NULL ) { \
2498
0
    (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2499
0
      "NoImageForProperty",format,arg); \
2500
0
    return((const char *) NULL); \
2501
0
  }
2502
12.5k
#define WarnNoImageInfoReturn(format,arg) \
2503
12.5k
  if (image_info == (ImageInfo *) NULL ) { \
2504
0
    (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
2505
0
      "NoImageInfoForProperty",format,arg); \
2506
0
    return((const char *) NULL); \
2507
0
  }
2508
2509
12.5k
  char
2510
12.5k
    value[MagickPathExtent];  /* formatted string to store as an artifact */
2511
2512
12.5k
  const char
2513
12.5k
    *string;     /* return a string already stored somewhere */
2514
2515
12.5k
  if ((image != (Image *) NULL) && (IsEventLogging() != MagickFalse))
2516
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2517
12.5k
  else
2518
12.5k
    if ((image_info != (ImageInfo *) NULL) &&
2519
12.5k
        (IsEventLogging() != MagickFalse))
2520
0
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
2521
12.5k
  *value='\0';           /* formatted string */
2522
12.5k
  string=(char *) NULL;  /* constant string reference */
2523
  /*
2524
    Get properties that are directly defined by images.
2525
  */
2526
12.5k
  switch (letter)
2527
12.5k
  {
2528
1.79k
    case 'b':  /* image size read in - in bytes */
2529
1.79k
    {
2530
1.79k
      WarnNoImageReturn("\"%%%c\"",letter);
2531
1.79k
      (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
2532
1.79k
        value);
2533
1.79k
      if (image->extent == 0)
2534
182
        (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
2535
182
          MagickPathExtent,value);
2536
1.79k
      break;
2537
1.79k
    }
2538
97
    case 'c':  /* image comment property - empty string by default */
2539
97
    {
2540
97
      WarnNoImageReturn("\"%%%c\"",letter);
2541
97
      string=GetImageProperty(image,"comment",exception);
2542
97
      if ( string == (const char *) NULL )
2543
97
        string="";
2544
97
      break;
2545
97
    }
2546
139
    case 'd':  /* Directory component of filename */
2547
139
    {
2548
139
      WarnNoImageReturn("\"%%%c\"",letter);
2549
139
      GetPathComponent(image->magick_filename,HeadPath,value);
2550
139
      if (*value == '\0')
2551
102
        string="";
2552
139
      break;
2553
139
    }
2554
329
    case 'e': /* Filename extension (suffix) of image file */
2555
329
    {
2556
329
      WarnNoImageReturn("\"%%%c\"",letter);
2557
329
      GetPathComponent(image->magick_filename,ExtensionPath,value);
2558
329
      if (*value == '\0')
2559
216
        string="";
2560
329
      break;
2561
329
    }
2562
2.00k
    case 'f': /* Filename without directory component */
2563
2.00k
    {
2564
2.00k
      WarnNoImageReturn("\"%%%c\"",letter);
2565
2.00k
      GetPathComponent(image->magick_filename,TailPath,value);
2566
2.00k
      if (*value == '\0')
2567
35
        string="";
2568
2.00k
      break;
2569
2.00k
    }
2570
73
    case 'g': /* Image geometry, canvas and offset  %Wx%H+%X+%Y */
2571
73
    {
2572
73
      WarnNoImageReturn("\"%%%c\"",letter);
2573
73
      (void) FormatLocaleString(value,MagickPathExtent,
2574
73
        "%.17gx%.17g%+.20g%+.20g",(double) image->page.width,(double)
2575
73
        image->page.height,(double) image->page.x,(double) image->page.y);
2576
73
      break;
2577
73
    }
2578
175
    case 'h': /* Image height (current) */
2579
175
    {
2580
175
      WarnNoImageReturn("\"%%%c\"",letter);
2581
175
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2582
175
        (image->rows != 0 ? image->rows : image->magick_rows));
2583
175
      break;
2584
175
    }
2585
68
    case 'i': /* Filename last used for an image (read or write) */
2586
68
    {
2587
68
      WarnNoImageReturn("\"%%%c\"",letter);
2588
68
      string=image->filename;
2589
68
      break;
2590
68
    }
2591
305
    case 'k': /* Number of unique colors  */
2592
305
    {
2593
      /*
2594
        FUTURE: ensure this does not generate the formatted comment!
2595
      */
2596
305
      WarnNoImageReturn("\"%%%c\"",letter);
2597
305
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2598
305
        GetNumberColors(image,(FILE *) NULL,exception));
2599
305
      break;
2600
305
    }
2601
71
    case 'l': /* Image label property - empty string by default */
2602
71
    {
2603
71
      WarnNoImageReturn("\"%%%c\"",letter);
2604
71
      string=GetImageProperty(image,"label",exception);
2605
71
      if (string == (const char *) NULL)
2606
71
        string="";
2607
71
      break;
2608
71
    }
2609
72
    case 'm': /* Image format (file magick) */
2610
72
    {
2611
72
      WarnNoImageReturn("\"%%%c\"",letter);
2612
72
      string=image->magick;
2613
72
      break;
2614
72
    }
2615
105
    case 'n': /* Number of images in the list.  */
2616
105
    {
2617
105
      if ( image != (Image *) NULL )
2618
105
        (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2619
105
          GetImageListLength(image));
2620
0
      else
2621
0
        string="0";    /* no images or scenes */
2622
105
      break;
2623
72
    }
2624
94
    case 'o': /* Output Filename - for delegate use only */
2625
94
    {
2626
94
      WarnNoImageInfoReturn("\"%%%c\"",letter);
2627
94
      string=image_info->filename;
2628
94
      break;
2629
94
    }
2630
94
    case 'p': /* Image index in current image list */
2631
94
    {
2632
94
      WarnNoImageReturn("\"%%%c\"",letter);
2633
94
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2634
94
        GetImageIndexInList(image));
2635
94
      break;
2636
94
    }
2637
69
    case 'q': /* Quantum depth of image in memory */
2638
69
    {
2639
69
      WarnNoImageReturn("\"%%%c\"",letter);
2640
69
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2641
69
        MAGICKCORE_QUANTUM_DEPTH);
2642
69
      break;
2643
69
    }
2644
147
    case 'r': /* Image storage class, colorspace, and alpha enabled.  */
2645
147
    {
2646
147
      ColorspaceType
2647
147
        colorspace;
2648
2649
147
      WarnNoImageReturn("\"%%%c\"",letter);
2650
147
      colorspace=image->colorspace;
2651
147
      (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s",
2652
147
        CommandOptionToMnemonic(MagickClassOptions,(ssize_t)
2653
147
        image->storage_class),CommandOptionToMnemonic(MagickColorspaceOptions,
2654
147
        (ssize_t) colorspace),image->alpha_trait != UndefinedPixelTrait ?
2655
147
        "Alpha" : "");
2656
147
      break;
2657
147
    }
2658
78
    case 's': /* Image scene number */
2659
78
    {
2660
#if 0  /* this seems non-sensical -- simplifying */
2661
      if (image_info->number_scenes != 0)
2662
        (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2663
          image_info->scene);
2664
      else if (image != (Image *) NULL)
2665
        (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2666
          image->scene);
2667
      else
2668
          string="0";
2669
#else
2670
78
      WarnNoImageReturn("\"%%%c\"",letter);
2671
78
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2672
78
        image->scene);
2673
78
#endif
2674
78
      break;
2675
78
    }
2676
341
    case 't': /* Base filename without directory or extension */
2677
341
    {
2678
341
      WarnNoImageReturn("\"%%%c\"",letter);
2679
341
      GetPathComponent(image->magick_filename,BasePath,value);
2680
341
      if (*value == '\0')
2681
35
        string="";
2682
341
      break;
2683
341
    }
2684
68
    case 'u': /* Unique filename */
2685
68
    {
2686
68
      WarnNoImageInfoReturn("\"%%%c\"",letter);
2687
68
      string=image_info->unique;
2688
68
      break;
2689
68
    }
2690
175
    case 'w': /* Image width (current) */
2691
175
    {
2692
175
      WarnNoImageReturn("\"%%%c\"",letter);
2693
175
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2694
175
        (image->columns != 0 ? image->columns : image->magick_columns));
2695
175
      break;
2696
175
    }
2697
85
    case 'x': /* Image horizontal resolution (with units) */
2698
85
    {
2699
85
      WarnNoImageReturn("\"%%%c\"",letter);
2700
85
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",
2701
85
        fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x :
2702
85
        image->units == PixelsPerCentimeterResolution ? DefaultResolution/2.54 :
2703
85
        DefaultResolution);
2704
85
      break;
2705
85
    }
2706
70
    case 'y': /* Image vertical resolution (with units) */
2707
70
    {
2708
70
      WarnNoImageReturn("\"%%%c\"",letter);
2709
70
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",
2710
70
        fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y :
2711
70
        image->units == PixelsPerCentimeterResolution ? DefaultResolution/2.54 :
2712
70
        DefaultResolution);
2713
70
      break;
2714
70
    }
2715
67
    case 'z': /* Image depth as read in */
2716
67
    {
2717
67
      WarnNoImageReturn("\"%%%c\"",letter);
2718
67
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2719
67
        image->depth);
2720
67
      break;
2721
67
    }
2722
73
    case 'A': /* Image alpha channel  */
2723
73
    {
2724
73
      WarnNoImageReturn("\"%%%c\"",letter);
2725
73
      string=CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t)
2726
73
        image->alpha_trait);
2727
73
      break;
2728
73
    }
2729
66
    case 'B':  /* image size read in - in bytes */
2730
66
    {
2731
66
      WarnNoImageReturn("\"%%%c\"",letter);
2732
66
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2733
66
        image->extent);
2734
66
      if (image->extent == 0)
2735
66
        (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2736
66
          GetBlobSize(image));
2737
66
      break;
2738
66
    }
2739
69
    case 'C': /* Image compression method.  */
2740
69
    {
2741
69
      WarnNoImageReturn("\"%%%c\"",letter);
2742
69
      string=CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
2743
69
        image->compression);
2744
69
      break;
2745
69
    }
2746
68
    case 'D': /* Image dispose method.  */
2747
68
    {
2748
68
      WarnNoImageReturn("\"%%%c\"",letter);
2749
68
      string=CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t)
2750
68
        image->dispose);
2751
68
      break;
2752
68
    }
2753
1.78k
    case 'G': /* Image size as geometry = "%wx%h" */
2754
1.78k
    {
2755
1.78k
      WarnNoImageReturn("\"%%%c\"",letter);
2756
1.78k
      (void) FormatLocaleString(value,MagickPathExtent,"%.17gx%.17g",(double)
2757
1.78k
        image->magick_columns,(double) image->magick_rows);
2758
1.78k
      break;
2759
1.78k
    }
2760
69
    case 'H': /* layer canvas height */
2761
69
    {
2762
69
      WarnNoImageReturn("\"%%%c\"",letter);
2763
69
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2764
69
        image->page.height);
2765
69
      break;
2766
69
    }
2767
78
    case 'M': /* Magick filename - filename given incl. coder & read mods */
2768
78
    {
2769
78
      WarnNoImageReturn("\"%%%c\"",letter);
2770
78
      string=image->magick_filename;
2771
78
      break;
2772
78
    }
2773
72
    case 'N': /* Number of images in the list.  */
2774
72
    {
2775
72
      if ((image != (Image *) NULL) && (image->next == (Image *) NULL))
2776
72
        (void) FormatLocaleString(value,MagickPathExtent,"%.17g\n",(double)
2777
72
          GetImageListLength(image));
2778
0
      else
2779
0
        string="";
2780
72
      break;
2781
78
    }
2782
67
    case 'O': /* layer canvas offset with sign = "+%X+%Y" */
2783
67
    {
2784
67
      WarnNoImageReturn("\"%%%c\"",letter);
2785
67
      (void) FormatLocaleString(value,MagickPathExtent,"%+ld%+ld",(long)
2786
67
        image->page.x,(long) image->page.y);
2787
67
      break;
2788
67
    }
2789
66
    case 'P': /* layer canvas page size = "%Wx%H" */
2790
66
    {
2791
66
      WarnNoImageReturn("\"%%%c\"",letter);
2792
66
      (void) FormatLocaleString(value,MagickPathExtent,"%.17gx%.17g",(double)
2793
66
        image->page.width,(double) image->page.height);
2794
66
      break;
2795
66
    }
2796
68
    case 'Q': /* image compression quality */
2797
68
    {
2798
68
      WarnNoImageReturn("\"%%%c\"",letter);
2799
68
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2800
68
        (image->quality == 0 ? 92 : image->quality));
2801
68
      break;
2802
68
    }
2803
240
    case 'S': /* Number of scenes in image list.  */
2804
240
    {
2805
240
      WarnNoImageInfoReturn("\"%%%c\"",letter);
2806
#if 0 /* What is this number? -- it makes no sense - simplifying */
2807
      if (image_info->number_scenes == 0)
2808
         string="2147483647";
2809
      else if ( image != (Image *) NULL )
2810
        (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2811
                image_info->scene+image_info->number_scenes);
2812
      else
2813
        string="0";
2814
#else
2815
240
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2816
240
        (image_info->number_scenes == 0 ? 2147483647 :
2817
240
         image_info->number_scenes));
2818
240
#endif
2819
240
      break;
2820
240
    }
2821
70
    case 'T': /* image time delay for animations */
2822
70
    {
2823
70
      WarnNoImageReturn("\"%%%c\"",letter);
2824
70
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2825
70
        image->delay);
2826
70
      break;
2827
70
    }
2828
75
    case 'U': /* Image resolution units. */
2829
75
    {
2830
75
      WarnNoImageReturn("\"%%%c\"",letter);
2831
75
      string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
2832
75
        image->units);
2833
75
      break;
2834
75
    }
2835
66
    case 'W': /* layer canvas width */
2836
66
    {
2837
66
      WarnNoImageReturn("\"%%%c\"",letter);
2838
66
      (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2839
66
        image->page.width);
2840
66
      break;
2841
66
    }
2842
70
    case 'X': /* layer canvas X offset */
2843
70
    {
2844
70
      WarnNoImageReturn("\"%%%c\"",letter);
2845
70
      (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
2846
70
        image->page.x);
2847
70
      break;
2848
70
    }
2849
69
    case 'Y': /* layer canvas Y offset */
2850
69
    {
2851
69
      WarnNoImageReturn("\"%%%c\"",letter);
2852
69
      (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
2853
69
        image->page.y);
2854
69
      break;
2855
69
    }
2856
21
    case '%': /* percent escaped */
2857
21
    {
2858
21
      string="%";
2859
21
      break;
2860
69
    }
2861
643
    case '@': /* Trim bounding box, without actually Trimming! */
2862
643
    {
2863
643
      RectangleInfo
2864
643
        page;
2865
2866
643
      WarnNoImageReturn("\"%%%c\"",letter);
2867
643
      page=GetImageBoundingBox(image,exception);
2868
643
      (void) FormatLocaleString(value,MagickPathExtent,
2869
643
        "%.17gx%.17g%+.20g%+.20g",(double) page.width,(double) page.height,
2870
643
        (double) page.x,(double)page.y);
2871
643
      break;
2872
643
    }
2873
1.62k
    case '#':
2874
1.62k
    {
2875
      /*
2876
        Image signature.
2877
      */
2878
1.62k
      WarnNoImageReturn("\"%%%c\"",letter);
2879
1.62k
      if ((image->columns != 0) && (image->rows != 0))
2880
1.44k
        (void) SignatureImage(image,exception);
2881
1.62k
      string=GetImageProperty(image,"signature",exception);
2882
1.62k
      break;
2883
1.62k
    }
2884
12.5k
  }
2885
12.5k
  if (string != (char *) NULL)
2886
2.68k
    return(string);
2887
9.82k
  if (*value != '\0')
2888
9.01k
    {
2889
      /*
2890
        Create a cloned copy of result.
2891
      */
2892
9.01k
      if (image != (Image *) NULL)
2893
9.01k
        {
2894
9.01k
          (void) SetImageArtifact(image,"magick-property",value);
2895
9.01k
          return(GetImageArtifact(image,"magick-property"));
2896
9.01k
        }
2897
0
      else
2898
0
        {
2899
0
          (void) SetImageOption(image_info,"magick-property",value);
2900
0
          return(GetImageOption(image_info,"magick-property"));
2901
0
        }
2902
9.01k
    }
2903
810
  return((char *) NULL);
2904
9.82k
}
2905
2906
MagickExport const char *GetMagickProperty(ImageInfo *image_info,
2907
  Image *image,const char *property,ExceptionInfo *exception)
2908
3.13k
{
2909
3.13k
  char
2910
3.13k
    value[MagickPathExtent];
2911
2912
3.13k
  const char
2913
3.13k
    *string;
2914
2915
3.13k
  assert(property != (const char *) NULL);
2916
3.13k
  assert(property[0] != '\0');
2917
3.13k
  assert(image != (Image *) NULL || image_info != (ImageInfo *) NULL );
2918
3.13k
  if (property[1] == '\0')  /* single letter property request */
2919
1.55k
    return(GetMagickPropertyLetter(image_info,image,*property,exception));
2920
1.58k
  if ((image != (Image *) NULL) && (IsEventLogging() != MagickFalse))
2921
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2922
1.58k
  else
2923
1.58k
    if ((image_info != (ImageInfo *) NULL) &&
2924
1.58k
        (IsEventLogging() != MagickFalse))
2925
0
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
2926
1.58k
  *value='\0';           /* formated string */
2927
1.58k
  string=(char *) NULL;  /* constant string reference */
2928
1.58k
  switch (*property)
2929
1.58k
  {
2930
73
    case 'b':
2931
73
    {
2932
73
      if (LocaleCompare("basename",property) == 0)
2933
20
        {
2934
20
          WarnNoImageReturn("\"%%[%s]\"",property);
2935
20
          GetPathComponent(image->magick_filename,BasePath,value);
2936
20
          if (*value == '\0')
2937
10
            string="";
2938
20
          break;
2939
20
        }
2940
53
      if (LocaleCompare("bit-depth",property) == 0)
2941
10
        {
2942
10
          WarnNoImageReturn("\"%%[%s]\"",property);
2943
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2944
10
            GetImageDepth(image,exception));
2945
10
          break;
2946
10
        }
2947
43
      if (LocaleCompare("bounding-box",property) == 0)
2948
10
        {
2949
10
          RectangleInfo
2950
10
            geometry;
2951
2952
10
          WarnNoImageReturn("\"%%[%s]\"",property);
2953
10
          geometry=GetImageBoundingBox(image,exception);
2954
10
          (void) FormatLocaleString(value,MagickPathExtent,"%g,%g %g,%g",
2955
10
            (double) geometry.x,(double) geometry.y,
2956
10
            (double) geometry.x+geometry.width,
2957
10
            (double) geometry.y+geometry.height);
2958
10
          break;
2959
10
        }
2960
33
      break;
2961
43
    }
2962
157
    case 'c':
2963
157
    {
2964
157
      if (LocaleCompare("channels",property) == 0)
2965
11
        {
2966
11
          WarnNoImageReturn("\"%%[%s]\"",property);
2967
11
          (void) FormatLocaleString(value,MagickPathExtent,"%s%s %g.%g",
2968
11
            CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
2969
11
            image->colorspace),image->alpha_trait != UndefinedPixelTrait ?
2970
11
            "a" : " ",(double) image->number_channels,(double)
2971
11
            image->number_meta_channels);
2972
11
          LocaleLower(value);
2973
11
          break;
2974
11
        }
2975
146
      if (LocaleCompare("colors",property) == 0)
2976
0
        {
2977
0
          WarnNoImageReturn("\"%%[%s]\"",property);
2978
0
          image->colors=GetNumberColors(image,(FILE *) NULL,exception);
2979
0
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
2980
0
            image->colors);
2981
0
          break;
2982
0
        }
2983
146
      if (LocaleCompare("colorspace",property) == 0)
2984
0
        {
2985
0
          WarnNoImageReturn("\"%%[%s]\"",property);
2986
0
          string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
2987
0
            image->colorspace);
2988
0
          break;
2989
0
        }
2990
146
      if (LocaleCompare("compose",property) == 0)
2991
0
        {
2992
0
          WarnNoImageReturn("\"%%[%s]\"",property);
2993
0
          string=CommandOptionToMnemonic(MagickComposeOptions,(ssize_t)
2994
0
            image->compose);
2995
0
          break;
2996
0
        }
2997
146
      if (LocaleCompare("compression",property) == 0)
2998
0
        {
2999
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3000
0
          string=CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
3001
0
            image->compression);
3002
0
          break;
3003
0
        }
3004
146
      if (LocaleCompare("convex-hull",property) == 0)
3005
117
        {
3006
117
          char
3007
117
            *points;
3008
3009
117
          PointInfo
3010
117
            *convex_hull;
3011
3012
117
          ssize_t
3013
117
            n;
3014
3015
117
          size_t
3016
117
            number_points;
3017
3018
117
          WarnNoImageReturn("\"%%[%s]\"",property);
3019
117
          convex_hull=GetImageConvexHull(image,&number_points,exception);
3020
117
          if (convex_hull == (PointInfo *) NULL)
3021
117
            break;
3022
0
          points=AcquireString("");
3023
0
          for (n=0; n < (ssize_t) number_points; n++)
3024
0
          {
3025
0
            (void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
3026
0
              convex_hull[n].x,convex_hull[n].y);
3027
0
            (void) ConcatenateString(&points,value);
3028
0
          }
3029
0
          convex_hull=(PointInfo *) RelinquishMagickMemory(convex_hull);
3030
0
          (void) SetImageProperty(image,"convex-hull",points,exception);
3031
0
          points=DestroyString(points);
3032
0
          string=GetImageProperty(image,"convex-hull",exception);
3033
0
          break;
3034
117
        }
3035
29
      if (LocaleCompare("convex-hull:extreme-points",property) == 0)
3036
0
        {
3037
0
          char
3038
0
            *points;
3039
3040
0
          PointInfo
3041
0
            extreme,
3042
0
            *convex_hull;
3043
3044
0
          ssize_t
3045
0
            n;
3046
3047
0
          size_t
3048
0
            number_points;
3049
3050
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3051
0
          convex_hull=GetImageConvexHull(image,&number_points,exception);
3052
0
          if (convex_hull == (PointInfo *) NULL)
3053
0
            break;
3054
0
          points=AcquireString("");
3055
0
          extreme=convex_hull[0];  /* top */
3056
0
          for (n=0; n < (ssize_t) number_points; n++)
3057
0
          {
3058
0
            if (convex_hull[n].y < extreme.y)
3059
0
              {
3060
0
                extreme=convex_hull[n];
3061
0
                continue;
3062
0
              }
3063
0
            if (convex_hull[n].y != extreme.y)
3064
0
              continue;
3065
0
            if (convex_hull[n].x < extreme.x)
3066
0
              extreme=convex_hull[n];
3067
0
          }
3068
0
          (void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
3069
0
            extreme.x,extreme.y);
3070
0
          (void) ConcatenateString(&points,value);
3071
0
          extreme=convex_hull[0]; /* right */
3072
0
          for (n=0; n < (ssize_t) number_points; n++)
3073
0
          {
3074
0
            if (convex_hull[n].x > extreme.x)
3075
0
              {
3076
0
                extreme=convex_hull[n];
3077
0
                continue;
3078
0
              }
3079
0
            if (convex_hull[n].x != extreme.x)
3080
0
              continue;
3081
0
            if (convex_hull[n].y < extreme.y)
3082
0
              extreme=convex_hull[n];
3083
0
          }
3084
0
          (void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
3085
0
            extreme.x,extreme.y);
3086
0
          (void) ConcatenateString(&points,value);
3087
0
          extreme=convex_hull[0];  /* bottom */
3088
0
          for (n=0; n < (ssize_t) number_points; n++)
3089
0
          {
3090
0
            if (convex_hull[n].y > extreme.y)
3091
0
              {
3092
0
                extreme=convex_hull[n];
3093
0
                continue;
3094
0
              }
3095
0
            if (convex_hull[n].y != extreme.y)
3096
0
              continue;
3097
0
            if (convex_hull[n].x > extreme.x)
3098
0
              extreme=convex_hull[n];
3099
0
          }
3100
0
          (void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
3101
0
            extreme.x,extreme.y);
3102
0
          (void) ConcatenateString(&points,value);
3103
0
          extreme=convex_hull[0];  /* left */
3104
0
          for (n=0; n < (ssize_t) number_points; n++)
3105
0
          {
3106
0
            if (convex_hull[n].x < extreme.x)
3107
0
              {
3108
0
                extreme=convex_hull[n];
3109
0
                continue;
3110
0
              }
3111
0
            if (convex_hull[n].x != extreme.x)
3112
0
              continue;
3113
0
            if (convex_hull[n].y > extreme.y)
3114
0
              extreme=convex_hull[n];
3115
0
          }
3116
0
          (void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
3117
0
            extreme.x,extreme.y);
3118
0
          (void) ConcatenateString(&points,value);
3119
0
          convex_hull=(PointInfo *) RelinquishMagickMemory(convex_hull);
3120
0
          (void) SetImageProperty(image,"convex-hull:extreme-points",points,
3121
0
            exception);
3122
0
          points=DestroyString(points);
3123
0
          string=GetImageProperty(image,"convex-hull:extreme-points",exception);
3124
0
          break;
3125
0
        }
3126
29
      if (LocaleCompare("copyright",property) == 0)
3127
0
        {
3128
0
          (void) CopyMagickString(value,GetMagickCopyright(),MagickPathExtent);
3129
0
          break;
3130
0
        }
3131
29
      break;
3132
29
    }
3133
43
    case 'd':
3134
43
    {
3135
43
      if (LocaleCompare("depth",property) == 0)
3136
18
        {
3137
18
          WarnNoImageReturn("\"%%[%s]\"",property);
3138
18
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
3139
18
            image->depth);
3140
18
          break;
3141
18
        }
3142
25
      if (LocaleCompare("directory",property) == 0)
3143
0
        {
3144
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3145
0
          GetPathComponent(image->magick_filename,HeadPath,value);
3146
0
          if (*value == '\0')
3147
0
            string="";
3148
0
          break;
3149
0
        }
3150
25
      break;
3151
25
    }
3152
79
    case 'e':
3153
79
    {
3154
79
      if (LocaleCompare("entropy",property) == 0)
3155
10
        {
3156
10
          double
3157
10
            entropy;
3158
3159
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3160
10
          (void) GetImageEntropy(image,&entropy,exception);
3161
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3162
10
            GetMagickPrecision(),entropy);
3163
10
          break;
3164
10
        }
3165
69
      if (LocaleCompare("extension",property) == 0)
3166
0
        {
3167
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3168
0
          GetPathComponent(image->magick_filename,ExtensionPath,value);
3169
0
          if (*value == '\0')
3170
0
            string="";
3171
0
          break;
3172
0
        }
3173
69
      break;
3174
69
    }
3175
69
    case 'g':
3176
52
    {
3177
52
      if (LocaleCompare("gamma",property) == 0)
3178
24
        {
3179
24
          WarnNoImageReturn("\"%%[%s]\"",property);
3180
24
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3181
24
            GetMagickPrecision(),image->gamma);
3182
24
          break;
3183
24
        }
3184
28
      break;
3185
52
    }
3186
30
    case 'h':
3187
30
    {
3188
30
      if (LocaleCompare("height",property) == 0)
3189
0
        {
3190
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3191
0
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",
3192
0
            image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
3193
0
          break;
3194
0
        }
3195
30
      break;
3196
30
    }
3197
96
    case 'i':
3198
96
    {
3199
96
      if (LocaleCompare("input",property) == 0)
3200
0
        {
3201
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3202
0
          string=image->filename;
3203
0
          break;
3204
0
        }
3205
96
      if (LocaleCompare("interlace",property) == 0)
3206
0
        {
3207
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3208
0
          string=CommandOptionToMnemonic(MagickInterlaceOptions,(ssize_t)
3209
0
            image->interlace);
3210
0
          break;
3211
0
        }
3212
96
      break;
3213
96
    }
3214
96
    case 'k':
3215
19
    {
3216
19
      if (LocaleCompare("kurtosis",property) == 0)
3217
0
        {
3218
0
          double
3219
0
            kurtosis,
3220
0
            skewness;
3221
3222
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3223
0
          (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
3224
0
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3225
0
            GetMagickPrecision(),kurtosis);
3226
0
          break;
3227
0
        }
3228
19
      break;
3229
19
    }
3230
192
    case 'm':
3231
192
    {
3232
192
      if (LocaleCompare("magick",property) == 0)
3233
0
        {
3234
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3235
0
          string=image->magick;
3236
0
          break;
3237
0
        }
3238
192
      if ((LocaleCompare("maxima",property) == 0) ||
3239
182
          (LocaleCompare("max",property) == 0))
3240
28
        {
3241
28
          double
3242
28
            maximum,
3243
28
            minimum;
3244
3245
28
          WarnNoImageReturn("\"%%[%s]\"",property);
3246
28
          (void) GetImageRange(image,&minimum,&maximum,exception);
3247
28
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3248
28
            GetMagickPrecision(),maximum);
3249
28
          break;
3250
28
        }
3251
164
      if (LocaleCompare("mean",property) == 0)
3252
67
        {
3253
67
          double
3254
67
            mean,
3255
67
            standard_deviation;
3256
3257
67
          WarnNoImageReturn("\"%%[%s]\"",property);
3258
67
          (void) GetImageMean(image,&mean,&standard_deviation,exception);
3259
67
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3260
67
            GetMagickPrecision(),mean);
3261
67
          break;
3262
67
        }
3263
97
      if (LocaleCompare("median",property) == 0)
3264
10
        {
3265
10
          double
3266
10
            median;
3267
3268
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3269
10
          (void) GetImageMedian(image,&median,exception);
3270
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3271
10
            GetMagickPrecision(),median);
3272
10
          break;
3273
10
        }
3274
87
      if (LocaleCompare("mime:type",property) == 0)
3275
0
        {
3276
0
          const MagickInfo
3277
0
            *magick_info;
3278
3279
0
          magick_info=GetMagickInfo(image->magick,exception);
3280
0
          if ((magick_info != (const MagickInfo *) NULL) &&
3281
0
              (GetMagickMimeType(magick_info) != (const char *) NULL))
3282
0
            (void) CopyMagickString(value,GetMagickMimeType(magick_info),
3283
0
              MagickPathExtent);
3284
0
          break;
3285
0
        }
3286
87
      if ((LocaleCompare("minima",property) == 0) ||
3287
75
          (LocaleCompare("min",property) == 0))
3288
43
        {
3289
43
          double
3290
43
            maximum,
3291
43
            minimum;
3292
3293
43
          WarnNoImageReturn("\"%%[%s]\"",property);
3294
43
          (void) GetImageRange(image,&minimum,&maximum,exception);
3295
43
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3296
43
            GetMagickPrecision(),minimum);
3297
43
          break;
3298
43
        }
3299
44
      if (LocaleNCompare("minimum-bounding-box",property,20) == 0)
3300
4
        {
3301
4
          char
3302
4
            *points;
3303
3304
4
          PointInfo
3305
4
            *bounding_box;
3306
3307
4
          ssize_t
3308
4
            n;
3309
3310
4
          size_t
3311
4
            number_points;
3312
3313
4
          WarnNoImageReturn("\"%%[%s]\"",property);
3314
4
          bounding_box=GetImageMinimumBoundingBox(image,&number_points,
3315
4
            exception);
3316
4
          if (bounding_box == (PointInfo *) NULL)
3317
4
            break;
3318
0
          points=AcquireString("");
3319
0
          for (n=0; n < (ssize_t) number_points; n++)
3320
0
          {
3321
0
            (void) FormatLocaleString(value,MagickPathExtent,"%g,%g ",
3322
0
              bounding_box[n].x,bounding_box[n].y);
3323
0
            (void) ConcatenateString(&points,value);
3324
0
          }
3325
0
          bounding_box=(PointInfo *) RelinquishMagickMemory(bounding_box);
3326
0
          (void) SetImageProperty(image,"minimum-bounding-box",points,
3327
0
            exception);
3328
0
          points=DestroyString(points);
3329
0
          string=GetImageProperty(image,property,exception);
3330
0
          break;
3331
4
        }
3332
40
      break;
3333
44
    }
3334
42
    case 'o':
3335
42
    {
3336
42
      if (LocaleCompare("opaque",property) == 0)
3337
0
        {
3338
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3339
0
          string=CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t)
3340
0
            IsImageOpaque(image,exception));
3341
0
          break;
3342
0
        }
3343
42
      if (LocaleCompare("orientation",property) == 0)
3344
0
        {
3345
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3346
0
          string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
3347
0
            image->orientation);
3348
0
          break;
3349
0
        }
3350
42
      if (LocaleCompare("output",property) == 0)
3351
0
        {
3352
0
          WarnNoImageInfoReturn("\"%%[%s]\"",property);
3353
0
          (void) CopyMagickString(value,image_info->filename,MagickPathExtent);
3354
0
          break;
3355
0
        }
3356
42
      break;
3357
42
    }
3358
209
    case 'p':
3359
209
    {
3360
209
      if (LocaleCompare("page",property) == 0)
3361
18
        {
3362
18
          WarnNoImageReturn("\"%%[%s]\"",property);
3363
18
          (void) FormatLocaleString(value,MagickPathExtent,"%.17gx%.17g",
3364
18
            (double) image->page.width,(double) image->page.height);
3365
18
          break;
3366
18
        }
3367
191
      if (LocaleNCompare("papersize:",property,10) == 0)
3368
97
        {
3369
97
          char
3370
97
            *papersize;
3371
3372
97
          WarnNoImageReturn("\"%%[%s]\"",property);
3373
97
          *value='\0';
3374
97
          papersize=GetPageGeometry(property+10);
3375
97
          if (papersize != (const char *) NULL)
3376
97
            {
3377
97
              RectangleInfo
3378
97
                page = { 0, 0, 0, 0 };
3379
3380
97
              (void) ParseAbsoluteGeometry(papersize,&page);
3381
97
              (void) FormatLocaleString(value,MagickPathExtent,"%.17gx%.17g",
3382
97
                (double) page.width,(double) page.height);
3383
97
              papersize=DestroyString(papersize);
3384
97
            }
3385
97
          break;
3386
97
        }
3387
94
#if defined(MAGICKCORE_LCMS_DELEGATE)
3388
94
      if (LocaleCompare("profile:icc",property) == 0 ||
3389
84
          LocaleCompare("profile:icm",property) == 0)
3390
20
        {
3391
#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
3392
#define cmsUInt32Number  DWORD
3393
#endif
3394
3395
20
          const StringInfo
3396
20
            *profile;
3397
3398
20
          cmsHPROFILE
3399
20
            icc_profile;
3400
3401
20
          WarnNoImageReturn("\"%%[%s]\"",property);
3402
20
          profile=GetImageProfile(image,property+8);
3403
20
          if (profile == (StringInfo *) NULL)
3404
20
            break;
3405
0
          icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
3406
0
            (cmsUInt32Number) GetStringInfoLength(profile));
3407
0
          if (icc_profile != (cmsHPROFILE *) NULL)
3408
0
            {
3409
#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
3410
              string=cmsTakeProductName(icc_profile);
3411
#else
3412
0
              (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
3413
0
                "en","US",value,MagickPathExtent);
3414
0
#endif
3415
0
              (void) cmsCloseProfile(icc_profile);
3416
0
            }
3417
0
      }
3418
74
#endif
3419
74
      if (LocaleCompare("printsize.x",property) == 0)
3420
10
        {
3421
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3422
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3423
10
            GetMagickPrecision(),(double) MagickSafeReciprocal(
3424
10
              image->resolution.x)*image->columns);
3425
10
          break;
3426
10
        }
3427
64
      if (LocaleCompare("printsize.y",property) == 0)
3428
10
        {
3429
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3430
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3431
10
            GetMagickPrecision(),(double) MagickSafeReciprocal(
3432
10
              image->resolution.y)*image->rows);
3433
10
          break;
3434
10
        }
3435
54
      if (LocaleCompare("profiles",property) == 0)
3436
10
        {
3437
10
          const char
3438
10
            *name;
3439
3440
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3441
10
          ResetImageProfileIterator(image);
3442
10
          name=GetNextImageProfile(image);
3443
10
          if (name != (char *) NULL)
3444
0
            {
3445
0
              (void) CopyMagickString(value,name,MagickPathExtent);
3446
0
              name=GetNextImageProfile(image);
3447
0
              while (name != (char *) NULL)
3448
0
              {
3449
0
                ConcatenateMagickString(value,",",MagickPathExtent);
3450
0
                ConcatenateMagickString(value,name,MagickPathExtent);
3451
0
                name=GetNextImageProfile(image);
3452
0
              }
3453
0
            }
3454
10
          break;
3455
10
        }
3456
44
      break;
3457
54
    }
3458
44
    case 'q':
3459
25
    {
3460
25
      if (LocaleCompare("quality",property) == 0)
3461
0
        {
3462
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3463
0
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
3464
0
            image->quality);
3465
0
          break;
3466
0
        }
3467
25
      break;
3468
25
    }
3469
28
    case 'r':
3470
28
    {
3471
28
      if (LocaleCompare("resolution.x",property) == 0)
3472
0
        {
3473
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3474
0
          (void) FormatLocaleString(value,MagickPathExtent,"%g",
3475
0
            image->resolution.x);
3476
0
          break;
3477
0
        }
3478
28
      if (LocaleCompare("resolution.y",property) == 0)
3479
0
        {
3480
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3481
0
          (void) FormatLocaleString(value,MagickPathExtent,"%g",
3482
0
            image->resolution.y);
3483
0
          break;
3484
0
        }
3485
28
      break;
3486
28
    }
3487
170
    case 's':
3488
170
    {
3489
170
      if (LocaleCompare("scene",property) == 0)
3490
95
        {
3491
95
          WarnNoImageInfoReturn("\"%%[%s]\"",property);
3492
95
          if (image_info->number_scenes != 0)
3493
77
            (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
3494
77
              image_info->scene);
3495
18
          else {
3496
18
            WarnNoImageReturn("\"%%[%s]\"",property);
3497
18
            (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
3498
18
              image->scene);
3499
18
          }
3500
95
          break;
3501
95
        }
3502
75
      if (LocaleCompare("scenes",property) == 0)
3503
10
        {
3504
          /* FUTURE: equivalent to %n? */
3505
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3506
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
3507
10
            GetImageListLength(image));
3508
10
          break;
3509
10
        }
3510
65
      if (LocaleCompare("size",property) == 0)
3511
19
        {
3512
19
          WarnNoImageReturn("\"%%[%s]\"",property);
3513
19
          (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
3514
19
            MagickPathExtent,value);
3515
19
          break;
3516
19
        }
3517
46
      if (LocaleCompare("skewness",property) == 0)
3518
10
        {
3519
10
          double
3520
10
            kurtosis,
3521
10
            skewness;
3522
3523
10
          WarnNoImageReturn("\"%%[%s]\"",property);
3524
10
          (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
3525
10
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3526
10
            GetMagickPrecision(),skewness);
3527
10
          break;
3528
10
        }
3529
36
      if (LocaleCompare("standard-deviation",property) == 0)
3530
0
        {
3531
0
          double
3532
0
            mean,
3533
0
            standard_deviation;
3534
3535
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3536
0
          (void) GetImageMean(image,&mean,&standard_deviation,exception);
3537
0
          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
3538
0
            GetMagickPrecision(),standard_deviation);
3539
0
          break;
3540
0
        }
3541
36
      break;
3542
36
    }
3543
60
    case 't':
3544
60
    {
3545
60
      if (LocaleCompare("type",property) == 0)
3546
30
        {
3547
30
          WarnNoImageReturn("\"%%[%s]\"",property);
3548
30
          string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
3549
30
            IdentifyImageType(image,exception));
3550
30
          break;
3551
30
        }
3552
30
       break;
3553
60
    }
3554
48
    case 'u':
3555
48
    {
3556
48
      if (LocaleCompare("unique",property) == 0)
3557
10
        {
3558
10
          WarnNoImageInfoReturn("\"%%[%s]\"",property);
3559
10
          string=image_info->unique;
3560
10
          break;
3561
10
        }
3562
38
      if (LocaleCompare("units",property) == 0)
3563
18
        {
3564
18
          WarnNoImageReturn("\"%%[%s]\"",property);
3565
18
          string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
3566
18
            image->units);
3567
18
          break;
3568
18
        }
3569
20
      break;
3570
38
    }
3571
27
    case 'v':
3572
27
    {
3573
27
      if (LocaleCompare("version",property) == 0)
3574
0
        {
3575
0
          string=GetMagickVersion((size_t *) NULL);
3576
0
          break;
3577
0
        }
3578
27
      break;
3579
27
    }
3580
27
    case 'w':
3581
20
    {
3582
20
      if (LocaleCompare("width",property) == 0)
3583
0
        {
3584
0
          WarnNoImageReturn("\"%%[%s]\"",property);
3585
0
          (void) FormatLocaleString(value,MagickPathExtent,"%.17g",(double)
3586
0
            (image->magick_columns != 0 ? image->magick_columns : 256));
3587
0
          break;
3588
0
        }
3589
20
      break;
3590
20
    }
3591
1.58k
  }
3592
1.58k
  if (string != (char *) NULL)
3593
68
    return(string);
3594
1.51k
  if (*value != '\0')
3595
510
    {
3596
      /*
3597
        Create a cloned copy of result, that will get cleaned up, eventually.
3598
      */
3599
510
      if (image != (Image *) NULL)
3600
510
        {
3601
510
          (void) SetImageArtifact(image,"magick-property",value);
3602
510
          return(GetImageArtifact(image,"magick-property"));
3603
510
        }
3604
0
      else
3605
0
        {
3606
0
          (void) SetImageOption(image_info,"magick-property",value);
3607
0
          return(GetImageOption(image_info,"magick-property"));
3608
0
        }
3609
510
    }
3610
1.00k
  return((char *) NULL);
3611
1.51k
}
3612
#undef WarnNoImageReturn
3613

3614
/*
3615
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3616
%                                                                             %
3617
%                                                                             %
3618
%                                                                             %
3619
%   G e t N e x t I m a g e P r o p e r t y                                   %
3620
%                                                                             %
3621
%                                                                             %
3622
%                                                                             %
3623
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3624
%
3625
%  GetNextImageProperty() gets the next free-form string property name.
3626
%
3627
%  The format of the GetNextImageProperty method is:
3628
%
3629
%      char *GetNextImageProperty(const Image *image)
3630
%
3631
%  A description of each parameter follows:
3632
%
3633
%    o image: the image.
3634
%
3635
*/
3636
MagickExport const char *GetNextImageProperty(const Image *image)
3637
52.6k
{
3638
52.6k
  assert(image != (Image *) NULL);
3639
52.6k
  assert(image->signature == MagickCoreSignature);
3640
52.6k
  if (IsEventLogging() != MagickFalse)
3641
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3642
0
      image->filename);
3643
52.6k
  if (image->properties == (void *) NULL)
3644
104
    return((const char *) NULL);
3645
52.5k
  return((const char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
3646
52.6k
}
3647

3648
/*
3649
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3650
%                                                                             %
3651
%                                                                             %
3652
%                                                                             %
3653
%   I n t e r p r e t I m a g e P r o p e r t i e s                           %
3654
%                                                                             %
3655
%                                                                             %
3656
%                                                                             %
3657
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3658
%
3659
%  InterpretImageProperties() replaces any embedded formatting characters with
3660
%  the appropriate image property and returns the interpreted text.
3661
%
3662
%  This searches for and replaces
3663
%     \n \r \%          replaced by newline, return, and percent resp.
3664
%     &lt; &gt; &amp;   replaced by '<', '>', '&' resp.
3665
%     %%                replaced by percent
3666
%
3667
%     %x %[x]       where 'x' is a single letter prosperity, case sensitive).
3668
%     %[type:name]  where 'type' a is special and known prefix.
3669
%     %[name]       where 'name' is a specifically known attribute, calculated
3670
%                   value, or a per-image property string name, or a per-image
3671
%                   'artifact' (as generated from a global option).
3672
%                   It may contain ':' as long as the prefix is not special.
3673
%
3674
%  Single letter % substitutions will only happen if the character before the
3675
%  percent is NOT a number. But braced substitutions will always be performed.
3676
%  This prevents the typical usage of percent in a interpreted geometry
3677
%  argument from being substituted when the percent is a geometry flag.
3678
%
3679
%  If 'glob-expressions' ('*' or '?' characters) is used for 'name' it may be
3680
%  used as a search pattern to print multiple lines of "name=value\n" pairs of
3681
%  the associated set of properties.
3682
%
3683
%  The returned string must be freed using DestroyString() by the caller.
3684
%
3685
%  The format of the InterpretImageProperties method is:
3686
%
3687
%      char *InterpretImageProperties(ImageInfo *image_info,
3688
%        Image *image,const char *embed_text,ExceptionInfo *exception)
3689
%
3690
%  A description of each parameter follows:
3691
%
3692
%    o image_info: the image info. (required)
3693
%
3694
%    o image: the image. (optional)
3695
%
3696
%    o embed_text: the address of a character string containing the embedded
3697
%      formatting characters.
3698
%
3699
%    o exception: return any errors or warnings in this structure.
3700
%
3701
*/
3702
MagickExport char *InterpretImageProperties(ImageInfo *image_info,Image *image,
3703
  const char *embed_text,ExceptionInfo *exception)
3704
12.7k
{
3705
41.0k
#define ExtendInterpretText(string_length) \
3706
41.0k
{ \
3707
41.0k
  size_t length=(string_length); \
3708
41.0k
  if ((size_t) (q-interpret_text+(ssize_t) length+1) >= extent) \
3709
41.0k
    { \
3710
10.5k
      extent+=length; \
3711
10.5k
      interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
3712
10.5k
        MagickPathExtent,sizeof(*interpret_text)); \
3713
10.5k
      if (interpret_text == (char *) NULL) \
3714
10.5k
        { \
3715
0
          status=MagickFalse; \
3716
0
          goto cleanup; \
3717
0
        } \
3718
10.5k
      q=interpret_text+strlen(interpret_text); \
3719
10.5k
   } \
3720
41.0k
}
3721
3722
12.7k
#define AppendKeyValue2Text(key,value)\
3723
253
{ \
3724
253
  size_t length=strlen(key)+strlen(value)+2; \
3725
253
  if ((size_t) (q-interpret_text+(ssize_t) length+1) >= extent) \
3726
253
    { \
3727
0
      extent+=length; \
3728
0
      interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
3729
0
        MagickPathExtent,sizeof(*interpret_text)); \
3730
0
      if (interpret_text == (char *) NULL) \
3731
0
        { \
3732
0
          status=MagickFalse; \
3733
0
          goto cleanup; \
3734
0
        } \
3735
0
      q=interpret_text+strlen(interpret_text); \
3736
0
     } \
3737
253
   q+=(ptrdiff_t) FormatLocaleString(q,extent,"%s=%s\n",(key),(value)); \
3738
253
}
3739
3740
14.9k
#define AppendString2Text(string) \
3741
14.9k
{ \
3742
14.9k
  size_t length = strlen((string)); \
3743
14.9k
  if ((size_t) (q-interpret_text+(ssize_t) length+1) >= extent) \
3744
14.9k
    { \
3745
0
      extent+=length; \
3746
0
      interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
3747
0
        MagickPathExtent,sizeof(*interpret_text)); \
3748
0
      if (interpret_text == (char *) NULL) \
3749
0
        { \
3750
0
          status=MagickFalse; \
3751
0
          goto cleanup; \
3752
0
        } \
3753
0
      q=interpret_text+strlen(interpret_text); \
3754
0
    } \
3755
14.9k
  (void) CopyMagickString(q,(string),extent); \
3756
14.9k
  q+=(ptrdiff_t) length; \
3757
14.9k
}
3758
3759
12.7k
  char
3760
12.7k
    *interpret_text = (char *) NULL,
3761
12.7k
    *q;  /* current position in interpret_text */
3762
3763
12.7k
  const char
3764
12.7k
    *p;  /* position in embed_text string being expanded */
3765
3766
12.7k
  Image
3767
12.7k
    *property_image = (Image *) NULL;
3768
3769
12.7k
  ImageInfo
3770
12.7k
    *property_info = (ImageInfo *) NULL;
3771
3772
12.7k
  MagickBooleanType
3773
12.7k
    number,
3774
12.7k
    status = MagickTrue;
3775
3776
12.7k
  size_t
3777
12.7k
    extent;  /* allocated length of interpret_text */
3778
3779
12.7k
  if ((image != (Image *) NULL) && (IsEventLogging() != MagickFalse))
3780
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3781
12.7k
  else
3782
12.7k
    if ((image_info != (ImageInfo *) NULL) && (IsEventLogging() != MagickFalse))
3783
0
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3784
0
        image_info->filename);
3785
12.7k
    else
3786
12.7k
      if (IsEventLogging() != MagickFalse)
3787
0
        (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no image");
3788
12.7k
  if (embed_text == (const char *) NULL)
3789
1.71k
    return(ConstantString(""));
3790
11.0k
  p=embed_text;
3791
11.1k
  while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0'))
3792
97
    p++;
3793
11.0k
  if (*p == '\0')
3794
422
    return(ConstantString(""));
3795
10.5k
  if ((*p == '@') && (IsPathAccessible(p+1) != MagickFalse))
3796
3
    {
3797
      /*
3798
        Handle a '@' replace string from file.
3799
      */
3800
3
      interpret_text=FileToString(p,~0UL,exception);
3801
3
      if (interpret_text != (char *) NULL)
3802
3
        return(interpret_text);
3803
3
    }
3804
  /*
3805
    Translate any embedded format characters.
3806
  */
3807
10.5k
  if (image_info != (ImageInfo *) NULL)
3808
10.5k
    property_info=image_info;
3809
0
  else
3810
0
    property_info=CloneImageInfo(image_info);
3811
10.5k
  if ((image != (Image *) NULL) && (image->columns != 0) && (image->rows != 0))
3812
2.54k
    property_image=image;
3813
8.04k
  else
3814
8.04k
    {
3815
8.04k
      property_image=AcquireImage(image_info,exception);
3816
8.04k
      if (property_image == (Image *) NULL)
3817
0
        {
3818
0
          status=MagickFalse;
3819
0
          goto cleanup;
3820
0
        }
3821
8.04k
      (void) SetImageExtent(property_image,1,1,exception);
3822
8.04k
      (void) SetImageBackgroundColor(property_image,exception);
3823
8.04k
    }
3824
10.5k
  interpret_text=AcquireString(embed_text); /* new string with extra space */
3825
10.5k
  if (interpret_text == (char *) NULL)
3826
0
    {
3827
0
      status=MagickFalse;
3828
0
      goto cleanup;
3829
0
    }
3830
10.5k
  extent=MagickPathExtent;                  /* allocated space in string */
3831
10.5k
  number=MagickFalse;                       /* is last char a number? */
3832
51.4k
  for (q=interpret_text; *p!='\0'; number=isdigit((int) ((unsigned char) *p)) ? MagickTrue : MagickFalse,p++)
3833
41.0k
  {
3834
    /*
3835
      Look for the various escapes, (and handle other specials)
3836
    */
3837
41.0k
    *q='\0';
3838
41.0k
    ExtendInterpretText(MagickPathExtent);
3839
41.0k
    switch (*p)
3840
41.0k
    {
3841
349
      case '\\':
3842
349
      {
3843
349
        switch (*(p+1))
3844
349
        {
3845
3
          case '\0':
3846
3
            continue;
3847
66
          case 'r':  /* convert to RETURN */
3848
66
          {
3849
66
            *q++='\r';
3850
66
            p++;
3851
66
            continue;
3852
0
          }
3853
66
          case 'n':  /* convert to NEWLINE */
3854
66
          {
3855
66
            *q++='\n';
3856
66
            p++;
3857
66
            continue;
3858
0
          }
3859
68
          case '\n':  /* EOL removal UNIX,MacOSX */
3860
68
          {
3861
68
            p++;
3862
68
            continue;
3863
0
          }
3864
66
          case '\r':  /* EOL removal DOS,Windows */
3865
66
          {
3866
66
            p++;
3867
66
            if (*p == '\n') /* return-newline EOL */
3868
0
              p++;
3869
66
            continue;
3870
0
          }
3871
80
          default:
3872
80
          {
3873
80
            p++;
3874
80
            *q++=(*p);
3875
80
          }
3876
349
        }
3877
80
        continue;
3878
349
      }
3879
262
      case '&':
3880
262
      {
3881
262
        if (LocaleNCompare("&lt;",p,4) == 0)
3882
34
          {
3883
34
            *q++='<';
3884
34
            p+=(ptrdiff_t) 3;
3885
34
          }
3886
228
        else
3887
228
          if (LocaleNCompare("&gt;",p,4) == 0)
3888
34
            {
3889
34
              *q++='>';
3890
34
              p+=(ptrdiff_t) 3;
3891
34
            }
3892
194
          else
3893
194
            if (LocaleNCompare("&amp;",p,5) == 0)
3894
18
              {
3895
18
                *q++='&';
3896
18
                p+=(ptrdiff_t) 4;
3897
18
              }
3898
176
            else
3899
176
              *q++=(*p);
3900
262
        continue;
3901
349
      }
3902
22.2k
      case '%':
3903
22.2k
        break;  /* continue to next set of handlers */
3904
18.1k
      default:
3905
18.1k
      {
3906
18.1k
        *q++=(*p);  /* any thing else is 'as normal' */
3907
18.1k
        continue;
3908
349
      }
3909
41.0k
    }
3910
22.2k
    p++; /* advance beyond the percent */
3911
    /*
3912
      Doubled Percent - or percent at end of string.
3913
    */
3914
22.2k
    if ((*p == '\0') || (*p == '\'') || (*p == '"'))
3915
468
      p--;
3916
22.2k
    if (*p == '%')
3917
577
      {
3918
577
        *q++='%';
3919
577
        continue;
3920
577
      }
3921
    /*
3922
      Single letter escapes %c.
3923
    */
3924
21.6k
    if (*p != '[')
3925
11.0k
      {
3926
11.0k
        const char
3927
11.0k
          *string;
3928
3929
11.0k
        if (number != MagickFalse)
3930
65
          {
3931
            /*
3932
              But only if not preceded by a number!
3933
            */
3934
65
            *q++='%'; /* do NOT substitute the percent */
3935
65
            p--;      /* back up one */
3936
65
            continue;
3937
65
          }
3938
10.9k
        string=GetMagickPropertyLetter(property_info,image,*p,exception);
3939
10.9k
        if (string != (char *) NULL)
3940
10.2k
          {
3941
10.2k
            AppendString2Text(string);
3942
10.2k
            (void) DeleteImageArtifact(property_image,"magick-property");
3943
10.2k
            (void) DeleteImageOption(property_info,"magick-property");
3944
10.2k
            continue;
3945
10.2k
          }
3946
714
        (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3947
714
          "UnknownImageProperty","\"%%%c\"",*p);
3948
714
        continue;
3949
10.9k
      }
3950
10.6k
    {
3951
10.6k
      char
3952
10.6k
        pattern[2*MagickPathExtent] = "\0";
3953
3954
10.6k
      const char
3955
10.6k
        *key,
3956
10.6k
        *string;
3957
3958
10.6k
      ssize_t
3959
10.6k
        depth,
3960
10.6k
        offset;
3961
3962
      /*
3963
        Braced Percent Escape %[...].
3964
      */
3965
10.6k
      p++;  /* advance p to just inside the opening brace */
3966
10.6k
      depth=1;
3967
10.6k
      if (*p == ']')
3968
25
        {
3969
25
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
3970
25
            "UnknownImageProperty","\"%%[]\"");
3971
25
          status=MagickFalse;
3972
25
          goto cleanup;
3973
25
        }
3974
132k
      for (offset=0; offset < (MagickPathExtent-1L) && (*p != '\0'); )
3975
131k
      {
3976
131k
        if ((*p == '\\') && (*(p+1) != '\0'))
3977
460
          {
3978
            /*
3979
              Skip escaped braces within braced pattern.
3980
            */
3981
460
            pattern[offset++]=(*p++);
3982
460
            pattern[offset++]=(*p++);
3983
460
            continue;
3984
460
          }
3985
131k
        if (*p == '[')
3986
1.82k
          depth++;
3987
131k
        if (*p == ']')
3988
12.2k
          depth--;
3989
131k
        if (depth <= 0)
3990
10.4k
          break;
3991
121k
        pattern[offset++]=(*p++);
3992
121k
      }
3993
10.6k
      pattern[offset]='\0';
3994
10.6k
      if (depth != 0)
3995
156
        {
3996
          /*
3997
            Check for unmatched final ']' for "%[...]".
3998
          */
3999
156
          if (offset >= 64)
4000
0
            {
4001
0
              pattern[61]='.';  /* truncate string for error message */
4002
0
              pattern[62]='.';
4003
0
              pattern[63]='.';
4004
0
              pattern[64]='\0';
4005
0
            }
4006
156
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
4007
156
            "UnbalancedBraces","\"%%[%s\"",pattern);
4008
156
          status=MagickFalse;
4009
156
          goto cleanup;
4010
156
        }
4011
      /*
4012
        Special Lookup Prefixes %[prefix:...].
4013
      */
4014
10.4k
      if (LocaleNCompare("fx:",pattern,3) == 0)
4015
4.47k
        {
4016
4.47k
          double
4017
4.47k
            value;
4018
4019
4.47k
          FxInfo
4020
4.47k
            *fx_info;
4021
4022
4.47k
          MagickBooleanType
4023
4.47k
            fx_status;
4024
4025
          /*
4026
            FX - value calculator.
4027
          */
4028
4.47k
          fx_info=AcquireFxInfo(property_image,pattern+3,exception);
4029
4.47k
          if (fx_info == (FxInfo *) NULL)
4030
1.61k
            continue;
4031
2.86k
          fx_status=FxEvaluateChannelExpression(fx_info,CompositePixelChannel,
4032
2.86k
            0,0,&value,exception);
4033
2.86k
          fx_info=DestroyFxInfo(fx_info);
4034
2.86k
          if (fx_status != MagickFalse)
4035
1.82k
            {
4036
1.82k
              char
4037
1.82k
                result[MagickPathExtent];
4038
4039
1.82k
              (void) FormatLocaleString(result,MagickPathExtent,"%.*g",
4040
1.82k
                GetMagickPrecision(),(double) value);
4041
1.82k
              AppendString2Text(result);
4042
1.82k
            }
4043
2.86k
          continue;
4044
2.86k
        }
4045
5.99k
      if (LocaleNCompare("hex:",pattern,4) == 0)
4046
1.27k
        {
4047
1.27k
          double
4048
1.27k
            value;
4049
4050
1.27k
          FxInfo
4051
1.27k
            *fx_info;
4052
4053
1.27k
          MagickStatusType
4054
1.27k
            fx_status;
4055
4056
1.27k
          PixelInfo
4057
1.27k
            pixel;
4058
4059
          /*
4060
            Pixel - color value calculator.
4061
          */
4062
1.27k
          GetPixelInfo(property_image,&pixel);
4063
1.27k
          fx_info=AcquireFxInfo(property_image,pattern+4,exception);
4064
1.27k
          if (fx_info == (FxInfo *) NULL)
4065
141
            continue;
4066
1.13k
          fx_status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
4067
1.13k
            &value,exception);
4068
1.13k
          pixel.red=(double) QuantumRange*value;
4069
1.13k
          fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4070
1.13k
            GreenPixelChannel,0,0,&value,exception);
4071
1.13k
          pixel.green=(double) QuantumRange*value;
4072
1.13k
          fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4073
1.13k
            BluePixelChannel,0,0,&value,exception);
4074
1.13k
          pixel.blue=(double) QuantumRange*value;
4075
1.13k
          if (property_image->colorspace == CMYKColorspace)
4076
0
            {
4077
0
              fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4078
0
                BlackPixelChannel,0,0,&value,exception);
4079
0
              pixel.black=(double) QuantumRange*value;
4080
0
            }
4081
1.13k
          fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4082
1.13k
            AlphaPixelChannel,0,0,&value,exception);
4083
1.13k
          pixel.alpha=(double) QuantumRange*value;
4084
1.13k
          fx_info=DestroyFxInfo(fx_info);
4085
1.13k
          if (fx_status != MagickFalse)
4086
756
            {
4087
756
              char
4088
756
                hex[MagickPathExtent];
4089
4090
756
              GetColorTuple(&pixel,MagickTrue,hex);
4091
756
              AppendString2Text(hex+1);
4092
756
            }
4093
1.13k
          continue;
4094
1.13k
        }
4095
4.72k
      if (LocaleNCompare("pixel:",pattern,6) == 0)
4096
141
        {
4097
141
          double
4098
141
            value;
4099
4100
141
          FxInfo
4101
141
            *fx_info;
4102
4103
141
          MagickStatusType
4104
141
            fx_status;
4105
4106
141
          PixelInfo
4107
141
            pixel;
4108
4109
          /*
4110
            Pixel - color value calculator.
4111
          */
4112
141
          GetPixelInfo(property_image,&pixel);
4113
141
          fx_info=AcquireFxInfo(property_image,pattern+6,exception);
4114
141
          if (fx_info == (FxInfo *) NULL)
4115
18
            continue;
4116
123
          fx_status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
4117
123
            &value,exception);
4118
123
          pixel.red=(double) QuantumRange*value;
4119
123
          fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4120
123
            GreenPixelChannel,0,0,&value,exception);
4121
123
          pixel.green=(double) QuantumRange*value;
4122
123
          fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4123
123
            BluePixelChannel,0,0,&value,exception);
4124
123
          pixel.blue=(double) QuantumRange*value;
4125
123
          if (property_image->colorspace == CMYKColorspace)
4126
0
            {
4127
0
              fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4128
0
                BlackPixelChannel,0,0,&value,exception);
4129
0
              pixel.black=(double) QuantumRange*value;
4130
0
            }
4131
123
          fx_status&=(MagickStatusType) FxEvaluateChannelExpression(fx_info,
4132
123
            AlphaPixelChannel,0,0,&value,exception);
4133
123
          pixel.alpha=(double) QuantumRange*value;
4134
123
          fx_info=DestroyFxInfo(fx_info);
4135
123
          if (fx_status != MagickFalse)
4136
119
            {
4137
119
              char
4138
119
                name[MagickPathExtent];
4139
4140
119
              GetColorTuple(&pixel,MagickFalse,name);
4141
119
              string=GetImageArtifact(property_image,"pixel:compliance");
4142
119
              if (string != (char *) NULL)
4143
0
                {
4144
0
                  ComplianceType compliance=(ComplianceType) ParseCommandOption(
4145
0
                    MagickComplianceOptions,MagickFalse,string);
4146
0
                  (void) QueryColorname(property_image,&pixel,compliance,name,
4147
0
                    exception);
4148
0
                }
4149
119
              AppendString2Text(name);
4150
119
            }
4151
123
          continue;
4152
123
        }
4153
4.58k
      if (LocaleNCompare("option:",pattern,7) == 0)
4154
0
        {
4155
          /*
4156
            Option - direct global option lookup (with globbing).
4157
          */
4158
0
          if (IsGlob(pattern+7) != MagickFalse)
4159
0
            {
4160
0
              ResetImageOptionIterator(property_info);
4161
0
              while ((key=GetNextImageOption(property_info)) != (const char *) NULL)
4162
0
                if (GlobExpression(key,pattern+7,MagickTrue) != MagickFalse)
4163
0
                  {
4164
0
                    string=GetImageOption(property_info,key);
4165
0
                    if (string != (const char *) NULL)
4166
0
                      AppendKeyValue2Text(key,string);
4167
0
                  }
4168
0
              continue;
4169
0
            }
4170
0
          string=GetImageOption(property_info,pattern+7);
4171
0
          if (string == (char *) NULL)
4172
0
            goto PropertyLookupFailure;
4173
0
          AppendString2Text(string);
4174
0
          continue;
4175
0
        }
4176
4.58k
      if (LocaleNCompare("artifact:",pattern,9) == 0)
4177
56
        {
4178
          /*
4179
            Artifact - direct image artifact lookup (with glob).
4180
          */
4181
56
          if (IsGlob(pattern+9) != MagickFalse)
4182
46
            {
4183
46
              ResetImageArtifactIterator(property_image);
4184
71
              while ((key=GetNextImageArtifact(property_image)) != (const char *) NULL)
4185
25
                if (GlobExpression(key,pattern+9,MagickTrue) != MagickFalse)
4186
13
                  {
4187
13
                    string=GetImageArtifact(property_image,key);
4188
13
                    if (string != (const char *) NULL)
4189
13
                      AppendKeyValue2Text(key,string);
4190
13
                  }
4191
46
              continue;
4192
46
            }
4193
10
          string=GetImageArtifact(property_image,pattern+9);
4194
10
          if (string == (char *) NULL)
4195
10
            goto PropertyLookupFailure;
4196
0
          AppendString2Text(string);
4197
0
          continue;
4198
0
        }
4199
4.52k
      if (LocaleNCompare("property:",pattern,9) == 0)
4200
30
        {
4201
          /*
4202
            Property - direct image property lookup (with glob).
4203
          */
4204
30
          if (IsGlob(pattern+9) != MagickFalse)
4205
20
            {
4206
20
              ResetImagePropertyIterator(property_image);
4207
30
              while ((key=GetNextImageProperty(property_image)) != (const char *) NULL)
4208
10
                if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
4209
0
                  {
4210
0
                    string=GetImageProperty(property_image,key,exception);
4211
0
                    if (string != (const char *) NULL)
4212
0
                      AppendKeyValue2Text(key,string);
4213
0
                  }
4214
20
              continue;
4215
20
            }
4216
10
          string=GetImageProperty(property_image,pattern+9,exception);
4217
10
          if (string == (char *) NULL)
4218
10
            goto PropertyLookupFailure;
4219
0
          AppendString2Text(string);
4220
0
          continue;
4221
0
        }
4222
      /*
4223
        Properties without special prefix.
4224
      */
4225
4.49k
      string=GetImageProperty(property_image,pattern,exception);
4226
4.49k
      if (string != (const char *) NULL)
4227
0
        {
4228
0
          AppendString2Text(string);
4229
0
          (void) DeleteImageArtifact(property_image,"magick-property");
4230
0
          (void) DeleteImageOption(property_info,"magick-property");
4231
0
          continue;
4232
0
        }
4233
4.49k
      if (IsGlob(pattern) != MagickFalse)
4234
1.35k
        {
4235
1.35k
          ResetImagePropertyIterator(property_image);
4236
2.61k
          while ((key=GetNextImageProperty(property_image)) != (const char *) NULL)
4237
1.26k
            if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
4238
240
              {
4239
240
                string=GetImageProperty(property_image,key,exception);
4240
240
                if (string != (const char *) NULL)
4241
240
                  AppendKeyValue2Text(key,string);
4242
240
              }
4243
1.35k
          continue;
4244
1.35k
        }
4245
      /*
4246
        Known property or attribute.
4247
      */
4248
3.13k
      string=GetMagickProperty(property_info,property_image,pattern,exception);
4249
3.13k
      if (string != (const char *) NULL)
4250
2.04k
        {
4251
2.04k
          AppendString2Text(string);
4252
2.04k
          continue;
4253
2.04k
        }
4254
      /*
4255
        Per-image artifact.
4256
      */
4257
1.09k
      string=GetImageArtifact(property_image,pattern);
4258
1.09k
      if (string != (char *) NULL)
4259
0
        {
4260
0
          AppendString2Text(string);
4261
0
          continue;
4262
0
        }
4263
      /*
4264
        Direct option lookup.
4265
      */
4266
1.09k
      string=GetImageOption(property_info,pattern);
4267
1.09k
      if (string != (char *) NULL)
4268
0
        {
4269
0
          AppendString2Text(string);
4270
0
          continue;
4271
0
        }
4272
4273
1.11k
PropertyLookupFailure:
4274
1.11k
      if (offset >= 64)
4275
0
        {
4276
0
          pattern[61]='.';  /* truncate string for error message */
4277
0
          pattern[62]='.';
4278
0
          pattern[63]='.';
4279
0
          pattern[64]='\0';
4280
0
        }
4281
1.11k
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4282
1.11k
        "UnknownImageProperty","\"%%[%s]\"",pattern);
4283
1.11k
    }
4284
1.11k
  }
4285
10.4k
  *q='\0';
4286
4287
10.5k
cleanup:
4288
10.5k
  if ((property_image != image) && (property_image != (Image *) NULL))
4289
8.04k
    property_image=DestroyImage(property_image);
4290
10.5k
  if ((property_info != image_info) && (property_info != (ImageInfo *) NULL))
4291
0
    property_info=DestroyImageInfo(property_info);
4292
10.5k
  if (status == MagickFalse)
4293
181
    {
4294
181
      if (interpret_text != (char *) NULL)
4295
181
        interpret_text=DestroyString(interpret_text);
4296
181
      return((char *) NULL);
4297
181
    }
4298
10.4k
  return(interpret_text);
4299
10.5k
}
4300

4301
/*
4302
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4303
%                                                                             %
4304
%                                                                             %
4305
%                                                                             %
4306
%   R e m o v e I m a g e P r o p e r t y                                     %
4307
%                                                                             %
4308
%                                                                             %
4309
%                                                                             %
4310
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4311
%
4312
%  RemoveImageProperty() removes a property from the image and returns its
4313
%  value.
4314
%
4315
%  In this case the ConstantString() value returned should be freed by the
4316
%  caller when finished.
4317
%
4318
%  The format of the RemoveImageProperty method is:
4319
%
4320
%      char *RemoveImageProperty(Image *image,const char *property)
4321
%
4322
%  A description of each parameter follows:
4323
%
4324
%    o image: the image.
4325
%
4326
%    o property: the image property.
4327
%
4328
*/
4329
MagickExport char *RemoveImageProperty(Image *image,const char *property)
4330
0
{
4331
0
  char
4332
0
    *value;
4333
4334
0
  assert(image != (Image *) NULL);
4335
0
  assert(image->signature == MagickCoreSignature);
4336
0
  if (IsEventLogging() != MagickFalse)
4337
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4338
0
  if (image->properties == (void *) NULL)
4339
0
    return((char *) NULL);
4340
0
  value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
4341
0
    property);
4342
0
  return(value);
4343
0
}
4344

4345
/*
4346
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4347
%                                                                             %
4348
%                                                                             %
4349
%                                                                             %
4350
%   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                       %
4351
%                                                                             %
4352
%                                                                             %
4353
%                                                                             %
4354
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4355
%
4356
%  ResetImagePropertyIterator() resets the image properties iterator.  Use it
4357
%  in conjunction with GetNextImageProperty() to iterate over all the values
4358
%  associated with an image property.
4359
%
4360
%  The format of the ResetImagePropertyIterator method is:
4361
%
4362
%      void ResetImagePropertyIterator(const Image *image)
4363
%
4364
%  A description of each parameter follows:
4365
%
4366
%    o image: the image.
4367
%
4368
*/
4369
MagickExport void ResetImagePropertyIterator(const Image *image)
4370
8.52k
{
4371
8.52k
  assert(image != (Image *) NULL);
4372
8.52k
  assert(image->signature == MagickCoreSignature);
4373
8.52k
  if (IsEventLogging() != MagickFalse)
4374
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4375
8.52k
  if (image->properties == (void *) NULL)
4376
104
    return;
4377
8.42k
  ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
4378
8.42k
}
4379

4380
/*
4381
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4382
%                                                                             %
4383
%                                                                             %
4384
%                                                                             %
4385
%   S e t I m a g e P r o p e r t y                                           %
4386
%                                                                             %
4387
%                                                                             %
4388
%                                                                             %
4389
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4390
%
4391
%  SetImageProperty() saves the given string value either to specific known
4392
%  attribute or to a freeform property string.
4393
%
4394
%  Attempting to set a property that is normally calculated will produce
4395
%  an exception.
4396
%
4397
%  The format of the SetImageProperty method is:
4398
%
4399
%      MagickBooleanType SetImageProperty(Image *image,const char *property,
4400
%        const char *value,ExceptionInfo *exception)
4401
%
4402
%  A description of each parameter follows:
4403
%
4404
%    o image: the image.
4405
%
4406
%    o property: the image property.
4407
%
4408
%    o values: the image property values.
4409
%
4410
%    o exception: return any errors or warnings in this structure.
4411
%
4412
*/
4413
MagickExport MagickBooleanType SetImageProperty(Image *image,
4414
  const char *property,const char *value,ExceptionInfo *exception)
4415
16.0M
{
4416
16.0M
  MagickBooleanType
4417
16.0M
    status;
4418
4419
16.0M
  MagickStatusType
4420
16.0M
    flags;
4421
4422
16.0M
  size_t
4423
16.0M
    property_length;
4424
4425
16.0M
  assert(image != (Image *) NULL);
4426
16.0M
  assert(image->signature == MagickCoreSignature);
4427
16.0M
  if (IsEventLogging() != MagickFalse)
4428
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4429
16.0M
  if (image->properties == (void *) NULL)
4430
429k
    image->properties=NewSplayTree(CompareSplayTreeString,
4431
429k
      RelinquishMagickMemory,RelinquishMagickMemory);  /* create splay-tree */
4432
16.0M
  if (value == (const char *) NULL)
4433
0
    return(DeleteImageProperty(image,property));  /* delete if NULL */
4434
16.0M
  if (strlen(property) <= 1)
4435
78.1k
    {
4436
      /*
4437
        Do not 'set' single letter properties - read only shorthand.
4438
      */
4439
78.1k
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4440
78.1k
        "SetReadOnlyProperty","`%s'",property);
4441
78.1k
      return(MagickFalse);
4442
78.1k
    }
4443
15.9M
  property_length=strlen(property);
4444
15.9M
  if ((property_length > 2) && (*(property+(property_length-2)) == ':') &&
4445
34.5k
      (*(property+(property_length-1)) == '*'))
4446
11.6k
    {
4447
11.6k
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4448
11.6k
        "SetReadOnlyProperty","`%s'",property);
4449
11.6k
      return(MagickFalse);
4450
11.6k
    }
4451
  /*
4452
    FUTURE: binary chars or quotes in key should produce a error
4453
    Set attributes with known names or special prefixes
4454
    return result is found, or break to set a free form property
4455
  */
4456
15.9M
  status=MagickTrue;
4457
15.9M
  switch (*property)
4458
15.9M
  {
4459
#if 0  /* Percent escape's sets values with this prefix: for later use
4460
          Throwing an exception causes this setting to fail */
4461
    case '8':
4462
    {
4463
      if (LocaleNCompare("8bim:",property,5) == 0)
4464
        {
4465
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
4466
            "SetReadOnlyProperty","`%s'",property);
4467
          return(MagickFalse);
4468
        }
4469
      break;
4470
    }
4471
#endif
4472
5.44k
    case 'B':
4473
151k
    case 'b':
4474
151k
    {
4475
151k
      if (LocaleCompare("background",property) == 0)
4476
33.3k
        {
4477
33.3k
          (void) QueryColorCompliance(value,AllCompliance,
4478
33.3k
            &image->background_color,exception);
4479
          /* check for FUTURE: value exception?? */
4480
          /* also add user input to splay tree */
4481
33.3k
        }
4482
151k
      break; /* not an attribute, add as a property */
4483
5.44k
    }
4484
6.97k
    case 'C':
4485
119k
    case 'c':
4486
119k
    {
4487
119k
      if (LocaleCompare("channels",property) == 0)
4488
431
        {
4489
431
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4490
431
            "SetReadOnlyProperty","`%s'",property);
4491
431
          return(MagickFalse);
4492
431
        }
4493
118k
      if (LocaleCompare("colorspace",property) == 0)
4494
0
        {
4495
0
          ssize_t
4496
0
            colorspace;
4497
4498
0
          colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
4499
0
            value);
4500
0
          if (colorspace < 0)
4501
0
            return(MagickFalse); /* FUTURE: value exception?? */
4502
0
          return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
4503
0
        }
4504
118k
      if (LocaleCompare("compose",property) == 0)
4505
1.38k
        {
4506
1.38k
          ssize_t
4507
1.38k
            compose;
4508
4509
1.38k
          compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
4510
1.38k
          if (compose < 0)
4511
689
            return(MagickFalse); /* FUTURE: value exception?? */
4512
696
          image->compose=(CompositeOperator) compose;
4513
696
          return(MagickTrue);
4514
1.38k
        }
4515
117k
      if (LocaleCompare("compress",property) == 0)
4516
12.0k
        {
4517
12.0k
          ssize_t
4518
12.0k
            compression;
4519
4520
12.0k
          compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
4521
12.0k
            value);
4522
12.0k
          if (compression < 0)
4523
2.18k
            return(MagickFalse); /* FUTURE: value exception?? */
4524
9.86k
          image->compression=(CompressionType) compression;
4525
9.86k
          return(MagickTrue);
4526
12.0k
        }
4527
105k
      break; /* not an attribute, add as a property */
4528
117k
    }
4529
105k
    case 'D':
4530
970k
    case 'd':
4531
970k
    {
4532
970k
      if (LocaleCompare("delay",property) == 0)
4533
6.92k
        {
4534
6.92k
          GeometryInfo
4535
6.92k
            geometry_info;
4536
4537
6.92k
          flags=ParseGeometry(value,&geometry_info);
4538
6.92k
          if ((flags & GreaterValue) != 0)
4539
958
            {
4540
958
              if (image->delay > (size_t) floor(geometry_info.rho+0.5))
4541
368
                image->delay=(size_t) floor(geometry_info.rho+0.5);
4542
958
            }
4543
5.96k
          else
4544
5.96k
            if ((flags & LessValue) != 0)
4545
1.80k
              {
4546
1.80k
                if ((double) image->delay < floor(geometry_info.rho+0.5))
4547
983
                  image->delay=(size_t) CastDoubleToSsizeT(floor(
4548
983
                    geometry_info.sigma+0.5));
4549
1.80k
              }
4550
4.16k
            else
4551
4.16k
              image->delay=(size_t) floor(geometry_info.rho+0.5);
4552
6.92k
          if ((flags & SigmaValue) != 0)
4553
1.90k
            image->ticks_per_second=CastDoubleToSsizeT(floor(
4554
1.90k
              geometry_info.sigma+0.5));
4555
6.92k
          return(MagickTrue);
4556
6.92k
        }
4557
963k
      if (LocaleCompare("delay_units",property) == 0)
4558
489
        {
4559
489
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4560
489
            "SetReadOnlyProperty","`%s'",property);
4561
489
          return(MagickFalse);
4562
489
        }
4563
962k
      if (LocaleCompare("density",property) == 0)
4564
48.4k
        {
4565
48.4k
          GeometryInfo
4566
48.4k
            geometry_info;
4567
4568
48.4k
          flags=ParseGeometry(value,&geometry_info);
4569
48.4k
          if ((flags & RhoValue) != 0)
4570
11.9k
            image->resolution.x=geometry_info.rho;
4571
48.4k
          image->resolution.y=image->resolution.x;
4572
48.4k
          if ((flags & SigmaValue) != 0)
4573
12.4k
            image->resolution.y=geometry_info.sigma;
4574
48.4k
          return(MagickTrue);
4575
48.4k
        }
4576
914k
      if (LocaleCompare("depth",property) == 0)
4577
0
        {
4578
0
          image->depth=StringToUnsignedLong(value);
4579
0
          return(MagickTrue);
4580
0
        }
4581
914k
      if (LocaleCompare("dispose",property) == 0)
4582
0
        {
4583
0
          ssize_t
4584
0
            dispose;
4585
4586
0
          dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
4587
0
          if (dispose < 0)
4588
0
            return(MagickFalse); /* FUTURE: value exception?? */
4589
0
          image->dispose=(DisposeType) dispose;
4590
0
          return(MagickTrue);
4591
0
        }
4592
914k
      break; /* not an attribute, add as a property */
4593
914k
    }
4594
#if 0  /* Percent escape's sets values with this prefix: for later use
4595
          Throwing an exception causes this setting to fail */
4596
    case 'E':
4597
    case 'e':
4598
    {
4599
      if (LocaleNCompare("exif:",property,5) == 0)
4600
        {
4601
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4602
            "SetReadOnlyProperty","`%s'",property);
4603
          return(MagickFalse);
4604
        }
4605
      break; /* not an attribute, add as a property */
4606
    }
4607
    case 'F':
4608
    case 'f':
4609
    {
4610
      if (LocaleNCompare("fx:",property,3) == 0)
4611
        {
4612
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4613
            "SetReadOnlyProperty","`%s'",property);
4614
          return(MagickFalse);
4615
        }
4616
      break; /* not an attribute, add as a property */
4617
    }
4618
#endif
4619
914k
    case 'G':
4620
42.4k
    case 'g':
4621
42.4k
    {
4622
42.4k
      if (LocaleCompare("gamma",property) == 0)
4623
1.55k
        {
4624
1.55k
          image->gamma=StringToDouble(value,(char **) NULL);
4625
1.55k
          return(MagickTrue);
4626
1.55k
        }
4627
40.8k
      if (LocaleCompare("gravity",property) == 0)
4628
579
        {
4629
579
          ssize_t
4630
579
            gravity;
4631
4632
579
          gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
4633
579
          if (gravity < 0)
4634
385
            return(MagickFalse); /* FUTURE: value exception?? */
4635
194
          image->gravity=(GravityType) gravity;
4636
194
          return(MagickTrue);
4637
579
        }
4638
40.3k
      break; /* not an attribute, add as a property */
4639
40.8k
    }
4640
40.3k
    case 'H':
4641
29.2k
    case 'h':
4642
29.2k
    {
4643
29.2k
      if (LocaleCompare("height",property) == 0)
4644
766
        {
4645
766
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4646
766
            "SetReadOnlyProperty","`%s'",property);
4647
766
          return(MagickFalse);
4648
766
        }
4649
28.4k
      break; /* not an attribute, add as a property */
4650
29.2k
    }
4651
28.4k
    case 'I':
4652
36.2k
    case 'i':
4653
36.2k
    {
4654
36.2k
      if (LocaleCompare("intensity",property) == 0)
4655
1.39k
        {
4656
1.39k
          ssize_t
4657
1.39k
            intensity;
4658
4659
1.39k
          intensity=ParseCommandOption(MagickIntensityOptions,MagickFalse,
4660
1.39k
            value);
4661
1.39k
          if (intensity < 0)
4662
1.39k
            return(MagickFalse);
4663
0
          image->intensity=(PixelIntensityMethod) intensity;
4664
0
          return(MagickTrue);
4665
1.39k
        }
4666
34.8k
      if (LocaleCompare("intent",property) == 0)
4667
2.72k
        {
4668
2.72k
          ssize_t
4669
2.72k
            rendering_intent;
4670
4671
2.72k
          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
4672
2.72k
            value);
4673
2.72k
          if (rendering_intent < 0)
4674
1.46k
            return(MagickFalse); /* FUTURE: value exception?? */
4675
1.26k
          image->rendering_intent=(RenderingIntent) rendering_intent;
4676
1.26k
          return(MagickTrue);
4677
2.72k
        }
4678
32.1k
      if (LocaleCompare("interpolate",property) == 0)
4679
793
        {
4680
793
          ssize_t
4681
793
            interpolate;
4682
4683
793
          interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
4684
793
            value);
4685
793
          if (interpolate < 0)
4686
401
            return(MagickFalse); /* FUTURE: value exception?? */
4687
392
          image->interpolate=(PixelInterpolateMethod) interpolate;
4688
392
          return(MagickTrue);
4689
793
        }
4690
#if 0  /* Percent escape's sets values with this prefix: for later use
4691
          Throwing an exception causes this setting to fail */
4692
      if (LocaleNCompare("iptc:",property,5) == 0)
4693
        {
4694
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4695
            "SetReadOnlyProperty","`%s'",property);
4696
          return(MagickFalse);
4697
        }
4698
#endif
4699
31.3k
      break; /* not an attribute, add as a property */
4700
32.1k
    }
4701
31.3k
    case 'K':
4702
27.7k
    case 'k':
4703
27.7k
      if (LocaleCompare("kurtosis",property) == 0)
4704
194
        {
4705
194
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4706
194
            "SetReadOnlyProperty","`%s'",property);
4707
194
          return(MagickFalse);
4708
194
        }
4709
27.5k
      break; /* not an attribute, add as a property */
4710
27.5k
    case 'L':
4711
25.0k
    case 'l':
4712
25.0k
    {
4713
25.0k
      if (LocaleCompare("loop",property) == 0)
4714
843
        {
4715
843
          image->iterations=StringToUnsignedLong(value);
4716
843
          return(MagickTrue);
4717
843
        }
4718
24.2k
      break; /* not an attribute, add as a property */
4719
25.0k
    }
4720
24.2k
    case 'M':
4721
34.0k
    case 'm':
4722
34.0k
      if ((LocaleCompare("magick",property) == 0) ||
4723
33.1k
          (LocaleCompare("max",property) == 0) ||
4724
30.7k
          (LocaleCompare("mean",property) == 0) ||
4725
28.6k
          (LocaleCompare("min",property) == 0) ||
4726
26.4k
          (LocaleCompare("min",property) == 0))
4727
7.57k
        {
4728
7.57k
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4729
7.57k
             "SetReadOnlyProperty","`%s'",property);
4730
7.57k
          return(MagickFalse);
4731
7.57k
        }
4732
26.4k
      break; /* not an attribute, add as a property */
4733
26.4k
    case 'O':
4734
13.9k
    case 'o':
4735
13.9k
      if (LocaleCompare("opaque",property) == 0)
4736
1.05k
        {
4737
1.05k
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4738
1.05k
            "SetReadOnlyProperty","`%s'",property);
4739
1.05k
          return(MagickFalse);
4740
1.05k
        }
4741
12.8k
      break; /* not an attribute, add as a property */
4742
13.0k
    case 'P':
4743
13.5M
    case 'p':
4744
13.5M
    {
4745
13.5M
      if (LocaleCompare("page",property) == 0)
4746
35.3k
        {
4747
35.3k
          char
4748
35.3k
            *geometry;
4749
4750
35.3k
          geometry=GetPageGeometry(value);
4751
35.3k
          flags=ParseAbsoluteGeometry(geometry,&image->page);
4752
35.3k
          geometry=DestroyString(geometry);
4753
35.3k
          return(MagickTrue);
4754
35.3k
        }
4755
#if 0  /* Percent escape's sets values with this prefix: for later use
4756
          Throwing an exception causes this setting to fail */
4757
      if (LocaleNCompare("pixel:",property,6) == 0)
4758
        {
4759
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4760
            "SetReadOnlyProperty","`%s'",property);
4761
          return(MagickFalse);
4762
        }
4763
#endif
4764
13.5M
      break; /* not an attribute, add as a property */
4765
13.5M
    }
4766
13.5M
    case 'R':
4767
20.5k
    case 'r':
4768
20.5k
    {
4769
20.5k
      if (LocaleCompare("rendering-intent",property) == 0)
4770
0
        {
4771
0
          ssize_t
4772
0
            rendering_intent;
4773
4774
0
          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
4775
0
            value);
4776
0
          if (rendering_intent < 0)
4777
0
            return(MagickFalse); /* FUTURE: value exception?? */
4778
0
          image->rendering_intent=(RenderingIntent) rendering_intent;
4779
0
          return(MagickTrue);
4780
0
        }
4781
20.5k
      break; /* not an attribute, add as a property */
4782
20.5k
    }
4783
20.5k
    case 'S':
4784
20.0k
    case 's':
4785
20.0k
      if ((LocaleCompare("size",property) == 0) ||
4786
18.2k
          (LocaleCompare("skewness",property) == 0) ||
4787
17.8k
          (LocaleCompare("scenes",property) == 0) ||
4788
16.9k
          (LocaleCompare("standard-deviation",property) == 0))
4789
3.44k
        {
4790
3.44k
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4791
3.44k
            "SetReadOnlyProperty","`%s'",property);
4792
3.44k
          return(MagickFalse);
4793
3.44k
        }
4794
16.5k
      break; /* not an attribute, add as a property */
4795
27.1k
    case 'T':
4796
277k
    case 't':
4797
277k
    {
4798
277k
      if (LocaleCompare("tile-offset",property) == 0)
4799
1.77k
        {
4800
1.77k
          char
4801
1.77k
            *geometry;
4802
4803
1.77k
          geometry=GetPageGeometry(value);
4804
1.77k
          flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
4805
1.77k
          geometry=DestroyString(geometry);
4806
1.77k
          return(MagickTrue);
4807
1.77k
        }
4808
275k
      if (LocaleCompare("type",property) == 0)
4809
16.8k
        {
4810
16.8k
          ssize_t
4811
16.8k
            type;
4812
4813
16.8k
          type=ParseCommandOption(MagickTypeOptions,MagickFalse,value);
4814
16.8k
          if (type < 0)
4815
16.4k
            return(MagickFalse);
4816
452
          image->type=(ImageType) type;
4817
452
          return(MagickTrue);
4818
16.8k
        }
4819
258k
      break; /* not an attribute, add as a property */
4820
275k
    }
4821
258k
    case 'U':
4822
23.7k
    case 'u':
4823
23.7k
    {
4824
23.7k
      if (LocaleCompare("units",property) == 0)
4825
7.67k
        {
4826
7.67k
          ssize_t
4827
7.67k
            units;
4828
4829
7.67k
          units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
4830
7.67k
          if (units < 0)
4831
2.05k
            return(MagickFalse); /* FUTURE: value exception?? */
4832
5.62k
          image->units=(ResolutionType) units;
4833
5.62k
          return(MagickTrue);
4834
7.67k
        }
4835
16.0k
      break; /* not an attribute, add as a property */
4836
23.7k
    }
4837
16.0k
    case 'V':
4838
48.2k
    case 'v':
4839
48.2k
    {
4840
48.2k
      if (LocaleCompare("version",property) == 0)
4841
609
        {
4842
609
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4843
609
            "SetReadOnlyProperty","`%s'",property);
4844
609
          return(MagickFalse);
4845
609
        }
4846
47.6k
      break; /* not an attribute, add as a property */
4847
48.2k
    }
4848
47.6k
    case 'W':
4849
10.2k
    case 'w':
4850
10.2k
    {
4851
10.2k
      if (LocaleCompare("width",property) == 0)
4852
2.28k
        {
4853
2.28k
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4854
2.28k
            "SetReadOnlyProperty","`%s'",property);
4855
2.28k
          return(MagickFalse);
4856
2.28k
        }
4857
7.92k
      break; /* not an attribute, add as a property */
4858
10.2k
    }
4859
#if 0  /* Percent escape's sets values with this prefix: for later use
4860
          Throwing an exception causes this setting to fail */
4861
    case 'X':
4862
    case 'x':
4863
    {
4864
      if (LocaleNCompare("xmp:",property,4) == 0)
4865
        {
4866
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4867
            "SetReadOnlyProperty","`%s'",property);
4868
          return(MagickFalse);
4869
        }
4870
      break; /* not an attribute, add as a property */
4871
    }
4872
#endif
4873
15.9M
  }
4874
  /* Default: not an attribute, add as a property */
4875
15.8M
  status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
4876
15.8M
    ConstantString(property),ConstantString(value));
4877
  /* FUTURE: error if status is bad? */
4878
15.8M
  return(status);
4879
15.9M
}