Coverage Report

Created: 2025-11-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/attribute.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2025 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
%
5
% This program is covered by multiple licenses, which are described in
6
% Copyright.txt. You should have received a copy of Copyright.txt with this
7
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8
%
9
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10
%                                                                             %
11
%                                                                             %
12
%                                                                             %
13
%        AAA   TTTTT  TTTTT  RRRR   IIIII  BBBB   U   U  TTTTT  EEEEE         %
14
%       A   A    T      T    R   R    I    B   B  U   U    T    E             %
15
%       AAAAA    T      T    RRRR     I    BBBB   U   U    T    EEE           %
16
%       A   A    T      T    R R      I    B   B  U   U    T    E             %
17
%       A   A    T      T    R  R   IIIII  BBBB    UUU     T    EEEEE         %
18
%                                                                             %
19
%                                                                             %
20
%              Methods to Get/Set/Destroy Image Text Attributes               %
21
%                                                                             %
22
%                                                                             %
23
%                             Software Design                                 %
24
%                               John Cristy                                   %
25
%                              February 2000                                  %
26
%                                                                             %
27
%                                                                             %
28
%                                                                             %
29
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30
%
31
%  The Attributes methods gets, sets, or destroys attributes associated
32
%  with a particular image (e.g. comments, copyright, author, etc).
33
%
34
%
35
*/
36

37
/*
38
  Include declarations.
39
*/
40
#include "magick/studio.h"
41
#include "magick/attribute.h"
42
#include "magick/blob.h"
43
#include "magick/log.h"
44
#include "magick/profile.h"
45
#include "magick/render.h"
46
#include "magick/tempfile.h"
47
#include "magick/utility.h"
48

49
/*
50
  Forward declarations.
51
*/
52
static void DestroyImageAttribute(ImageAttribute *attribute);
53

54
/*
55
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56
%                                                                             %
57
%                                                                             %
58
%                                                                             %
59
%   C l o n e I m a g e A t t r i b u t e s                                   %
60
%                                                                             %
61
%                                                                             %
62
%                                                                             %
63
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64
%
65
%  CloneImageAttributes() copies the text attributes from one image to another.
66
%  Any text attributes in the destination image are preserved.
67
%  CloneImageAttributes returns MagickPass if all of the attribututes are
68
%  successfully cloned or MagickFail if there is a memory allocation error.
69
%
70
%  The format of the CloneImageAttributes method is:
71
%
72
%      MagickPassFail CloneImageAttributes(Image* clone_image,
73
%                                          const Image* original_image)
74
%
75
%  A description of each parameter follows:
76
%
77
%    o clone_image: The destination image.
78
%
79
%    o original_image: The source image.
80
%
81
%
82
*/
83
MagickExport MagickPassFail
84
CloneImageAttributes(Image* clone_image,
85
                     const Image* original_image)
86
440k
{
87
440k
  MagickPassFail
88
440k
    status;
89
90
440k
  ImageAttribute
91
440k
    *cloned_attribute,
92
440k
    *cloned_attributes;
93
94
440k
  const ImageAttribute
95
440k
    *attribute;
96
97
440k
  status = MagickPass;
98
99
  /*
100
    Search for tail of list (if any)
101
  */
102
440k
  if ((cloned_attributes=clone_image->attributes))
103
0
    {
104
0
      for( ;
105
0
          cloned_attributes->next != (ImageAttribute *) NULL;
106
0
          cloned_attributes=cloned_attributes->next);
107
0
    }
108
109
440k
  attribute=GetImageAttribute(original_image,(char *) NULL);
110
1.23M
  for ( ; attribute != (const ImageAttribute *) NULL;
111
796k
        attribute=attribute->next)
112
796k
    {
113
      /*
114
        Construct AttributeInfo to append.
115
      */
116
796k
      cloned_attribute=MagickAllocateMemory(ImageAttribute *,
117
796k
                                            sizeof(ImageAttribute));
118
796k
      if (cloned_attribute == (ImageAttribute *) NULL)
119
0
        {
120
0
          status = MagickFail;
121
0
          break;
122
0
        }
123
796k
      cloned_attribute->key=AcquireString(attribute->key);
124
796k
      cloned_attribute->length=attribute->length;
125
796k
      cloned_attribute->value=
126
796k
        MagickAllocateMemory(char *,cloned_attribute->length+1);
127
796k
      cloned_attribute->previous=(ImageAttribute *) NULL;
128
796k
      cloned_attribute->next=(ImageAttribute *) NULL;
129
796k
      if ((cloned_attribute->value == (char *) NULL) ||
130
796k
          (cloned_attribute->key == (char *) NULL))
131
0
        {
132
0
          DestroyImageAttribute(cloned_attribute);
133
0
          status = MagickFail;
134
0
          break;
135
0
        }
136
796k
      (void) strlcpy(cloned_attribute->value,attribute->value,cloned_attribute->length+1);
137
138
796k
      if (cloned_attributes == (ImageAttribute *) NULL)
139
225k
        {
140
          /*
141
            Start list
142
          */
143
225k
          cloned_attributes=cloned_attribute;
144
225k
          clone_image->attributes=cloned_attributes;
145
225k
        }
146
570k
      else
147
570k
        {
148
          /*
149
            Append to list
150
          */
151
570k
          cloned_attributes->next=cloned_attribute;
152
570k
          cloned_attribute->previous=cloned_attributes;
153
570k
          cloned_attributes=cloned_attribute;
154
570k
        }
155
796k
    }
156
157
440k
  return status;
158
440k
}
159

160
/*
161
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162
%                                                                             %
163
%                                                                             %
164
%                                                                             %
165
%   D e s t r o y I m a g e A t t r i b u t e s                               %
166
%                                                                             %
167
%                                                                             %
168
%                                                                             %
169
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
170
%
171
%  DestroyImageAttributes() deallocates memory associated with the image
172
%  attribute list.
173
%
174
%  The format of the DestroyImageAttributes method is:
175
%
176
%      DestroyImageAttributes(Image *image)
177
%
178
%  A description of each parameter follows:
179
%
180
%    o image: The image.
181
%
182
%
183
*/
184
static void
185
DestroyImageAttribute(ImageAttribute *attribute)
186
5.56M
{
187
5.56M
  if (attribute == (ImageAttribute *) NULL)
188
0
    return;
189
5.56M
  MagickFreeMemory(attribute->value);
190
5.56M
  MagickFreeMemory(attribute->key);
191
5.56M
  (void) memset(attribute,0xbf,sizeof(ImageAttribute));
192
5.56M
  MagickFreeMemory(attribute);
193
5.56M
}
194
MagickExport void DestroyImageAttributes(Image *image)
195
14.7M
{
196
14.7M
  ImageAttribute
197
14.7M
    *attribute;
198
199
14.7M
  register ImageAttribute
200
14.7M
    *p;
201
202
14.7M
  assert(image != (Image *) NULL);
203
14.7M
  assert(image->signature == MagickSignature);
204
17.6M
  for (p=image->attributes; p != (ImageAttribute *) NULL; )
205
2.87M
    {
206
2.87M
      attribute=p;
207
2.87M
      p=p->next;
208
2.87M
      DestroyImageAttribute(attribute);
209
2.87M
    }
210
14.7M
  image->attributes=(ImageAttribute *) NULL;
211
14.7M
}
212

213
/*
214
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215
%                                                                             %
216
%                                                                             %
217
%                                                                             %
218
%   G e t I m a g e A t t r i b u t e                                         %
219
%                                                                             %
220
%                                                                             %
221
%                                                                             %
222
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223
%
224
%  GetImageAttribute() searches the list of image attributes and returns
225
%  a pointer to the attribute if it exists otherwise NULL.
226
%
227
%  The format of the GetImageAttribute method is:
228
%
229
%      const ImageAttribute *GetImageAttribute(const Image *image,
230
%        const char *key)
231
%
232
%  A description of each parameter follows:
233
%
234
%    o image: The image.
235
%
236
%    o key:  These character strings are the name of an image attribute to
237
%      return.
238
%
239
%
240
*/
241
242
static unsigned int
243
GenerateIPTCAttribute(Image *image,const char *key)
244
0
{
245
#if 0
246
  static const struct
247
  {
248
    char *name;
249
    int   dataset;
250
    int   record;
251
  }
252
#define IPTC_ATTRIBUTE(dataset,record,name) {name,dataset,record}
253
  IPTCAttributes[] =
254
  {
255
    IPTC_ATTRIBUTE(2,5,"Image Name"),
256
    IPTC_ATTRIBUTE(2,7,"Edit Status"),
257
    IPTC_ATTRIBUTE(2,10,"Priority"),
258
    IPTC_ATTRIBUTE(2,15,"Category"),
259
    IPTC_ATTRIBUTE(2,20,"Supplemental Category"),
260
    IPTC_ATTRIBUTE(2,22,"Fixture Identifier"),
261
    IPTC_ATTRIBUTE(2,25,"Keyword"),
262
    IPTC_ATTRIBUTE(2,30,"Release Date"),
263
    IPTC_ATTRIBUTE(2,35,"Release Time"),
264
    IPTC_ATTRIBUTE(2,40,"Special Instructions"),
265
    IPTC_ATTRIBUTE(2,45,"Reference Service"),
266
    IPTC_ATTRIBUTE(2,47,"Reference Date"),
267
    IPTC_ATTRIBUTE(2,50,"Reference Number"),
268
    IPTC_ATTRIBUTE(2,55,"Created Date"),
269
    IPTC_ATTRIBUTE(2,60,"Created Time"),
270
    IPTC_ATTRIBUTE(2,65,"Originating Program"),
271
    IPTC_ATTRIBUTE(2,70,"Program Version"),
272
    IPTC_ATTRIBUTE(2,75,"Object Cycle"),
273
    IPTC_ATTRIBUTE(2,80,"Byline"),
274
    IPTC_ATTRIBUTE(2,85,"Byline Title"),
275
    IPTC_ATTRIBUTE(2,90,"City"),
276
    IPTC_ATTRIBUTE(2,95,"Province State"),
277
    IPTC_ATTRIBUTE(2,100,"Country Code"),
278
    IPTC_ATTRIBUTE(2,101,"Country"),
279
    IPTC_ATTRIBUTE(2,103,"Original Transmission Reference"),
280
    IPTC_ATTRIBUTE(2,105,"Headline"),
281
    IPTC_ATTRIBUTE(2,110,"Credit"),
282
    IPTC_ATTRIBUTE(2,115,"Source"),
283
    IPTC_ATTRIBUTE(2,116,"Copyright String"),
284
    IPTC_ATTRIBUTE(2,120,"Caption"),
285
    IPTC_ATTRIBUTE(2,121,"Local Caption"),
286
    IPTC_ATTRIBUTE(2,122,"Caption Writer"),
287
    IPTC_ATTRIBUTE(2,200,"Custom Field 1"),
288
    IPTC_ATTRIBUTE(2,201,"Custom Field 2"),
289
    IPTC_ATTRIBUTE(2,202,"Custom Field 3"),
290
    IPTC_ATTRIBUTE(2,203,"Custom Field 4"),
291
    IPTC_ATTRIBUTE(2,204,"Custom Field 5"),
292
    IPTC_ATTRIBUTE(2,205,"Custom Field 6"),
293
    IPTC_ATTRIBUTE(2,206,"Custom Field 7"),
294
    IPTC_ATTRIBUTE(2,207,"Custom Field 8"),
295
    IPTC_ATTRIBUTE(2,208,"Custom Field 9"),
296
    IPTC_ATTRIBUTE(2,209,"Custom Field 10"),
297
    IPTC_ATTRIBUTE(2,210,"Custom Field 11"),
298
    IPTC_ATTRIBUTE(2,211,"Custom Field 12"),
299
    IPTC_ATTRIBUTE(2,212,"Custom Field 13"),
300
    IPTC_ATTRIBUTE(2,213,"Custom Field 14"),
301
    IPTC_ATTRIBUTE(2,214,"Custom Field 15"),
302
    IPTC_ATTRIBUTE(2,215,"Custom Field 16"),
303
    IPTC_ATTRIBUTE(2,216,"Custom Field 17"),
304
    IPTC_ATTRIBUTE(2,217,"Custom Field 18"),
305
    IPTC_ATTRIBUTE(2,218,"Custom Field 19"),
306
    IPTC_ATTRIBUTE(2,219,"Custom Field 20")
307
  };
308
#endif
309
310
0
  char
311
0
    *attribute;
312
313
0
  int
314
0
    count,
315
0
    dataset,
316
0
    record;
317
318
0
  register long
319
0
    i;
320
321
0
  size_t
322
0
    length;
323
324
0
  const unsigned char
325
0
    *profile;
326
327
0
  size_t
328
0
    profile_length;
329
330
0
  if((profile=GetImageProfile(image,"IPTC",&profile_length)) == 0)
331
0
    return(False);
332
0
  count=sscanf(key,"IPTC:%d:%d",&dataset,&record);
333
0
  if (count != 2)
334
0
    return(False);
335
0
  for (i=0; i < (long) profile_length; i++)
336
0
    {
337
0
      if (profile[i] != 0x1cU)
338
0
        continue;
339
0
      if (profile[i+1] != dataset)
340
0
        {
341
          /* fprintf(stderr,"Skipping dataset %d\n",profile[i+1]); */
342
0
          continue;
343
0
        }
344
0
      if (profile[i+2] != record)
345
0
        {
346
          /* fprintf(stderr,"Skipping record %d\n",profile[i+2]); */
347
0
          continue;
348
0
        }
349
0
      length=(size_t) profile[i+3] << 8;
350
0
      length|=(size_t) profile[i+4];
351
0
      attribute=MagickAllocateMemory(char *,length+1);
352
0
      if (attribute == (char *) NULL)
353
0
        continue;
354
0
      (void) strlcpy(attribute,(char *) profile+i+5,length+1);
355
0
      (void) SetImageAttribute(image,key,(const char *) attribute);
356
0
      MagickFreeMemory(attribute);
357
0
      break;
358
0
    }
359
0
  return(i < (long) profile_length);
360
0
}
361
362
static unsigned char
363
ReadByte(unsigned char **p,size_t *length)
364
9.78M
{
365
9.78M
  unsigned char
366
9.78M
    c;
367
368
9.78M
  if (*length < 1)
369
1.06k
    return(0xff);
370
9.78M
  c=(*(*p)++);
371
9.78M
  (*length)--;
372
9.78M
  return(c);
373
9.78M
}
374
375
static magick_int32_t
376
ReadMSBLong(unsigned char **p,size_t *length)
377
0
{
378
0
  int
379
0
    c;
380
381
0
  union
382
0
  {
383
0
    magick_uint32_t u;
384
0
    magick_int32_t s;
385
0
  } value;
386
387
0
  register unsigned int
388
0
    i;
389
390
0
  unsigned char
391
0
    buffer[4];
392
393
0
  if (*length < 4)
394
0
    return(-1);
395
0
  for (i=0; i < 4; i++)
396
0
  {
397
0
    c=(*(*p)++);
398
0
    (*length)--;
399
0
    buffer[i]=(unsigned char) c;
400
0
  }
401
0
  value.u=(magick_uint32_t) (buffer[0] & 0xff) << 24;
402
0
  value.u|=(magick_uint32_t) buffer[1] << 16;
403
0
  value.u|=(magick_uint32_t) buffer[2] << 8;
404
0
  value.u|=(magick_uint32_t) buffer[3];
405
0
  return(value.s);
406
0
}
407
408
static magick_int32_t
409
ReadMSBShort(unsigned char **p,size_t *length)
410
0
{
411
0
  int
412
0
    c;
413
414
0
  union
415
0
  {
416
0
    magick_uint32_t u;
417
0
    magick_int32_t s;
418
0
  } value;
419
420
0
  register unsigned int
421
0
    i;
422
423
0
  unsigned char
424
0
    buffer[2];
425
426
0
  if (*length < 2)
427
0
    return(-1);
428
0
  for (i=0; i < 2; i++)
429
0
  {
430
0
    c=(*(*p)++);
431
0
    (*length)--;
432
0
    buffer[i]=(unsigned char) c;
433
0
  }
434
0
  value.u=(magick_uint32_t) (buffer[0] & 0xff) << 8;
435
0
  value.u|=(magick_uint32_t) buffer[1];
436
0
  return(value.s);
437
0
}
438
439
/*
440
  Advance 'blob' by 'amount' or the amount remaining in 'length'.
441
  Decrement 'length' by 'amount' or to zero.
442
*/
443
static inline size_t AdvanceBlob(const size_t amount, size_t length, unsigned char **blob)
444
0
{
445
0
  if (length > amount)
446
0
    {
447
0
      *blob+=amount;
448
0
      length-=amount;
449
0
    }
450
0
  else
451
0
    {
452
0
      *blob+=length;
453
0
      length=0U;
454
0
    }
455
0
  return length;
456
0
}
457
458
static char *
459
TracePSClippingPath(unsigned char *blob,size_t length,
460
                    const unsigned long columns,
461
                    const unsigned long rows)
462
0
{
463
0
  char
464
0
    *path,
465
0
    *message;
466
467
0
  int
468
0
    knot_count,
469
0
    selector;
470
471
0
  long
472
0
    x,
473
0
    y;
474
475
0
  PointInfo
476
0
    first[3],       /* First Bezier knot in sub-path */
477
0
    last[3],        /* Last seen Bezier knot in sub-path */
478
0
    point[3];       /* Current Bezier knot in sub-path */
479
480
0
  register long
481
0
    i;
482
483
0
  unsigned int
484
0
    in_subpath;
485
486
0
  ARG_NOT_USED(columns);
487
0
  ARG_NOT_USED(rows);
488
489
0
  first[0].x=first[0].y=first[1].x=first[1].y=0;
490
0
  last[1].x=last[1].y=last[2].x=last[2].y=0;
491
0
  path=AllocateString((char *) NULL);
492
0
  if (path == (char *) NULL)
493
0
    return((char *) NULL);
494
0
  message=AllocateString((char *) NULL);
495
496
0
  FormatString(message,"/ClipImage {\n");
497
0
  (void) ConcatenateString(&path,message);
498
0
  FormatString(message,"/c {curveto} bind def\n");
499
0
  (void) ConcatenateString(&path,message);
500
0
  FormatString(message,"/l {lineto} bind def\n");
501
0
  (void) ConcatenateString(&path,message);
502
0
  FormatString(message,"/m {moveto} bind def\n");
503
0
  (void) ConcatenateString(&path,message);
504
0
  FormatString(message,"/v {currentpoint 6 2 roll curveto} bind def\n");
505
0
  (void) ConcatenateString(&path,message);
506
0
  FormatString(message,"/y {2 copy curveto} bind def\n");
507
0
  (void) ConcatenateString(&path,message);
508
0
  FormatString(message,"/z {closepath} bind def\n");
509
0
  (void) ConcatenateString(&path,message);
510
0
  FormatString(message,"newpath\n");
511
0
  (void) ConcatenateString(&path,message);
512
513
0
  knot_count=0;
514
0
  in_subpath=False;
515
516
  /*
517
    Open and closed subpaths are all closed in the following
518
    parser loop as there's no way for the polygon renderer
519
    to render an open path to a masking image.
520
521
    The clipping path format is defined in "Adobe Photoshop File
522
    Formats Specification" version 6.0 downloadable from adobe.com.
523
  */
524
0
  while (length > 0)
525
0
    {
526
0
      selector=ReadMSBShort(&blob,&length);
527
0
      switch (selector)
528
0
        {
529
0
        case 0:
530
0
        case 3:
531
0
          {
532
0
            if (knot_count == 0)
533
0
              {
534
                /*
535
                  Expected subpath length record
536
                */
537
0
                knot_count=ReadMSBShort(&blob,&length);
538
0
                length=AdvanceBlob(22U,length,&blob);
539
0
              }
540
0
            else
541
0
              {
542
0
                length=AdvanceBlob(24U,length,&blob);
543
0
              }
544
0
            break;
545
0
          }
546
0
        case 1:
547
0
        case 2:
548
0
        case 4:
549
0
        case 5:
550
0
          {
551
0
            if (knot_count == 0)
552
0
              {
553
                /*
554
                  Unexpected subpath knot
555
                */
556
0
                length=AdvanceBlob(24U,length,&blob);
557
0
              }
558
0
            else
559
0
              {
560
                /*
561
                  Add sub-path knot
562
                */
563
0
                for (i=0; i < 3; i++)
564
0
                  {
565
0
                    y=ReadMSBLong(&blob,&length);
566
0
                    x=ReadMSBLong(&blob,&length);
567
0
                    point[i].x=(double) x/4096/4096;
568
0
                    point[i].y=1.0-(double) y/4096/4096;
569
0
                  }
570
0
                if (!in_subpath)
571
0
                  {
572
0
                    FormatString(message,"%.6f %.6f m\n",
573
0
                                 point[1].x,point[1].y);
574
0
                    for (i=0; i < 3; i++)
575
0
                      {
576
0
                        first[i]=point[i];
577
0
                        last[i]=point[i];
578
0
                      }
579
0
                  }
580
0
                else
581
0
                  {
582
                    /*
583
                      Handle special cases when Bezier curves are used
584
                      to describe corners and straight lines. This
585
                      special handling is desirable to bring down the
586
                      size in bytes of the clipping path data.
587
                    */
588
0
                    if ((last[1].x == last[2].x) &&
589
0
                        (last[1].y == last[2].y) &&
590
0
                        (point[0].x == point[1].x) &&
591
0
                        (point[0].y == point[1].y))
592
0
                      {
593
                        /*
594
                          First control point equals first anchor
595
                          point and last control point equals last
596
                          anchor point. Straight line between anchor
597
                          points.
598
                        */
599
0
                        FormatString(message,"%.6f %.6f l\n",
600
0
                                     point[1].x,point[1].y);
601
0
                      }
602
0
                    else if ((last[1].x == last[2].x) &&
603
0
                             (last[1].y == last[2].y))
604
0
                      {
605
                        /* First control point equals first anchor point */
606
0
                        FormatString(message,"%.6f %.6f %.6f %.6f v\n",
607
0
                                     point[0].x,point[0].y,
608
0
                                     point[1].x,point[1].y);
609
0
                      }
610
0
                    else if ((point[0].x == point[1].x) &&
611
0
                             (point[0].y == point[1].y))
612
0
                      {
613
                        /* Last control point equals last anchor point. */
614
0
                        FormatString(message,"%.6f %.6f %.6f %.6f y\n",
615
0
                                     last[2].x,last[2].y,
616
0
                                     point[1].x,point[1].y);
617
0
                      }
618
0
                    else
619
0
                      {
620
                        /* The full monty */
621
0
                        FormatString(message,
622
0
                                     "%.6f %.6f %.6f %.6f %.6f %.6f c\n",
623
0
                                     last[2].x,last[2].y,point[0].x,
624
0
                                     point[0].y,point[1].x,
625
0
                                     point[1].y);
626
0
                      }
627
0
                    for (i=0; i < 3; i++)
628
0
                      last[i]=point[i];
629
0
                  }
630
0
                (void) ConcatenateString(&path,message);
631
0
                in_subpath=True;
632
0
                knot_count--;
633
                /*
634
                  Close the subpath if there are no more knots.
635
                */
636
0
                if (knot_count == 0)
637
0
                  {
638
                    /*
639
                      Same special handling as above except we compare
640
                      to the first point in the path and close the
641
                      path.
642
                    */
643
0
                    if ((last[1].x == last[2].x) &&
644
0
                        (last[1].y == last[2].y) &&
645
0
                        (first[0].x == first[1].x) &&
646
0
                        (first[0].y == first[1].y))
647
0
                      {
648
0
                        FormatString(message,"%.6f %.6f l z\n",
649
0
                                     first[1].x,first[1].y);
650
0
                      }
651
0
                    else if ((last[1].x == last[2].x) &&
652
0
                             (last[1].y == last[2].y))
653
0
                      {
654
0
                        FormatString(message,"%.6f %.6f %.6f %.6f v z\n",
655
0
                                     first[0].x,first[0].y,
656
0
                                     first[1].x,first[1].y);
657
0
                      }
658
0
                    else if ((first[0].x == first[1].x) &&
659
0
                             (first[0].y == first[1].y))
660
0
                      {
661
0
                        FormatString(message,"%.6f %.6f %.6f %.6f y z\n",
662
0
                                     last[2].x,last[2].y,
663
0
                                     first[1].x,first[1].y);
664
0
                      }
665
0
                    else
666
0
                      {
667
0
                        FormatString(message,
668
0
                                     "%.6f %.6f %.6f %.6f %.6f %.6f c z\n",
669
0
                                     last[2].x,last[2].y,
670
0
                                     first[0].x,first[0].y,
671
0
                                     first[1].x,first[1].y);
672
0
                      }
673
0
                    (void) ConcatenateString(&path,message);
674
0
                    in_subpath=False;
675
0
                  }
676
0
              }
677
0
            break;
678
0
          }
679
0
        case 6:
680
0
        case 7:
681
0
        case 8:
682
0
        default:
683
0
          {
684
0
            length=AdvanceBlob(24U,length,&blob);
685
0
            break;
686
0
          }
687
0
        }
688
0
    }
689
  /*
690
    Returns an empty PS path if the path has no knots.
691
  */
692
0
  FormatString(message,"eoclip\n");
693
0
  (void) ConcatenateString(&path,message);
694
0
  FormatString(message,"} bind def");
695
0
  (void) ConcatenateString(&path,message);
696
0
  MagickFreeMemory(message);
697
0
  return(path);
698
0
}
699
700
static char *
701
TraceSVGClippingPath(unsigned char *blob,
702
                     size_t length,
703
                     const unsigned long columns,
704
                     const unsigned long rows)
705
0
{
706
0
  char
707
0
    *path,
708
0
    *message;
709
710
0
  int
711
0
    knot_count,
712
0
    selector;
713
714
0
  long
715
0
    x,
716
0
    y;
717
718
0
  PointInfo
719
0
    first[3],
720
0
    last[3],
721
0
    point[3];
722
723
0
  register long
724
0
    i;
725
726
0
  unsigned int
727
0
    in_subpath;
728
729
0
  first[0].x=first[0].y=first[1].x=first[1].y=0;
730
0
  last[1].x=last[1].y=last[2].x=last[2].y=0;
731
0
  path=AllocateString((char *) NULL);
732
0
  if (path == (char *) NULL)
733
0
    return((char *) NULL);
734
0
  message=AllocateString((char *) NULL);
735
736
0
  FormatString(message,"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
737
0
  (void) ConcatenateString(&path,message);
738
0
  FormatString(message,"<svg width=\"%lu\" height=\"%lu\">\n",columns,rows);
739
0
  (void) ConcatenateString(&path,message);
740
0
  FormatString(message,"<g>\n");
741
0
  (void) ConcatenateString(&path,message);
742
0
  FormatString(message,"<path style=\"fill:#00000000;stroke:#00000000;");
743
0
  (void) ConcatenateString(&path,message);
744
0
  FormatString(message,"stroke-width:0;stroke-antialiasing:false\" d=\"\n");
745
0
  (void) ConcatenateString(&path,message);
746
747
0
  knot_count=0;
748
0
  in_subpath=False;
749
750
  /*
751
    Open and closed subpaths are all closed in the following parser
752
    loop as there's no way for the polygon renderer to render an open
753
    path to a masking image.
754
755
    The clipping path format is defined in "Adobe Photoshop File
756
    Formats Specification" version 6.0 downloadable from adobe.com.
757
  */
758
0
  while (length > 0)
759
0
    {
760
0
      selector=ReadMSBShort(&blob,&length);
761
0
      switch (selector)
762
0
        {
763
0
        case 0:
764
0
        case 3:
765
0
          {
766
0
            if (knot_count == 0)
767
0
              {
768
                /*
769
                  Expected subpath length record
770
                */
771
0
                knot_count=ReadMSBShort(&blob,&length);
772
0
                length=AdvanceBlob(22U,length,&blob);
773
0
              }
774
0
            else
775
0
              {
776
0
                length=AdvanceBlob(24U,length,&blob);
777
0
              }
778
0
            break;
779
0
          }
780
0
        case 1:
781
0
        case 2:
782
0
        case 4:
783
0
        case 5:
784
0
          {
785
0
            if (knot_count == 0)
786
0
              {
787
                /*
788
                  Unexpected subpath knot
789
                */
790
0
                length=AdvanceBlob(24U,length,&blob);
791
0
              }
792
0
            else
793
0
              {
794
                /*
795
                  Add sub-path knot
796
                */
797
0
                for (i=0; i < 3; i++)
798
0
                  {
799
0
                    y=ReadMSBLong(&blob,&length);
800
0
                    x=ReadMSBLong(&blob,&length);
801
0
                    point[i].x=(double) x*columns/4096.0/4096.0;
802
0
                    point[i].y=(double) y*rows/4096.0/4096.0;
803
0
                  }
804
0
                if (!in_subpath)
805
0
                  {
806
0
                    FormatString(message,"M %.6f,%.6f\n",
807
0
                                 point[1].x,point[1].y);
808
0
                    for (i=0; i < 3; i++)
809
0
                      {
810
0
                        first[i]=point[i];
811
0
                        last[i]=point[i];
812
0
                      }
813
0
                  }
814
0
                else
815
0
                  {
816
                    /*
817
                      Handle special case when Bezier curves are used
818
                      to describe straight lines.
819
                    */
820
0
                    if ((last[1].x == last[2].x) &&
821
0
                        (last[1].y == last[2].y) &&
822
0
                        (point[0].x == point[1].x) &&
823
0
                        (point[0].y == point[1].y))
824
0
                      {
825
                        /*
826
                          First control point equals first anchor
827
                          point and last control point equals last
828
                          anchor point. Straight line between anchor
829
                          points.
830
                        */
831
0
                        FormatString(message,"L %.6f,%.6f\n",
832
0
                                     point[1].x,point[1].y);
833
0
                      }
834
0
                    else
835
0
                      {
836
0
                        FormatString(message,
837
0
                                     "C %.6f,%.6f %.6f,%.6f %.6f,%.6f\n",
838
0
                                     last[2].x,last[2].y,
839
0
                                     point[0].x,point[0].y,
840
0
                                     point[1].x,point[1].y);
841
0
                      }
842
0
                    for (i=0; i < 3; i++)
843
0
                      last[i]=point[i];
844
0
                  }
845
0
                (void) ConcatenateString(&path,message);
846
0
                in_subpath=True;
847
0
                knot_count--;
848
                /*
849
                  Close the subpath if there are no more knots.
850
                */
851
0
                if (knot_count == 0)
852
0
                  {
853
                    /*
854
                      Same special handling as above except we compare
855
                      to the first point in the path and close the
856
                      path.
857
                    */
858
0
                    if ((last[1].x == last[2].x) &&
859
0
                        (last[1].y == last[2].y) &&
860
0
                        (first[0].x == first[1].x) &&
861
0
                        (first[0].y == first[1].y))
862
0
                      {
863
0
                        FormatString(message,
864
0
                                     "L %.6f,%.6f Z\n",first[1].x,first[1].y);
865
0
                      }
866
0
                    else
867
0
                      {
868
0
                        FormatString(message,
869
0
                                     "C %.6f,%.6f %.6f,%.6f %.6f,%.6f Z\n",
870
0
                                     last[2].x,last[2].y,
871
0
                                     first[0].x,first[0].y,
872
0
                                     first[1].x,first[1].y);
873
0
                        (void) ConcatenateString(&path,message);
874
0
                      }
875
0
                    in_subpath=False;
876
0
                  }
877
0
              }
878
0
            break;
879
0
          }
880
0
        case 6:
881
0
        case 7:
882
0
        case 8:
883
0
        default:
884
0
          {
885
0
            length=AdvanceBlob(24U,length,&blob);
886
0
            break;
887
0
          }
888
0
        }
889
0
    }
890
  /*
891
    Returns an empty SVG image if the path has no knots.
892
  */
893
0
  FormatString(message,"\"/>\n");
894
0
  (void) ConcatenateString(&path,message);
895
0
  FormatString(message,"</g>\n");
896
0
  (void) ConcatenateString(&path,message);
897
0
  FormatString(message,"</svg>\n");
898
0
  (void) ConcatenateString(&path,message);
899
0
  MagickFreeMemory(message);
900
0
  return(path);
901
0
}
902
903
static int
904
Generate8BIMAttribute(Image *image,const char *key)
905
0
{
906
0
  char
907
0
    *attribute,
908
0
    name[MaxTextExtent],
909
0
    format[MaxTextExtent],
910
0
    *resource;
911
912
0
  int
913
0
    id,
914
0
    start,
915
0
    stop,
916
0
    sub_number;
917
918
0
  register long
919
0
    i;
920
921
0
  size_t
922
0
    length;
923
924
0
  unsigned char
925
0
    *info;
926
927
0
  unsigned int
928
0
    status;
929
930
0
  long
931
0
    count;
932
933
0
  const unsigned char
934
0
    *profile;
935
936
0
  size_t
937
0
    profile_length;
938
939
0
  if ((profile=GetImageProfile(image,"IPTC",&profile_length)) == 0)
940
0
    return(False);
941
942
  /*
943
    There may be spaces in resource names, but there are no newlines,
944
    so use a newline as terminator to get the full name.
945
  */
946
0
  count=sscanf(key,"8BIM:%d,%d:%[^\n]\n%[^\n]",&start,&stop,name,format);
947
0
  if ((count != 2) && (count != 3) && (count != 4))
948
0
    return(False);
949
0
  if (count < 4)
950
0
    (void)strlcpy(format,"SVG",sizeof(format));
951
0
  if (count < 3)
952
0
    *name='\0';
953
0
  sub_number=1;
954
0
  if (*name == '#')
955
0
    sub_number=MagickAtoL(&name[1]);
956
0
  sub_number=Max(sub_number,1);
957
0
  resource=(char *) NULL;
958
959
0
  status=False;
960
0
  length=profile_length;
961
  /*
962
    FIXME: following cast should not be necessary but info can't be
963
    const due to odd function design.
964
  */
965
0
  info=(unsigned char *) profile;
966
967
0
  while ((length > 0) && (status == False))
968
0
    {
969
0
      if (ReadByte(&info,&length) != '8')
970
0
        continue;
971
0
      if (ReadByte(&info,&length) != 'B')
972
0
        continue;
973
0
      if (ReadByte(&info,&length) != 'I')
974
0
        continue;
975
0
      if (ReadByte(&info,&length) != 'M')
976
0
        continue;
977
0
      id=ReadMSBShort(&info,&length);
978
0
      if (id < start)
979
0
        continue;
980
0
      if (id > stop)
981
0
        continue;
982
0
      if (resource != (char *)NULL)
983
0
        MagickFreeMemory(resource);
984
0
      count=ReadByte(&info,&length);
985
0
      if ((count > 0) && ((size_t) count <= length))
986
0
        {
987
0
          resource=(char *) MagickAllocateMemory(char *,
988
0
                                                 (size_t) count+MaxTextExtent);
989
0
          if (resource != (char *) NULL)
990
0
            {
991
0
              for (i=0; i < count; i++)
992
0
                resource[i]=(char) ReadByte(&info,&length);
993
0
              resource[count]='\0';
994
0
            }
995
0
        }
996
0
      if (!(count & 0x01))
997
0
        (void) ReadByte(&info,&length);
998
0
      count=ReadMSBLong(&info,&length);
999
      /*
1000
        ReadMSBLong() can return negative values such as -1 or any
1001
        other negative value.  Make sure that it is in range.
1002
      */
1003
0
      if ((count < 0) || ((size_t) count > length))
1004
0
        {
1005
0
          length=0; /* Quit loop */
1006
0
          continue;
1007
0
        }
1008
0
      if ((*name != '\0') && (*name != '#'))
1009
0
        {
1010
0
          if ((resource == (char *) NULL) ||
1011
0
              (LocaleCompare(name,resource) != 0))
1012
0
            {
1013
              /*
1014
                No name match, scroll forward and try next resource.
1015
              */
1016
0
              info+=count;
1017
0
              length-=count;
1018
0
              continue;
1019
0
            }
1020
0
        }
1021
0
      if ((*name == '#') && (sub_number != 1))
1022
0
        {
1023
          /*
1024
            No numbered match, scroll forward and try next resource.
1025
          */
1026
0
          sub_number--;
1027
0
          info+=count;
1028
0
          length-=count;
1029
0
          continue;
1030
0
        }
1031
      /*
1032
        We have the resource of interest.
1033
      */
1034
0
      attribute=(char *) MagickAllocateMemory(char *,
1035
0
                                              (size_t) count+MaxTextExtent);
1036
0
      if (attribute != (char *) NULL)
1037
0
        {
1038
0
          (void) memcpy(attribute,(char *) info,(size_t) count);
1039
0
          attribute[count]='\0';
1040
0
          info+=count;
1041
0
          length-=count;
1042
0
          if ((id <= 1999) || (id >= 2999))
1043
0
            {
1044
0
              (void) SetImageAttribute(image,key,(const char *) attribute);
1045
0
            }
1046
0
          else
1047
0
            {
1048
0
              char
1049
0
                *path;
1050
0
              if (LocaleCompare("SVG",format) == 0)
1051
0
                path=TraceSVGClippingPath((unsigned char *) attribute,count,
1052
0
                                          image->columns,image->rows);
1053
0
              else
1054
0
                path=TracePSClippingPath((unsigned char *) attribute,count,
1055
0
                                         image->columns,image->rows);
1056
0
              (void) SetImageAttribute(image,key,(const char *) path);
1057
0
              MagickFreeMemory(path);
1058
0
            }
1059
0
          MagickFreeMemory(attribute);
1060
0
          status=True;
1061
0
        }
1062
0
    }
1063
0
  if (resource != (char *)NULL)
1064
0
    MagickFreeMemory(resource);
1065
0
  return(status);
1066
0
}
1067
1068
9.40k
#define DE_STACK_SIZE  16
1069
1.59k
#define EXIF_DELIMITER  "\n"
1070
#define EXIF_NUM_FORMATS  12
1071
0
#define EXIF_FMT_NOTYPE 0       /* Not yet assigned, unknown */
1072
6.96k
#define EXIF_FMT_BYTE  1        /* An 8-bit unsigned integer. */
1073
3.95k
#define EXIF_FMT_ASCII  2       /* An 8-bit byte containing one 7-bit ASCII code. NUL terminated */
1074
3.94k
#define EXIF_FMT_USHORT  3      /* A 16-bit (2-byte) unsigned integer */
1075
17.1k
#define EXIF_FMT_ULONG  4       /* A 32-bit (4-byte) unsigned intege */
1076
2.74k
#define EXIF_FMT_URATIONAL  5   /* Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator. */
1077
2.75k
#define EXIF_FMT_SBYTE  6
1078
5.16k
#define EXIF_FMT_UNDEFINED  7   /* An 8-bit byte that may take any value depending on the field definition. */
1079
1.22k
#define EXIF_FMT_SSHORT  8
1080
7.24k
#define EXIF_FMT_SLONG  9       /* A 32-bit (4-byte) signed integer (2's complement notation). */
1081
1.72k
#define EXIF_FMT_SRATIONAL  10  /* Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator. */
1082
1.79k
#define EXIF_FMT_SINGLE  11
1083
12.6k
#define EXIF_FMT_DOUBLE  12
1084
77.0k
#define EXIF_TAG_START  0x0100
1085
67.7k
#define EXIF_TAG_STOP  0xFFFF
1086
47.9k
#define TAG_EXIF_OFFSET  0x8769
1087
47.0k
#define TAG_INTEROP_OFFSET  0xa005
1088
#define GPS_TAG_START 0x0000
1089
20.0k
#define GPS_TAG_STOP  0x001F
1090
152k
#define GPS_OFFSET 0x8825
1091
0
#define GPS_LATITUDE 0x0002
1092
0
#define GPS_LONGITUDE 0x0004
1093
0
#define GPS_TIMESTAMP 0x0007
1094
34.0k
#define MAX_TAGS_PER_IFD 1024 /* Maximum tags allowed per IFD */
1095
179
#define EXIF_ORIENTATION 0x0112
1096
39.3k
#define AnyCount -1
1097
1098
/* Bit-field flags for supporting OR logic to represent tags which accept multiple types */
1099
0
#define F_NOTYPE     0x0000  /* Not yet assigned, unknown */
1100
6.96k
#define F_BYTE       0x0002  /* An 8-bit unsigned integer. */
1101
3.95k
#define F_ASCII      0x0004  /* An 8-bit byte containing one 7-bit ASCII code. NUL terminated (count includes NUL). */
1102
3.39k
#define F_SHORT      0x0008  /* A 16-bit (2-byte) unsigned integer */
1103
13.5k
#define F_LONG       0x0010  /* A 32-bit (4-byte) unsigned intege */
1104
2.74k
#define F_RATIONAL   0x0020  /* Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator. */
1105
2.75k
#define F_SBYTE      0x0040  /* An 8-bit signed integer. */
1106
5.16k
#define F_UNDEFINED  0x0080  /* An 8-bit byte that may take any value depending on the field definition. */
1107
1.22k
#define F_SSHORT     0x0100  /* A 16-bit (2-byte) signed integer */
1108
5.34k
#define F_SLONG      0x0200  /* A 32-bit (4-byte) signed integer (2's complement notation). */
1109
1.72k
#define F_SRATIONAL  0x0400  /* Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator. */
1110
1.79k
#define F_SINGLE     0x0800  /* Single IEEE floating point value (32 bit) */
1111
12.6k
#define F_DOUBLE     0x1000  /* Double IEEE floating point value (64 bit) */
1112
1113
typedef struct _IFDTagTableType
1114
{
1115
  /* TIFF IFD tag number */
1116
  unsigned short
1117
    tag;
1118
1119
   /* Accepted tag formats declared by a standard */
1120
  short
1121
    format;
1122
1123
  /* Other formats for the tag discovered to be in use (likely due to a bug) */
1124
  short
1125
    format_alt;
1126
1127
  /* Expected component count. -1 == Any, 0 == not set yet */
1128
  short
1129
    count;
1130
1131
  /* Tag description */
1132
  const char
1133
    description[36];
1134
1135
  /*
1136
    Add tag format and component numbers
1137
    https://www.media.mit.edu/pia/Research/deepview/exif.html
1138
  */
1139
} IFDTagTableType;
1140
1141
#define TAG_INFO(tag,format,format_alt,count,description) {tag,format,format_alt,count,description}
1142
1143
/* https://en.wikipedia.org/wiki/Exif */
1144
/* Microsoft Windows GDI+ tags are documented at https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-property-tags-in-numerical-order */
1145
1146
static IFDTagTableType
1147
tag_table[] =
1148
  {
1149
    /*
1150
      GPS Info IFD tags. These should be in a separate numeric space
1151
      from EXIF/TIFF tags!
1152
    */
1153
    TAG_INFO(0x0000, F_BYTE, F_NOTYPE, 4, "GPSVersionID"), /* GPS IFD */
1154
    TAG_INFO(0x0001, F_ASCII, F_NOTYPE, 2, "GPSLatitudeRef"), /* GPS IFD */
1155
    TAG_INFO(0x0002, F_RATIONAL, F_NOTYPE, 3, "GPSLatitude"), /* GPS IFD */
1156
    TAG_INFO(0x0003, F_ASCII, F_NOTYPE, 2, "GPSLongitudeRef"), /* GPS IFD */
1157
    TAG_INFO(0x0004, F_RATIONAL, F_NOTYPE, 3, "GPSLongitude"), /* GPS IFD */
1158
    TAG_INFO(0x0005, F_BYTE, F_NOTYPE, 1, "GPSAltitudeRef"), /* GPS IFD */
1159
    TAG_INFO(0x0006, F_RATIONAL, F_NOTYPE, 1, "GPSAltitude"), /* GPS IFD */
1160
    TAG_INFO(0x0007, F_RATIONAL, F_NOTYPE, 3, "GPSTimeStamp"), /* GPS IFD */
1161
    TAG_INFO(0x0008, F_ASCII, F_NOTYPE, AnyCount, "GPSSatellites"), /* GPS IFD */
1162
    TAG_INFO(0x0009, F_ASCII, F_NOTYPE, 2, "GPSStatus"), /* GPS IFD */
1163
    TAG_INFO(0x000A, F_ASCII, F_NOTYPE, 2, "GPSMeasureMode"), /* GPS IFD */
1164
    TAG_INFO(0x000B, F_RATIONAL, F_NOTYPE, 1, "GPSDOP"), /* GPS IFD */
1165
    TAG_INFO(0x000C, F_ASCII, F_NOTYPE, 2, "GPSSpeedRef"), /* GPS IFD */
1166
    TAG_INFO(0x000D, F_RATIONAL, F_NOTYPE, 1, "GPSSpeed"), /* GPS IFD */
1167
    TAG_INFO(0x000E, F_ASCII, F_NOTYPE, 2, "GPSTrackRef"), /* GPS IFD */
1168
    TAG_INFO(0x000F, F_RATIONAL, F_NOTYPE, 1, "GPSTrack"), /* GPS IFD */
1169
    TAG_INFO(0x0010, F_ASCII, F_NOTYPE, 2, "GPSImgDirectionRef"), /* GPS IFD */
1170
    TAG_INFO(0x0011, F_RATIONAL, F_NOTYPE, 1, "GPSImgDirection"), /* GPS IFD */
1171
    TAG_INFO(0x0012, F_ASCII, F_NOTYPE, 0, "GPSMapDatum"), /* GPS IFD */
1172
    TAG_INFO(0x0013, F_ASCII, F_NOTYPE, 2, "GPSDestLatitudeRef"), /* GPS IFD */
1173
    TAG_INFO(0x0014, F_RATIONAL, F_NOTYPE, 3, "GPSDestLatitude"), /* GPS IFD */
1174
    TAG_INFO(0x0015, F_ASCII, F_NOTYPE, 2, "GPSDestLongitudeRef"), /* GPS IFD */
1175
    TAG_INFO(0x0016, F_RATIONAL, F_NOTYPE, 3, "GPSDestLongitude"), /* GPS IFD */
1176
    TAG_INFO(0x0017, F_ASCII, F_NOTYPE, 2, "GPSDestBearingRef"), /* GPS IFD */
1177
    TAG_INFO(0x0018, F_RATIONAL, F_NOTYPE, 1, "GPSDestBearing"), /* GPS IFD */
1178
    TAG_INFO(0x0019, F_ASCII, F_NOTYPE, 2, "GPSDestDistanceRef"), /* GPS IFD */
1179
    TAG_INFO(0x001A, F_RATIONAL, F_NOTYPE, 1, "GPSDestDistance"), /* GPS IFD */
1180
    TAG_INFO(0x001B, F_UNDEFINED, F_NOTYPE, AnyCount, "GPSProcessingMethod"), /* GPS IFD */
1181
    TAG_INFO(0x001C, F_UNDEFINED, F_NOTYPE, AnyCount, "GPSAreaInformation"), /* GPS IFD */
1182
    TAG_INFO(0x001D, F_ASCII, F_NOTYPE, 11, "GPSDateStamp"), /* GPS IFD */
1183
    TAG_INFO(0x001E, F_SHORT, F_NOTYPE, 1, "GPSDifferential"), /* GPS IFD */
1184
    TAG_INFO(0x001F, F_RATIONAL, F_NOTYPE, 1, "GPSHPositioningError"), /* GPS IFD */
1185
1186
    TAG_INFO(0x0100, F_SHORT | F_LONG, F_SLONG, 1, "ImageWidth"),
1187
    TAG_INFO(0x0101, F_SHORT | F_LONG, F_SLONG, 1, "ImageLength"),
1188
    TAG_INFO(0x0102, F_SHORT, F_NOTYPE, AnyCount /* Could be 1, 3, 4! */, "BitsPerSample"),
1189
    TAG_INFO(0x0103, F_SHORT, F_NOTYPE, 1, "Compression"),
1190
    TAG_INFO(0x0106, F_SHORT, F_NOTYPE, 1, "PhotometricInterpretation"),
1191
    TAG_INFO(0x010A, F_SHORT, F_NOTYPE, 1, "FillOrder"),
1192
    TAG_INFO(0x010D, F_ASCII, F_NOTYPE, AnyCount, "DocumentName"),
1193
    TAG_INFO(0x010E, F_ASCII, F_NOTYPE, AnyCount, "ImageDescription"),
1194
    TAG_INFO(0x010F, F_ASCII, F_NOTYPE, AnyCount, "Make"),
1195
    TAG_INFO(0x0110, F_ASCII, F_NOTYPE, AnyCount, "Model"),
1196
    TAG_INFO(0x0111, F_SHORT | F_LONG, F_NOTYPE, 0, "StripOffsets"),
1197
    TAG_INFO(0x0112, F_SHORT, F_SLONG, 1, "Orientation"),
1198
    TAG_INFO(0x0115, F_SHORT, F_NOTYPE, 1, "SamplesPerPixel"),
1199
    TAG_INFO(0x0116, F_SHORT | F_LONG, F_NOTYPE, 1, "RowsPerStrip"),
1200
    TAG_INFO(0x0117, F_LONG  | F_SHORT, F_NOTYPE, 0, "StripByteCounts"),
1201
    TAG_INFO(0x0118, F_SHORT, F_NOTYPE, 0, "MinSampleValue"),
1202
    TAG_INFO(0x0119, F_SHORT, F_NOTYPE, 0, "MaxSampleValue"),
1203
    TAG_INFO(0x011A, F_RATIONAL, F_NOTYPE, 1, "XResolution"),
1204
    TAG_INFO(0x011B, F_RATIONAL, F_NOTYPE, 1, "YResolution"),
1205
    TAG_INFO(0x011C, F_SHORT, F_NOTYPE, 1, "PlanarConfiguration"),
1206
    TAG_INFO(0x011D, F_ASCII, F_NOTYPE, 0, "PageName"),
1207
    TAG_INFO(0x011E, F_RATIONAL, F_NOTYPE, 1, "XPosition"),
1208
    TAG_INFO(0x011F, F_RATIONAL, F_NOTYPE, 1, "YPosition"),
1209
1210
    TAG_INFO(0x0120, F_LONG, F_NOTYPE, AnyCount, "FreeOffsets"), /* Defunct feature */
1211
    TAG_INFO(0x0121, F_LONG, F_NOTYPE, AnyCount, "FreeByteCounts"), /* Defunct feature */
1212
    TAG_INFO(0x0122, F_SHORT, F_NOTYPE, 1, "GrayResponseUnit"),
1213
    TAG_INFO(0x0123, F_SHORT, F_NOTYPE, 0 /* 2**BitsPerSample */, "GrayResponseCurve"),
1214
    TAG_INFO(0x0124, F_LONG, F_NOTYPE, 1, "T4Options"),
1215
    TAG_INFO(0x0125, F_LONG, F_NOTYPE, 1, "T6Options"),
1216
    TAG_INFO(0x0128, F_SHORT, F_NOTYPE, 1, "ResolutionUnit"),
1217
    TAG_INFO(0x012D, F_SHORT, F_NOTYPE, 3*256  /* (1 or 3) * (1 << BitsPerSample) */, "TransferFunction"),
1218
    TAG_INFO(0x0131, F_ASCII, F_NOTYPE, AnyCount, "Software"),
1219
    TAG_INFO(0x0132, F_ASCII, F_NOTYPE, 21 /* 20+NUL? TIFF spec says 20 */, "DateTime"),
1220
    TAG_INFO(0x013B, F_ASCII, F_NOTYPE, AnyCount, "Artist"),
1221
    TAG_INFO(0x013C, F_ASCII, F_NOTYPE, AnyCount, "HostComputer"),
1222
    TAG_INFO(0x013D, F_SHORT, F_NOTYPE, 0, "Predictor"),
1223
    TAG_INFO(0x013E, F_RATIONAL, F_NOTYPE, 2, "WhitePoint"),
1224
    TAG_INFO(0x013F, F_RATIONAL, F_NOTYPE, 6, "PrimaryChromaticities"),
1225
    TAG_INFO(0x0140, F_SHORT, F_NOTYPE, 0 /* 3 * (2**BitsPerSample) */, "ColorMap"),
1226
    TAG_INFO(0x0141, F_SHORT, F_NOTYPE, 2, "HalfToneHints"),
1227
    TAG_INFO(0x0142, F_SHORT | F_LONG, F_NOTYPE, AnyCount /* TBD */, "TileWidth"),
1228
    TAG_INFO(0x0143, F_SHORT | F_LONG, F_NOTYPE, AnyCount /* TBD */, "TileLength"),
1229
    TAG_INFO(0x0144, F_LONG, F_NOTYPE, AnyCount /* TBD */, "TileOffsets"),
1230
    TAG_INFO(0x0145, F_SHORT | F_LONG, F_NOTYPE, AnyCount /* TBD */, "TileByteCounts"),
1231
    TAG_INFO(0x014A, F_LONG, F_NOTYPE, 1, "SubIFD"),
1232
    TAG_INFO(0x014C, F_SHORT, F_NOTYPE, 1, "InkSet"),
1233
    TAG_INFO(0x014D, F_ASCII, F_NOTYPE, AnyCount, "InkNames"),
1234
    TAG_INFO(0x014E, F_SHORT, F_NOTYPE, 1, "NumberOfInks"),
1235
    TAG_INFO(0x0150, F_BYTE | F_SHORT, F_NOTYPE, 0 /* 2, or 2*SamplesPerPixel */, "DotRange"),
1236
    TAG_INFO(0x0151, F_ASCII, F_NOTYPE, AnyCount, "TargetPrinter"),
1237
    TAG_INFO(0x0152, F_SHORT, F_NOTYPE, 0, "ExtraSample"),
1238
    TAG_INFO(0x0153, F_SHORT, F_NOTYPE, 0, "SampleFormat"),
1239
    TAG_INFO(0x0154, F_BYTE | F_SHORT | F_LONG | F_SRATIONAL | F_DOUBLE, F_NOTYPE, 0, "SMinSampleValue"), /* BYTE or SHORT or LONG or RATIONAL or DOUBLE */
1240
    TAG_INFO(0x0155, F_BYTE | F_SHORT | F_LONG | F_SRATIONAL | F_DOUBLE, F_NOTYPE, 0, "SMaxSampleValue"), /* BYTE or SHORT or LONG or RATIONAL or DOUBLE */
1241
    TAG_INFO(0x0156, F_SHORT, F_NOTYPE, 6, "TransferRange"),
1242
    TAG_INFO(0x0157, F_BYTE, F_NOTYPE, AnyCount, "ClipPath"),
1243
    TAG_INFO(0x0158, F_LONG, F_NOTYPE, 1, "XClipPathUnits"),
1244
    TAG_INFO(0x0159, F_LONG, F_NOTYPE, 1, "YClipPathUnits"),
1245
    TAG_INFO(0x015A, F_SHORT, F_NOTYPE, 1, "Indexed"),
1246
    TAG_INFO(0x015B, F_UNDEFINED, F_NOTYPE, 0, "JPEGTables"),
1247
    TAG_INFO(0x015F, F_SHORT, F_NOTYPE, 0, "OPIProxy"),
1248
    TAG_INFO(0x0200, F_SHORT, F_NOTYPE, 1, "JPEGProc"),
1249
    TAG_INFO(0x0201, F_LONG, F_NOTYPE, 1, "JPEGInterchangeFormat"),
1250
    TAG_INFO(0x0202, F_LONG, F_NOTYPE, 1, "JPEGInterchangeFormatLength"),
1251
    TAG_INFO(0x0203, F_SHORT, F_NOTYPE, 1, "JPEGRestartInterval"),
1252
    TAG_INFO(0x0205, F_SHORT, F_NOTYPE, 0, "JPEGLosslessPredictors"),
1253
    TAG_INFO(0x0206, F_SHORT, F_NOTYPE, 0, "JPEGPointTransforms"),
1254
    TAG_INFO(0x0207, F_LONG, F_NOTYPE, 0, "JPEGQTables"),
1255
    TAG_INFO(0x0208, F_LONG, F_NOTYPE, 0, "JPEGDCTables"),
1256
    TAG_INFO(0x0209, F_LONG, F_NOTYPE, 0, "JPEGACTables"),
1257
    TAG_INFO(0x0211, F_RATIONAL, F_NOTYPE, 3, "YCbCrCoefficients"),
1258
    TAG_INFO(0x0212, F_SHORT, F_NOTYPE, 2, "YCbCrSubSampling"),
1259
    TAG_INFO(0x0213, F_SHORT, F_NOTYPE, 1, "YCbCrPositioning"),
1260
    TAG_INFO(0x0214, F_RATIONAL, F_NOTYPE, 0 /* 2*SamplesPerPixel */, "ReferenceBlackWhite"),
1261
    TAG_INFO(0x02BC, F_NOTYPE, F_NOTYPE, 0, "ExtensibleMetadataPlatform"), /* XMP */
1262
    TAG_INFO(0x0301, F_NOTYPE, F_NOTYPE, 0, "Gamma"),
1263
    TAG_INFO(0x0302, F_NOTYPE, F_NOTYPE, 0, "ICCProfileDescriptor"),
1264
    TAG_INFO(0x0303, F_NOTYPE, F_NOTYPE, 0, "SRGBRenderingIntent"),
1265
    TAG_INFO(0x0320, F_NOTYPE, F_NOTYPE, 0, "ImageTitle"),
1266
1267
    /*
1268
      Interoperability IFD tags.  These should be in a separate
1269
      numeric space from EXIF/TIFF tags!
1270
    */
1271
    TAG_INFO(0x1000, F_NOTYPE, F_NOTYPE, 0, "RelatedImageFileFormat"), /* Exif Interoperability IFD */
1272
    TAG_INFO(0x1001, F_SHORT | F_LONG, F_NOTYPE, 0, "RelatedImageWidth"), /* Exif Interoperability IFD */
1273
    TAG_INFO(0x1002, F_SHORT | F_LONG, F_NOTYPE, 0, "RelatedImageLength"), /* Exif Interoperability IFD */
1274
1275
    TAG_INFO(0x5001, F_SHORT, F_NOTYPE, 1, "ResolutionXUnit"), /* Microsoft Windows GDI+ */
1276
    TAG_INFO(0x5002, F_SHORT, F_NOTYPE, 1, "ResolutionYUnit"), /* Microsoft Windows GDI+ */
1277
    TAG_INFO(0x5003, F_SHORT, F_NOTYPE, 1, "ResolutionXLengthUnit"), /* Microsoft Windows GDI+ */
1278
    TAG_INFO(0x5004, F_SHORT, F_NOTYPE, 1, "ResolutionYLengthUnit"), /* Microsoft Windows GDI+ */
1279
    TAG_INFO(0x5005, F_ASCII, F_NOTYPE, AnyCount /* Number of flags */, "PrintFlags"), /* Microsoft Windows GDI+ */
1280
    TAG_INFO(0x5006, F_SHORT, F_NOTYPE, 1, "PrintFlagsVersion"), /* Microsoft Windows GDI+ */
1281
    TAG_INFO(0x5007, F_BYTE, F_NOTYPE, 1, "PrintFlagsCrop"), /* Microsoft Windows GDI+ */
1282
    TAG_INFO(0x5008, F_LONG, F_NOTYPE, 1, "PrintFlagsBleedWidth"), /* Microsoft Windows GDI+ */
1283
    TAG_INFO(0x5009, F_SHORT, F_NOTYPE, 1, "PrintFlagsBleedWidthScale"), /* Microsoft Windows GDI+ */
1284
    TAG_INFO(0x500A, F_RATIONAL, F_NOTYPE, 1, "HalftoneLPI"), /* Microsoft Windows GDI+ */
1285
    TAG_INFO(0x500B, F_SHORT, F_NOTYPE, 1, "HalftoneLPIUnit"), /* Microsoft Windows GDI+ */
1286
    TAG_INFO(0x500C, F_RATIONAL, F_NOTYPE, 1, "HalftoneDegree"), /* Microsoft Windows GDI+ */
1287
    TAG_INFO(0x500D, F_SHORT, F_NOTYPE, 1, "HalftoneShape"), /* Microsoft Windows GDI+ */
1288
    TAG_INFO(0x500E, F_LONG, F_NOTYPE, 1, "HalftoneMisc"), /* Microsoft Windows GDI+ */
1289
    TAG_INFO(0x500F, F_BYTE, F_NOTYPE, 1, "HalftoneScreen"), /* Microsoft Windows GDI+ */
1290
    TAG_INFO(0x5010, F_SHORT, F_NOTYPE, AnyCount, "JPEGQuality"), /* Microsoft Windows GDI+ */
1291
    TAG_INFO(0x5011, F_UNDEFINED, F_NOTYPE, AnyCount, "GridSize"), /* Microsoft Windows GDI+ */
1292
    TAG_INFO(0x5012, F_LONG, F_NOTYPE, 1, "ThumbnailFormat"), /* Microsoft Windows GDI+ */
1293
    TAG_INFO(0x5013, F_LONG, F_NOTYPE, 1, "ThumbnailWidth"), /* Microsoft Windows GDI+ */
1294
    TAG_INFO(0x5014, F_LONG, F_NOTYPE, 1, "ThumbnailHeight"), /* Microsoft Windows GDI+ */
1295
    TAG_INFO(0x5015, F_SHORT, F_NOTYPE, 1, "ThumbnailColorDepth"), /* Microsoft Windows GDI+ */
1296
    TAG_INFO(0x5016, F_SHORT, F_NOTYPE, 1, "ThumbnailPlanes"), /* Microsoft Windows GDI+ */
1297
    TAG_INFO(0x5017, F_LONG, F_NOTYPE, 1, "ThumbnailRawBytes"), /* Microsoft Windows GDI+ */
1298
    TAG_INFO(0x5018, F_LONG, F_NOTYPE, 1, "ThumbnailSize"), /* Microsoft Windows GDI+ */
1299
    TAG_INFO(0x5019, F_LONG, F_NOTYPE, 1, "ThumbnailCompressedSize"), /* Microsoft Windows GDI+ */
1300
    TAG_INFO(0x501A, F_UNDEFINED, F_NOTYPE, 0, "ColorTransferFunction"), /* Microsoft Windows GDI+ */
1301
    TAG_INFO(0x501B, F_BYTE, F_NOTYPE, 0, "ThumbnailData"), /* Microsoft Windows GDI+ */
1302
    TAG_INFO(0x5020, F_LONG | F_SHORT, F_NOTYPE, 1, "ThumbnailImageWidth"), /* Microsoft Windows GDI+ */
1303
    TAG_INFO(0x5021, F_LONG | F_SHORT, F_NOTYPE, 1, "ThumbnailImageHeight"), /* Microsoft Windows GDI+ */
1304
    TAG_INFO(0x5022, F_SHORT, F_NOTYPE, 1, "ThumbnailBitsPerSample"), /* Microsoft Windows GDI+ */
1305
    TAG_INFO(0x5023, F_SHORT, F_NOTYPE, 1, "ThumbnailCompression"), /* Microsoft Windows GDI+ */
1306
    TAG_INFO(0x5024, F_SHORT, F_NOTYPE, 1, "ThumbnailPhotometricInterp"), /* Microsoft Windows GDI+ */
1307
    TAG_INFO(0x5025, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailImageDescription"), /* Microsoft Windows GDI+ */
1308
    TAG_INFO(0x5026, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailEquipMake"), /* Microsoft Windows GDI+ */
1309
    TAG_INFO(0x5027, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailEquipModel"), /* Microsoft Windows GDI+ */
1310
    TAG_INFO(0x5028, F_LONG | F_SHORT, F_NOTYPE, 0, "ThumbnailStripOffsets"), /* Microsoft Windows GDI+ */
1311
    TAG_INFO(0x5029, F_SHORT, F_NOTYPE, 1, "ThumbnailOrientation"), /* Microsoft Windows GDI+ */
1312
    TAG_INFO(0x502A, F_SHORT, F_NOTYPE, 1, "ThumbnailSamplesPerPixel"), /* Microsoft Windows GDI+ */
1313
    TAG_INFO(0x502B, F_LONG | F_SHORT, F_NOTYPE, 1, "ThumbnailRowsPerStrip"), /* Microsoft Windows GDI+ */
1314
    TAG_INFO(0x502C, F_LONG | F_SHORT, F_NOTYPE, 0, "ThumbnailStripBytesCount"), /* Microsoft Windows GDI+ */
1315
    TAG_INFO(0x502D, F_SHORT /* type provided by ThumbnailResolutionUnit */, F_NOTYPE, 0, "ThumbnailResolutionX"), /* Microsoft Windows GDI+ */
1316
    TAG_INFO(0x502E, F_SHORT /* type provided by ThumbnailResolutionUnit */, F_NOTYPE, 0, "ThumbnailResolutionY"), /* Microsoft Windows GDI+ */
1317
    TAG_INFO(0x502F, F_SHORT, F_NOTYPE, 1, "ThumbnailPlanarConfig"), /* Microsoft Windows GDI+ */
1318
    TAG_INFO(0x5030, F_SHORT, F_NOTYPE, 1, "ThumbnailResolutionUnit"), /* Microsoft Windows GDI+ */
1319
    TAG_INFO(0x5031, F_SHORT, F_NOTYPE, 0, "ThumbnailTransferFunction"), /* Microsoft Windows GDI+ */
1320
    TAG_INFO(0x5032, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailSoftwareUsed"), /* Microsoft Windows GDI+ */
1321
    TAG_INFO(0x5033, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailDateTime"), /* Microsoft Windows GDI+ */
1322
    TAG_INFO(0x5034, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailArtist"), /* Microsoft Windows GDI+ */
1323
    TAG_INFO(0x5035, F_RATIONAL, F_NOTYPE, 2, "ThumbnailWhitePoint"), /* Microsoft Windows GDI+ */
1324
    TAG_INFO(0x5036, F_RATIONAL, F_NOTYPE, 6, "ThumbnailPrimaryChromaticities"), /* Microsoft Windows GDI+ */
1325
    TAG_INFO(0x5037, F_RATIONAL, F_NOTYPE, 3, "ThumbnailYCbCrCoefficients"), /* Microsoft Windows GDI+ */
1326
    TAG_INFO(0x5038, F_SHORT, F_NOTYPE, 2, "ThumbnailYCbCrSubsampling"), /* Microsoft Windows GDI+ */
1327
    TAG_INFO(0x5039, F_SHORT, F_NOTYPE, 1, "ThumbnailYCbCrPositioning"), /* Microsoft Windows GDI+ */
1328
    TAG_INFO(0x503A, F_RATIONAL, F_NOTYPE, 6, "ThumbnailRefBlackWhite"), /* Microsoft Windows GDI+ */
1329
    TAG_INFO(0x503B, F_ASCII, F_NOTYPE, AnyCount, "ThumbnailCopyRight"), /* Microsoft Windows GDI+ */
1330
    TAG_INFO(0x5090, F_SHORT, F_NOTYPE, 64, "LuminanceTable"), /* Microsoft Windows GDI+ */
1331
    TAG_INFO(0x5091, F_SHORT, F_NOTYPE, 64, "ChrominanceTable"), /* Microsoft Windows GDI+ */
1332
    TAG_INFO(0x5100, F_LONG, F_NOTYPE, 0, "FrameDelay"), /* Microsoft Windows GDI+ */
1333
    TAG_INFO(0x5101, F_SHORT, F_NOTYPE, 1, "LoopCount"), /* Microsoft Windows GDI+ */
1334
    TAG_INFO(0x5102, F_BYTE, F_NOTYPE, 0, "GlobalPalette"), /* Microsoft Windows GDI+ */
1335
    TAG_INFO(0x5103, F_BYTE, F_NOTYPE, 1, "IndexBackground"), /* Microsoft Windows GDI+ */
1336
    TAG_INFO(0x5104, F_BYTE, F_NOTYPE, 1, "IndexTransparent"), /* Microsoft Windows GDI+ */
1337
    TAG_INFO(0x5110, F_BYTE, F_NOTYPE, 0, "PixelUnit"), /* Microsoft Windows GDI+ */
1338
    TAG_INFO(0x5111, F_LONG, F_NOTYPE, 1, "PixelPerUnitX"), /* Microsoft Windows GDI+ */
1339
    TAG_INFO(0x5112, F_LONG, F_NOTYPE, 1, "PixelPerUnitY"), /* Microsoft Windows GDI+ */
1340
    TAG_INFO(0x5113, F_BYTE, F_NOTYPE, AnyCount, "PaletteHistogram"), /* Microsoft Windows GDI+ */
1341
    TAG_INFO(0x800D, F_ASCII, F_NOTYPE, AnyCount, "ImageID"), /* Adobe OPI */
1342
    TAG_INFO(0x80E3, F_NOTYPE, F_NOTYPE, 0, "Matteing"),
1343
    TAG_INFO(0x80E4, F_NOTYPE, F_NOTYPE, 0, "DataType"),
1344
    TAG_INFO(0x80E5, F_NOTYPE, F_NOTYPE, 0, "ImageDepth"),
1345
    TAG_INFO(0x80E6, F_NOTYPE, F_NOTYPE, 0, "TileDepth"),
1346
    TAG_INFO(0x828D, F_SHORT, F_NOTYPE, 0, "CFARepeatPatternDim"),
1347
    TAG_INFO(0x828E, F_BYTE, F_NOTYPE, 0, "CFAPattern"),
1348
    TAG_INFO(0x828F, F_RATIONAL, F_NOTYPE, 0, "BatteryLevel"),
1349
    TAG_INFO(0x8298, F_ASCII, F_NOTYPE, AnyCount, "Copyright"),
1350
    TAG_INFO(0x829A, F_RATIONAL, F_NOTYPE, 1, "ExposureTime"),
1351
    TAG_INFO(0x829D, F_RATIONAL, F_NOTYPE, 1, "FNumber"),
1352
    TAG_INFO(0x83BB, F_LONG, F_NOTYPE, 0, "IPTC/NAA"),
1353
    TAG_INFO(0x84E3, F_NOTYPE, F_NOTYPE, 0, "IT8RasterPadding"),
1354
    TAG_INFO(0x84E5, F_NOTYPE, F_NOTYPE, 0, "IT8ColorTable"),
1355
    TAG_INFO(0x8649, F_NOTYPE, F_NOTYPE, 0, "ImageResourceInformation"),
1356
    TAG_INFO(0x8769, F_LONG, F_NOTYPE, 1, "ExifOffset"), /* Exif IFD Pointer */
1357
    TAG_INFO(0x8773, F_BYTE, F_NOTYPE, AnyCount, "InterColorProfile"), /* ICCProfile */
1358
    TAG_INFO(0x8822, F_SHORT, F_NOTYPE, 1, "ExposureProgram"),
1359
    TAG_INFO(0x8824, F_ASCII, F_NOTYPE, AnyCount, "SpectralSensitivity"),
1360
    TAG_INFO(0x8825, F_LONG /* TBD */, F_SLONG, 1, "GPSInfo"), /* Offset to GPS IFD */
1361
    TAG_INFO(0x8827, F_SHORT, F_NOTYPE, AnyCount, "PhotographicSensitivity"), /* Was "ISOSpeedRatings" */
1362
    TAG_INFO(0x8828, F_UNDEFINED, F_NOTYPE, AnyCount, "OECF"),
1363
    TAG_INFO(0x8830, F_SHORT, F_NOTYPE, 1, "SensitivityType"),
1364
    TAG_INFO(0x8831, F_LONG, F_NOTYPE, 1, "StandardOutputSensitivity"),
1365
    TAG_INFO(0x8832, F_LONG, F_NOTYPE, 1, "RecommendedExposureIndex"),
1366
    TAG_INFO(0x8833, F_LONG, F_NOTYPE, 1, "ISOSpeed"),
1367
    TAG_INFO(0x8834, F_LONG, F_NOTYPE, 1, "ISOSpeedLatitudeyyy"),
1368
    TAG_INFO(0x8835, F_LONG, F_NOTYPE, 1, "ISOSpeedLatitudezzz"),
1369
    TAG_INFO(0x9000, F_UNDEFINED, F_NOTYPE, 4, "ExifVersion"), /* e.g. "0300" without NUL */
1370
    TAG_INFO(0x9003, F_ASCII, F_NOTYPE, 20, "DateTimeOriginal"),
1371
    TAG_INFO(0x9004, F_ASCII, F_NOTYPE, 20, "DateTimeDigitized"),
1372
    TAG_INFO(0x9010, F_ASCII, F_NOTYPE, 7, "OffsetTime"),
1373
    TAG_INFO(0x9011, F_ASCII, F_NOTYPE, 7, "OffsetTimeOriginal"),
1374
    TAG_INFO(0x9012, F_ASCII, F_NOTYPE, 7, "OffsetTimeDigitized"),
1375
    TAG_INFO(0x9101, F_UNDEFINED, F_NOTYPE, 4, "ComponentsConfiguration"),
1376
    TAG_INFO(0x9102, F_RATIONAL, F_NOTYPE, 1, "CompressedBitsPerPixel"),
1377
    TAG_INFO(0x9201, F_SRATIONAL /* TBD */, F_RATIONAL, 1, "ShutterSpeedValue"),
1378
    TAG_INFO(0x9202, F_RATIONAL, F_NOTYPE, 1, "ApertureValue"),
1379
    TAG_INFO(0x9203, F_SRATIONAL, F_NOTYPE, 1, "BrightnessValue"),
1380
    TAG_INFO(0x9204, F_SRATIONAL /* TBD */, F_RATIONAL, 1, "ExposureBiasValue"),
1381
    TAG_INFO(0x9205, F_RATIONAL, F_NOTYPE, 1, "MaxApertureValue"),
1382
    TAG_INFO(0x9206, F_RATIONAL, F_NOTYPE, 1, "SubjectDistance"),
1383
    TAG_INFO(0x9207, F_SHORT, F_NOTYPE, 1, "MeteringMode"),
1384
    TAG_INFO(0x9208, F_SHORT, F_NOTYPE, 1, "LightSource"),
1385
    TAG_INFO(0x9209, F_SHORT, F_NOTYPE, 1, "Flash"),
1386
    TAG_INFO(0x920A, F_RATIONAL, F_NOTYPE, 1, "FocalLength"),
1387
    TAG_INFO(0x9214, F_SHORT, F_NOTYPE, AnyCount /* 2 or 3 or 4 */, "SubjectArea"),
1388
    TAG_INFO(0x927C, F_UNDEFINED, F_NOTYPE, AnyCount, "MakerNote"),
1389
    TAG_INFO(0x9286, F_UNDEFINED, F_NOTYPE, AnyCount, "UserComment"),
1390
    TAG_INFO(0x9290, F_ASCII, F_NOTYPE, AnyCount, "SubSecTime"),
1391
    TAG_INFO(0x9291, F_ASCII, F_NOTYPE, AnyCount, "SubSecTimeOriginal"),
1392
    TAG_INFO(0x9292, F_ASCII, F_NOTYPE, AnyCount, "SubSecTimeDigitized"),
1393
    TAG_INFO(0x9400, F_SRATIONAL, F_NOTYPE, 1, "Temperature"),
1394
    TAG_INFO(0x9401, F_RATIONAL, F_NOTYPE, 1, "Humidity"),
1395
    TAG_INFO(0x9402, F_RATIONAL, F_NOTYPE, 1, "Pressure"),
1396
    TAG_INFO(0x9403, F_SRATIONAL, F_NOTYPE, 1, "WaterDepth"),
1397
    TAG_INFO(0x9404, F_RATIONAL, F_NOTYPE, 1, "Acceleration"),
1398
    TAG_INFO(0x9405, F_SRATIONAL, F_NOTYPE, 1, "CameraElevationAngle "),
1399
    TAG_INFO(0x9C9B, F_BYTE, F_NOTYPE, AnyCount, "WinXP-Title"),     /* Win XP specific, UTF-16 Unicode */
1400
    TAG_INFO(0x9C9C, F_BYTE, F_NOTYPE, AnyCount, "WinXP-Comments"),  /* Win XP specific, UTF-16 Unicode */
1401
    TAG_INFO(0x9C9D, F_BYTE, F_NOTYPE, AnyCount, "WinXP-Author"),    /* Win XP specific, UTF-16 Unicode */
1402
    TAG_INFO(0x9C9E, F_BYTE, F_NOTYPE, AnyCount, "WinXP-Keywords"),  /* Win XP specific, UTF-16 Unicode */
1403
    TAG_INFO(0x9C9F, F_BYTE, F_NOTYPE, AnyCount, "WinXP-Subject"),   /* Win XP specific, UTF-16 Unicode */
1404
    TAG_INFO(0xA000, F_UNDEFINED, F_NOTYPE, 4, "FlashPixVersion"),
1405
    TAG_INFO(0xA001, F_SHORT , F_NOTYPE, 1, "ColorSpace"),
1406
    TAG_INFO(0xA002, F_SHORT | F_LONG /* TBD */, F_SLONG, 1, "PixelXDimension"), /* Was "ExifImageWidth" */
1407
    TAG_INFO(0xA003, F_SHORT | F_LONG /* TBD */, F_SLONG, 1, "PixelYDimension"), /* Was "ExifImageLength" */
1408
    TAG_INFO(0xA004, F_ASCII, F_NOTYPE, 13, "RelatedSoundFile"),
1409
    TAG_INFO(0xA005, F_LONG, F_NOTYPE, 0, "InteroperabilityOffset"), /* Interoperability IFD Pointer */
1410
    TAG_INFO(0xA20B, F_RATIONAL, F_NOTYPE, 1, "FlashEnergy"),
1411
    TAG_INFO(0xA20C, F_UNDEFINED, F_NOTYPE, AnyCount, "SpatialFrequencyResponse"),
1412
    TAG_INFO(0xA20D, F_NOTYPE, F_NOTYPE, 0, "Noise"),
1413
    TAG_INFO(0xA20E, F_RATIONAL, F_NOTYPE, 1, "FocalPlaneXResolution"),
1414
    TAG_INFO(0xA20F, F_RATIONAL, F_NOTYPE, 1, "FocalPlaneYResolution"),
1415
    TAG_INFO(0xA210, F_SHORT, F_NOTYPE, 1, "FocalPlaneResolutionUnit"),
1416
    TAG_INFO(0xA211, F_NOTYPE, F_NOTYPE, 0, "ImageNumber"),
1417
    TAG_INFO(0xA212, F_NOTYPE, F_NOTYPE, 0, "SecurityClassification"),
1418
    TAG_INFO(0xA213, F_NOTYPE, F_NOTYPE, 0, "ImageHistory"),
1419
    TAG_INFO(0xA214, F_SHORT, F_NOTYPE, 2, "SubjectLocation"),
1420
    TAG_INFO(0xA215, F_RATIONAL, F_NOTYPE, 1, "ExposureIndex"),
1421
    TAG_INFO(0xA216, F_NOTYPE, F_NOTYPE, 0, "TIFF_EPStandardID"),
1422
    TAG_INFO(0xA217, F_SHORT, F_BYTE, 1, "SensingMethod"),
1423
    TAG_INFO(0xA300, F_UNDEFINED, F_BYTE, AnyCount /* varies (0,1,2,3), TBD */, "FileSource"),
1424
    TAG_INFO(0xA301, F_UNDEFINED, F_BYTE, 1, "SceneType"),
1425
    TAG_INFO(0xA302, F_UNDEFINED, F_NOTYPE, AnyCount, "CFAPattern"),
1426
    TAG_INFO(0xA401, F_SHORT, F_NOTYPE, 1, "CustomRendered"),
1427
    TAG_INFO(0xA402, F_SHORT, F_NOTYPE, 1, "ExposureMode"),
1428
    TAG_INFO(0xA403, F_SHORT, F_NOTYPE, 1, "WhiteBalance"),
1429
    TAG_INFO(0xA404, F_RATIONAL, F_NOTYPE, 1, "DigitalZoomRatio"),
1430
    TAG_INFO(0xA405, F_SHORT, F_NOTYPE, 1, "FocalLengthIn35mmFilm"),
1431
    TAG_INFO(0xA406, F_SHORT, F_NOTYPE, 1, "SceneCaptureType"),
1432
    TAG_INFO(0xA407, F_SHORT, F_NOTYPE, 1, "GainControl"),
1433
    TAG_INFO(0xA408, F_SHORT, F_NOTYPE, 1, "Contrast"),
1434
    TAG_INFO(0xA409, F_SHORT, F_NOTYPE, 1, "Saturation"),
1435
    TAG_INFO(0xA40A, F_SHORT, F_NOTYPE, 1, "Sharpness"),
1436
    TAG_INFO(0xA40B, F_UNDEFINED, F_NOTYPE, AnyCount, "DeviceSettingDescription"),
1437
    TAG_INFO(0xA40C, F_SHORT, F_NOTYPE, 1, "SubjectDistanceRange"),
1438
    TAG_INFO(0xA420, F_ASCII, F_NOTYPE, 33, "ImageUniqueID"),
1439
    TAG_INFO(0xA430, F_ASCII, F_NOTYPE, AnyCount, "CameraOwnerName"),
1440
    TAG_INFO(0xA431, F_ASCII, F_NOTYPE, AnyCount, "BodySerialNumber"),
1441
    TAG_INFO(0xA432, F_RATIONAL, F_NOTYPE, 4, "LensSpecification"),
1442
    TAG_INFO(0xA433, F_ASCII, F_NOTYPE, AnyCount, "LensMake"),
1443
    TAG_INFO(0xA434, F_ASCII, F_NOTYPE, AnyCount, "LensModel"),
1444
    TAG_INFO(0xA435, F_ASCII, F_NOTYPE, AnyCount, "LensSerialNumber"),
1445
    TAG_INFO(0xA436, F_ASCII, F_NOTYPE, AnyCount, "ImageTitle"),
1446
    TAG_INFO(0xA437, F_ASCII, F_NOTYPE, AnyCount, "Photographer"),
1447
    TAG_INFO(0xA438, F_ASCII, F_NOTYPE, AnyCount, "ImageEditor"),
1448
    TAG_INFO(0xA439, F_ASCII, F_NOTYPE, AnyCount, "CameraFirmware"),
1449
    TAG_INFO(0xA43A, F_ASCII, F_NOTYPE, AnyCount, "RAWDevelopingSoftware"),
1450
    TAG_INFO(0xA43B, F_ASCII, F_NOTYPE, AnyCount, "ImageEditingSoftware"),
1451
    TAG_INFO(0xA43C, F_ASCII, F_NOTYPE, AnyCount, "MetadataEditingSoftware"),
1452
    TAG_INFO(0xA460, F_SHORT, F_NOTYPE, 1, "CompositeImage"),
1453
    TAG_INFO(0xA461, F_SHORT, F_NOTYPE, 2, "SourceImageNumberOfCompositeImage"),
1454
    TAG_INFO(0xA462, F_UNDEFINED, F_NOTYPE, AnyCount, "SourceExposureTimesOfCompositeImage"),
1455
    TAG_INFO(0xA500, F_RATIONAL, F_NOTYPE, 1, "Gamma")
1456
  };
1457
1458
/* Set to non-zero in order to use bsearch rather than linear search. */
1459
#if !defined(EXIF_USE_BSEARCH)
1460
#define EXIF_USE_BSEARCH 1
1461
#endif /* !defined(EXIF_USE_BSEARCH) */
1462
1463
#if EXIF_USE_BSEARCH
1464
/*
1465
  Compare two IFDTagTableType entries by tag
1466
*/
1467
static int IFDTagTableTypeCompare(const void *l, const void *r)
1468
542k
{
1469
542k
  const IFDTagTableType * restrict lp = l;
1470
542k
  const IFDTagTableType * restrict rp = r;
1471
542k
  int sense;
1472
542k
  unsigned int lptag = lp->tag;
1473
542k
  unsigned int rptag = rp->tag;
1474
1475
542k
  if (lptag > rptag)
1476
307k
    sense = 1;
1477
235k
  else if (lptag < rptag)
1478
195k
    sense = -1;
1479
39.3k
  else
1480
39.3k
    sense = 0;
1481
1482
542k
  return sense;
1483
542k
}
1484
#endif /* if EXIF_USE_BSEARCH */
1485
1486
/*
1487
  Find EXIF table entry matching tag
1488
*/
1489
static const IFDTagTableType *
1490
FindEXIFTableEntryByTag(const unsigned int t,
1491
                        const IFDTagTableType *tag_table,
1492
                        const size_t tag_table_entries) MAGICK_FUNC_PURE;
1493
1494
static const IFDTagTableType *
1495
FindEXIFTableEntryByTag(const unsigned int t,
1496
                        const IFDTagTableType *tag_table,
1497
                        const size_t tag_table_entries)
1498
69.7k
{
1499
69.7k
  const IFDTagTableType *key_p = (IFDTagTableType *) NULL;
1500
1501
69.7k
#if EXIF_USE_BSEARCH
1502
69.7k
  IFDTagTableType key;
1503
1504
69.7k
  key.tag = t;
1505
1506
69.7k
  key_p=(void *) bsearch((const void *) &key,(void *) tag_table,tag_table_entries,
1507
69.7k
                         sizeof(tag_table[0]),IFDTagTableTypeCompare);
1508
#else
1509
  register unsigned int
1510
    ttt;
1511
1512
  size_t
1513
    i;
1514
1515
  for (i=0; i < tag_table_entries; i++)
1516
    {
1517
      ttt = tag_table[i].tag;
1518
      if (ttt == t)
1519
        {
1520
          key_p = &tag_table[i];
1521
          break;
1522
        }
1523
      else if (ttt > t)
1524
        {
1525
          break;
1526
        }
1527
    }
1528
#endif /* if EXIF_USE_BSEARCH */
1529
1530
69.7k
  return key_p;
1531
69.7k
}
1532
1533
/*
1534
  Convert an EXIF IFD tag ID to a tag description
1535
1536
  An EXIF IFD tag value to be translated, and a buffer of at least
1537
  MaxTextExtent length are passed as arguments.  For convenience, the
1538
  converted string is returned.
1539
*/
1540
1541
static const char *
1542
EXIFIfdTagToDescription(unsigned int t, char *tag_description)
1543
0
{
1544
0
  const IFDTagTableType *key_p;
1545
1546
0
  key_p = FindEXIFTableEntryByTag(t, tag_table, ArraySize(tag_table));
1547
0
  if (key_p != (IFDTagTableType *) NULL)
1548
0
    {
1549
0
      (void) strlcpy(tag_description,key_p->description,MaxTextExtent);
1550
0
      return tag_description;
1551
0
    }
1552
#if 0
1553
  unsigned int
1554
    i;
1555
1556
  for (i=0; i < ArraySize(tag_table); i++)
1557
    {
1558
      if (tag_table[i].tag == t)
1559
        {
1560
          (void) strlcpy(tag_description,tag_table[i].description,MaxTextExtent);
1561
          return tag_description;
1562
        }
1563
      else if (tag_table[i].tag > t)
1564
        {
1565
          break;
1566
        }
1567
    }
1568
#endif
1569
1570
0
  FormatString(tag_description,"0x%04X",t);
1571
0
  return tag_description;
1572
0
}
1573
1574
/*
1575
  Convert an EXIF IFD tag description to a tag ID.
1576
*/
1577
static int
1578
EXIFIfdTagDescriptionToTagId(const char *description)
1579
13.5k
{
1580
13.5k
  unsigned int
1581
13.5k
    i;
1582
1583
596k
  for (i=0; i < sizeof(tag_table)/sizeof(tag_table[0]); i++)
1584
596k
    if (LocaleCompare(tag_table[i].description,description) == 0)
1585
13.5k
      return tag_table[i].tag;
1586
1587
0
  return -1;
1588
13.5k
}
1589
1590
/*
1591
  Convert a TIFF IFD field value type to string description
1592
*/
1593
static const char *
1594
EXIFIfdFieldTypeToStr(unsigned int f)
1595
0
{
1596
0
  const char
1597
0
    *str="unknown";
1598
1599
0
  switch (f)
1600
0
    {
1601
0
    case EXIF_FMT_BYTE:
1602
0
      str="BYTE";
1603
0
      break;
1604
0
    case EXIF_FMT_ASCII:
1605
0
      str="STRING";
1606
0
      break;
1607
0
    case EXIF_FMT_USHORT:
1608
0
      str="SHORT";
1609
0
      break;
1610
0
    case EXIF_FMT_ULONG:
1611
0
      str="LONG";
1612
0
      break;
1613
0
    case EXIF_FMT_URATIONAL:
1614
0
      str="RATIONAL";
1615
0
      break;
1616
0
    case EXIF_FMT_SBYTE:
1617
0
      str="SBYTE";
1618
0
      break;
1619
0
    case EXIF_FMT_UNDEFINED:
1620
0
      str="UNDEFINED";
1621
0
      break;
1622
0
    case EXIF_FMT_SSHORT:
1623
0
      str="SSHORT";
1624
0
      break;
1625
0
    case EXIF_FMT_SLONG:
1626
0
      str="SLONG";
1627
0
      break;
1628
0
    case EXIF_FMT_SRATIONAL:
1629
0
      str="SRATIONAL";
1630
0
      break;
1631
0
    case EXIF_FMT_SINGLE:
1632
0
      str="SINGLE";
1633
0
      break;
1634
0
    case EXIF_FMT_DOUBLE:
1635
0
      str="DOUBLE";
1636
0
      break;
1637
0
    }
1638
1639
0
  return str;
1640
0
}
1641
1642
static const unsigned int
1643
  format_bytes[] =
1644
  {
1645
    0,
1646
    1, /* BYTE */
1647
    1, /* STRING / ASCII */
1648
    2, /* USHORT */
1649
    4, /* ULONG */
1650
    8, /* URATIONAL */
1651
    1, /* SBYTE */
1652
    1, /* UNDEFINED */
1653
    2, /* SSHORT */
1654
    4, /* SLONG */
1655
    8, /* SRATIONAL */
1656
    4, /* SINGLE / FLOAT */
1657
    8  /* DOUBLE */
1658
  };
1659
1660
/*
1661
  Translate from EXIF_FMT_FOO to F_FOO bit-field flag.
1662
*/
1663
static unsigned int ExifFmtToFmtBits(unsigned int f)
1664
61.1k
{
1665
61.1k
  unsigned int
1666
61.1k
    bits;
1667
1668
61.1k
    switch (f)
1669
61.1k
    {
1670
0
    case EXIF_FMT_NOTYPE:
1671
0
    default:
1672
0
      bits=F_NOTYPE;
1673
0
      break;
1674
6.96k
    case EXIF_FMT_BYTE:
1675
6.96k
      bits=F_BYTE;
1676
6.96k
      break;
1677
3.95k
    case EXIF_FMT_ASCII:
1678
3.95k
      bits=F_ASCII;
1679
3.95k
      break;
1680
3.39k
    case EXIF_FMT_USHORT:
1681
3.39k
      bits=F_SHORT;
1682
3.39k
      break;
1683
13.5k
    case EXIF_FMT_ULONG:
1684
13.5k
      bits=F_LONG;
1685
13.5k
      break;
1686
2.74k
    case EXIF_FMT_URATIONAL:
1687
2.74k
      bits=F_RATIONAL;
1688
2.74k
      break;
1689
2.75k
    case EXIF_FMT_SBYTE:
1690
2.75k
      bits=F_SBYTE;
1691
2.75k
      break;
1692
5.16k
    case EXIF_FMT_UNDEFINED:
1693
5.16k
      bits=F_UNDEFINED;
1694
5.16k
      break;
1695
1.22k
    case EXIF_FMT_SSHORT:
1696
1.22k
      bits=F_SSHORT;
1697
1.22k
      break;
1698
5.34k
    case EXIF_FMT_SLONG:
1699
5.34k
      bits=F_SLONG;
1700
5.34k
      break;
1701
1.72k
    case EXIF_FMT_SRATIONAL:
1702
1.72k
      bits=F_SRATIONAL;
1703
1.72k
      break;
1704
1.79k
    case EXIF_FMT_SINGLE:
1705
1.79k
      bits=F_SINGLE;
1706
1.79k
      break;
1707
12.6k
    case EXIF_FMT_DOUBLE:
1708
12.6k
      bits=F_DOUBLE;
1709
12.6k
      break;
1710
61.1k
    }
1711
61.1k
    return bits;
1712
61.1k
}
1713
1714
/*
1715
  Get the expected bit-field bits for format and use it to verify if
1716
  it is an expected format (a bit matches).
1717
*/
1718
typedef enum
1719
  {
1720
    ExifFmtGood,
1721
    ExifFmtFishy,
1722
    ExifFmtBad
1723
  } ExifFmtStatus;
1724
1725
static ExifFmtStatus ValidateExifFmtAgainstBitField(const IFDTagTableType *key_p,const unsigned int fmt)
1726
39.3k
{
1727
39.3k
  MagickPassFail
1728
39.3k
    status;
1729
1730
39.3k
  if (key_p->format & ExifFmtToFmtBits(fmt))
1731
17.5k
    {
1732
      /* Totally good! */
1733
17.5k
      status=ExifFmtGood;
1734
17.5k
    }
1735
21.8k
  else if ((key_p->format | key_p->format_alt) & ExifFmtToFmtBits(fmt))
1736
2.14k
    {
1737
      /* Fishy */
1738
2.14k
      status=ExifFmtFishy;
1739
2.14k
    }
1740
19.6k
  else
1741
19.6k
    {
1742
      /* Bad */
1743
19.6k
      status=ExifFmtBad;
1744
19.6k
    }
1745
39.3k
  return status;
1746
39.3k
}
1747
1748
static magick_int16_t
1749
Read16s(int morder,unsigned char *ishort)
1750
497
{
1751
497
  union
1752
497
  {
1753
497
    magick_uint16_t u;
1754
497
    magick_int16_t s;
1755
497
  } value;
1756
1757
497
  if (morder)
1758
155
    value.u=((magick_uint16_t) ishort[0] << 8) | ishort[1];
1759
342
  else
1760
342
    value.u=((magick_uint16_t) ishort[1] << 8) | ishort[0];
1761
497
  return(value.s);
1762
497
}
1763
1764
static magick_uint16_t
1765
Read16u(int morder,unsigned char *ishort)
1766
300k
{
1767
300k
  magick_uint16_t
1768
300k
    value;
1769
1770
300k
  if (morder)
1771
247k
    value=((magick_uint16_t) ishort[0] << 8)
1772
247k
      | (magick_uint16_t) ishort[1];
1773
52.9k
  else
1774
52.9k
    value=((magick_uint16_t) ishort[1] << 8)
1775
52.9k
      | (magick_uint16_t) ishort[0];
1776
300k
  return(value);
1777
300k
}
1778
1779
static magick_int32_t
1780
Read32s(int morder,unsigned char *ilong)
1781
1.90k
{
1782
1.90k
  union
1783
1.90k
  {
1784
1.90k
    magick_uint32_t u;
1785
1.90k
    magick_int32_t s;
1786
1.90k
  } value;
1787
1788
1.90k
  if (morder)
1789
1.67k
    value.u=((magick_uint32_t) ilong[0] << 24)
1790
1.67k
      | ((magick_uint32_t) ilong[1] << 16)
1791
1.67k
      | ((magick_uint32_t) ilong[2] << 8)
1792
1.67k
      | ((magick_uint32_t) ilong[3]);
1793
224
  else
1794
224
    value.u=((magick_uint32_t) ilong[3] << 24)
1795
224
      | ((magick_uint32_t) ilong[2] << 16)
1796
224
      | ((magick_uint32_t) ilong[1] << 8 )
1797
224
      | ((magick_uint32_t) ilong[0]);
1798
1.90k
  return(value.s);
1799
1.90k
}
1800
1801
static magick_uint32_t
1802
Read32u(int morder, unsigned char *ilong)
1803
168k
{
1804
168k
  magick_uint32_t
1805
168k
    value;
1806
1807
168k
  if (morder)
1808
142k
    value=((magick_uint32_t) ilong[0] << 24)
1809
142k
      | ((magick_uint32_t) ilong[1] << 16)
1810
142k
      | ((magick_uint32_t) ilong[2] << 8)
1811
142k
      | ((magick_uint32_t) ilong[3]);
1812
26.5k
  else
1813
26.5k
    value=((magick_uint32_t) ilong[3] << 24)
1814
26.5k
      | ((magick_uint32_t) ilong[2] << 16)
1815
26.5k
      | ((magick_uint32_t) ilong[1] << 8 )
1816
26.5k
      | ((magick_uint32_t) ilong[0]);
1817
1818
168k
  return value;
1819
168k
}
1820
1821
static void
1822
Write16u(int morder, void *location, magick_uint16_t value)
1823
12
{
1824
12
  char
1825
12
    *pval;
1826
1827
12
  pval = (char *)location;
1828
12
  if (morder)
1829
0
    {
1830
0
      *pval++ = (char)((value >> 8) & 0xff);
1831
0
      *pval++ = (char)(value & 0xff);
1832
0
    }
1833
12
  else
1834
12
    {
1835
12
      *pval++ = (char)(value & 0xff);
1836
12
      *pval++ = (char)((value >> 8) & 0xff);
1837
12
    }
1838
12
}
1839
1840
static int
1841
GenerateEXIFAttribute(Image *image,const char *specification)
1842
352k
{
1843
352k
  char
1844
352k
    *final,
1845
352k
    *key,
1846
352k
    tag_description[MaxTextExtent],
1847
352k
    *value;
1848
1849
352k
  int
1850
352k
    id,
1851
352k
    morder,
1852
352k
    all;
1853
1854
352k
  int
1855
352k
    tag;
1856
1857
352k
  register size_t
1858
352k
    i;
1859
1860
352k
  size_t
1861
352k
    length;
1862
1863
352k
  unsigned int
1864
352k
    level,
1865
352k
    offset;
1866
1867
352k
  unsigned int
1868
352k
    gpsoffset;
1869
1870
352k
  unsigned char
1871
352k
    *tiffp,
1872
352k
    *tiffp_max,
1873
352k
    *ifdstack[DE_STACK_SIZE],
1874
352k
    *ifdp,
1875
352k
    *info;
1876
1877
352k
  unsigned int
1878
352k
    de,
1879
352k
    destack[DE_STACK_SIZE],
1880
352k
    nde;
1881
1882
352k
  const unsigned char
1883
352k
    *profile_info;
1884
1885
352k
  size_t
1886
352k
    profile_length;
1887
1888
352k
  MagickBool
1889
352k
    logging,
1890
352k
    gpsfoundstack[DE_STACK_SIZE],
1891
352k
    gpsfound;
1892
1893
352k
  MagickBool
1894
352k
    debug=MagickFalse;
1895
1896
352k
  assert((ArraySize(format_bytes)-1) == EXIF_NUM_FORMATS);
1897
352k
  logging=IsEventLogged(TransformEvent);
1898
352k
  {
1899
352k
    const char *
1900
352k
      env_value;
1901
1902
    /*
1903
      Allow enabling debug of EXIF tags
1904
    */
1905
352k
    if ((env_value=getenv("MAGICK_DEBUG_EXIF")))
1906
0
      {
1907
0
        if (LocaleCompare(env_value,"TRUE") == 0)
1908
0
          debug=MagickTrue;
1909
0
      }
1910
352k
  }
1911
352k
  gpsfound=MagickFalse;
1912
352k
  gpsoffset=0;
1913
  /*
1914
    Determine if there is any EXIF data available in the image.
1915
  */
1916
352k
  value=(char *) NULL;
1917
352k
  final=AllocateString("");
1918
352k
  profile_info=GetImageProfile(image,"EXIF",&profile_length);
1919
352k
  if (profile_info == 0)
1920
338k
    {
1921
338k
      if (logging)
1922
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1923
0
                              "No EXIF profile present");
1924
338k
      goto generate_attribute_failure;
1925
338k
    }
1926
13.5k
  if (logging && debug)
1927
0
    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1928
0
                          "EXIF: profile_info=%p, profile_length=%" MAGICK_SIZE_T_F "u",
1929
0
                          profile_info, (MAGICK_SIZE_T) profile_length);
1930
  /*
1931
    If EXIF data exists, then try to parse the request for a tag in
1932
    the form "EXIF:key".
1933
  */
1934
13.5k
  key=(char *) NULL;
1935
13.5k
  if (strlen(specification) > 5)
1936
13.5k
    {
1937
13.5k
      key=(char *) &specification[5]; /* "EXIF:key" */
1938
13.5k
    }
1939
0
  else
1940
0
    {
1941
0
      if (logging)
1942
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1943
0
                              "No EXIF:key found");
1944
0
      goto generate_attribute_failure;
1945
0
    }
1946
13.5k
  while (isspace((int) (*key)))
1947
0
    key++;
1948
13.5k
  all=0;
1949
13.5k
  tag=(-1);
1950
13.5k
  switch(*key)
1951
13.5k
    {
1952
      /*
1953
        Caller has asked for all the tags in the EXIF data.
1954
      */
1955
0
    case '*':
1956
0
      {
1957
0
        tag=0;
1958
0
        all=1; /* return the data in description=value format */
1959
0
        break;
1960
0
      }
1961
0
    case '!':
1962
0
      {
1963
0
        tag=0;
1964
0
        all=2; /* return the data in tageid=value format */
1965
0
        break;
1966
0
      }
1967
      /*
1968
        Check for a hex based tag specification first.
1969
      */
1970
0
    case '#':
1971
0
      {
1972
0
        char
1973
0
          c;
1974
1975
0
        size_t
1976
0
          n;
1977
1978
0
        tag=0;
1979
0
        key++;
1980
0
        n=strlen(key);
1981
0
        if (n != 4)
1982
0
          {
1983
0
            if (logging)
1984
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1985
0
                                    "EXIF: Hex tag not 4 bytes");
1986
0
            goto generate_attribute_failure;
1987
0
          }
1988
0
        else
1989
0
          {
1990
            /*
1991
              Parse tag specification as a hex number.
1992
            */
1993
0
            n/=4;
1994
0
            do
1995
0
              {
1996
0
                for (i=n; i > 0; i--)
1997
0
                  {
1998
0
                    c=(*key++);
1999
0
                    tag<<=4;
2000
0
                    if ((c >= '0') && (c <= '9'))
2001
0
                      tag|=c-'0';
2002
0
                    else
2003
0
                      if ((c >= 'A') && (c <= 'F'))
2004
0
                        tag|=c-('A'-10);
2005
0
                      else
2006
0
                        if ((c >= 'a') && (c <= 'f'))
2007
0
                          tag|=c-('a'-10);
2008
0
                        else
2009
0
                          {
2010
0
                            if (logging)
2011
0
                              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2012
0
                                                    "EXIF: Failed to parse hex tag");
2013
0
                            goto generate_attribute_failure;
2014
0
                          }
2015
0
                  }
2016
0
              } while (*key != '\0');
2017
0
          }
2018
0
        break;
2019
0
      }
2020
13.5k
    default:
2021
13.5k
      {
2022
        /*
2023
          Try to match the text with a tag name instead.
2024
        */
2025
13.5k
        tag=EXIFIfdTagDescriptionToTagId(key);
2026
13.5k
        if (logging && debug)
2027
0
          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2028
0
                                "EXIF: Found tag %d for key \"%s\"",tag,key);
2029
13.5k
        break;
2030
0
      }
2031
13.5k
    }
2032
13.5k
  if (tag < 0)
2033
0
    {
2034
0
      if (logging)
2035
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2036
0
                              "EXIF: Negative tag value!");
2037
0
      goto generate_attribute_failure;;
2038
0
    }
2039
13.5k
  length=profile_length;
2040
13.5k
  info=(unsigned char *) profile_info;
2041
9.62M
  while (length != 0)
2042
9.62M
    {
2043
9.62M
      if (ReadByte(&info,&length) != 0x45)
2044
9.57M
        continue;
2045
53.1k
      if (ReadByte(&info,&length) != 0x78)
2046
17.1k
        continue;
2047
35.9k
      if (ReadByte(&info,&length) != 0x69)
2048
4.55k
        continue;
2049
31.3k
      if (ReadByte(&info,&length) != 0x66)
2050
2.77k
        continue;
2051
28.5k
      if (ReadByte(&info,&length) != 0x00)
2052
15.7k
        continue;
2053
12.8k
      if (ReadByte(&info,&length) != 0x00)
2054
2.60k
        continue;
2055
10.2k
      break;
2056
12.8k
    }
2057
13.5k
  if (length < 16)
2058
3.61k
    {
2059
3.61k
      if (logging)
2060
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2061
0
                              "EXIF: Tag length length < 16 (have %" MAGICK_SIZE_T_F "u )",
2062
0
                              (MAGICK_SIZE_T)length);
2063
3.61k
      goto generate_attribute_failure;
2064
3.61k
    }
2065
9.94k
  tiffp=info;
2066
9.94k
  tiffp_max=tiffp+length;
2067
9.94k
  id=Read16u(0,tiffp);
2068
9.94k
  morder=0;
2069
9.94k
  if (id == 0x4949) /* LSB */
2070
2.09k
    morder=0;
2071
7.85k
  else
2072
7.85k
    if (id == 0x4D4D) /* MSB */
2073
7.18k
      morder=1;
2074
671
    else
2075
671
      {
2076
671
        if (logging)
2077
0
          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2078
0
                                "EXIF: Unknown byte order (%04x)",
2079
0
                                morder);
2080
671
        goto generate_attribute_failure;
2081
671
      }
2082
9.27k
  if (Read16u(morder,tiffp+2) != 0x002a)
2083
166
    {
2084
166
      if (logging)
2085
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2086
0
                              "EXIF: Expected 0x002a!");
2087
166
      goto generate_attribute_failure;
2088
166
    }
2089
  /*
2090
    This is the offset to the first IFD.
2091
  */
2092
9.10k
  offset=Read32u(morder,tiffp+4);
2093
9.10k
  if (offset >= length)
2094
263
    {
2095
263
      if (logging)
2096
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2097
0
                              "EXIF: Offset (%u) is > length (%" MAGICK_SIZE_T_F "u)!",
2098
0
                              offset, (MAGICK_SIZE_T) length);
2099
263
      goto generate_attribute_failure;
2100
263
    }
2101
  /*
2102
    Set the pointer to the first IFD and follow it were it leads.
2103
  */
2104
8.84k
  ifdp=tiffp+offset;
2105
8.84k
  level=0;
2106
8.84k
  de=0U;
2107
8.84k
  do
2108
26.9k
    {
2109
      /*
2110
        If there is anything on the stack then pop it off.
2111
      */
2112
26.9k
      if (level > 0)
2113
18.0k
        {
2114
18.0k
          level--;
2115
18.0k
          ifdp=ifdstack[level];
2116
18.0k
          de=destack[level];
2117
18.0k
          gpsfound=gpsfoundstack[level];
2118
18.0k
        }
2119
      /*
2120
        Determine how many entries there are in the current IFD.
2121
        Limit the number of entries parsed to MAX_TAGS_PER_IFD.
2122
      */
2123
26.9k
      if ((ifdp < tiffp) || (ifdp+2 > tiffp_max))
2124
64
        {
2125
64
          if (logging)
2126
0
            (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2127
0
                                  "EXIF: ifdp out of range!");
2128
64
          goto generate_attribute_failure;
2129
64
        }
2130
26.8k
      nde=Read16u(morder,ifdp);
2131
26.8k
      if (logging && debug)
2132
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2133
0
                              "EXIF: IFD at offset %" MAGICK_SSIZE_T_F "d has %u tags",
2134
0
                              (MAGICK_SSIZE_T) (ifdp-tiffp), nde);
2135
26.8k
      if (nde > MAX_TAGS_PER_IFD)
2136
6.78k
        {
2137
6.78k
          nde=MAX_TAGS_PER_IFD;
2138
6.78k
          if (logging && debug)
2139
0
            (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2140
0
                                  "EXIF: Limiting IFD at offset %" MAGICK_SSIZE_T_F "d to %u tags!",
2141
0
                                  (MAGICK_SSIZE_T) (ifdp-tiffp), nde);
2142
6.78k
        }
2143
128k
      for (; de < nde; de++)
2144
126k
        {
2145
126k
          size_t
2146
126k
            n;
2147
2148
126k
          unsigned int
2149
126k
            c,
2150
126k
            f,
2151
126k
            t;
2152
2153
126k
          unsigned char
2154
126k
            *pde,
2155
126k
            *pval;
2156
2157
126k
          pde=(unsigned char *) (ifdp+2+(12*(size_t) de));
2158
126k
          if (logging && debug)
2159
0
            (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2160
0
                                  "EXIF: PDE offset %" MAGICK_SSIZE_T_F "d", (MAGICK_SSIZE_T) (pde-ifdp));
2161
126k
          if ((pde < tiffp) || (pde + 12 > tiffp_max))
2162
2.56k
            {
2163
2.56k
              if (logging)
2164
0
                (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2165
0
                                      "EXIF: Invalid Exif, entry is beyond metadata limit.");
2166
2.56k
              goto generate_attribute_failure;
2167
2.56k
            }
2168
124k
          t=Read16u(morder,pde); /* get tag value */
2169
124k
          f=Read16u(morder,pde+2); /* get the format */
2170
124k
          if (logging && debug)
2171
0
            (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2172
0
                                  "EXIF: Tag %u, Format %u", t, f);
2173
124k
          if (((size_t) f >= ArraySize(format_bytes)) || (format_bytes[f] == 0))
2174
9.19k
            {
2175
9.19k
              if (logging)
2176
0
                (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2177
0
                                      "EXIF: Invalid Exif, unsupported format %u.",(unsigned) f);
2178
9.19k
              break;
2179
9.19k
            }
2180
114k
          c=Read32u(morder,pde+4); /* get number of components */
2181
114k
          n=MagickArraySize(c,format_bytes[f]);
2182
114k
          if (logging && debug)
2183
0
            {
2184
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2185
0
                                    "EXIF: %u components, %u bytes per component", c,format_bytes[f]);
2186
0
            }
2187
114k
          if ((c > length) || (n > length) || ((n == 0) && (c != 0) && (format_bytes[f] != 0)))
2188
2.56k
            {
2189
2.56k
              if (logging)
2190
0
                (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2191
0
                                      "EXIF: Invalid Exif, too many components (%u components).",c);
2192
2.56k
              goto generate_attribute_failure;
2193
2.56k
            }
2194
112k
          if (n <= 4)
2195
82.9k
            {
2196
82.9k
              pval=(unsigned char *) pde+8;
2197
82.9k
            }
2198
29.5k
          else
2199
29.5k
            {
2200
29.5k
              size_t
2201
29.5k
                oval;
2202
2203
              /*
2204
                The directory entry contains an offset.
2205
              */
2206
29.5k
              oval=Read32u(morder,pde+8);
2207
29.5k
              if ((oval > length) || ((oval+n) > length)) /* Impossibly long! */
2208
17.7k
                {
2209
17.7k
                  if (logging && debug)
2210
0
                    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2211
0
                                          "EXIF: Invalid Exif directory entry!"
2212
0
                                          " (offset %" MAGICK_SIZE_T_F "u, %" MAGICK_SIZE_T_F "u components)",
2213
0
                                          (MAGICK_SIZE_T) oval, (MAGICK_SIZE_T) n);
2214
17.7k
                  continue;
2215
17.7k
                }
2216
11.7k
              pval=(unsigned char *)(tiffp+oval);
2217
11.7k
            }
2218
2219
94.7k
          if ((pval < tiffp) || (pval > tiffp_max))
2220
0
            {
2221
0
              if (logging)
2222
0
                (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2223
0
                                      "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2224
0
                                      (MAGICK_SSIZE_T) (pval-tiffp));
2225
0
              goto generate_attribute_failure;
2226
0
            }
2227
2228
94.7k
          if (gpsfound)
2229
19.8k
            {
2230
19.8k
              if (/* (t < GPS_TAG_START) || */ (t > GPS_TAG_STOP))
2231
15.9k
                {
2232
15.9k
                  if (logging & debug)
2233
0
                    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2234
0
                                          "EXIF: Skipping bogus GPS IFD tag %d ...",t);
2235
15.9k
                  continue;
2236
15.9k
                }
2237
19.8k
            }
2238
74.8k
          else
2239
74.8k
            {
2240
74.8k
              if ((t < EXIF_TAG_START) || ( t > EXIF_TAG_STOP))
2241
8.95k
                {
2242
8.95k
                  if (logging & debug)
2243
0
                    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2244
0
                                          "EXIF: Skipping bogus EXIF IFD tag %d ...",t);
2245
8.95k
                  continue;
2246
8.95k
                }
2247
74.8k
            }
2248
2249
          /* if (logging && debug) */
2250
69.7k
          {
2251
69.7k
            const char *ifd_type;
2252
69.7k
            int valid = MagickTrue;
2253
2254
69.7k
            if (gpsfound)
2255
3.85k
              ifd_type="GPS";
2256
65.9k
            else
2257
65.9k
              ifd_type="EXIF";
2258
2259
            /*
2260
              Enable logging via:
2261
2262
              MAGICK_DEBUG=transform MAGICK_DEBUG_EXIF=TRUE gm convert file.jpg -format '%[EXIF:*]' info:-
2263
            */
2264
2265
69.7k
            {
2266
              /*
2267
                For a given IFD type and tag, validate that the claimed format and components are valid.
2268
              */
2269
69.7k
              const IFDTagTableType *key_p = FindEXIFTableEntryByTag(t,tag_table,ArraySize(tag_table));
2270
69.7k
              if (key_p)
2271
39.3k
                {
2272
                  /* Validate format */
2273
39.3k
                  ExifFmtStatus fmt_status = ValidateExifFmtAgainstBitField(key_p,f);
2274
2275
39.3k
                  switch (fmt_status)
2276
39.3k
                    {
2277
17.5k
                    case ExifFmtGood:
2278
17.5k
                      break;
2279
2.14k
                    case ExifFmtFishy:
2280
                      /* Support tracing tags which use the wrong field type */
2281
2.14k
                      if (logging)
2282
0
                        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2283
0
                                              "%s IFD: TagVal=%d (0x%04x) TagDescr=\"%s\" Fishy field type %d (%s)",
2284
0
                                              ifd_type,
2285
0
                                              t,t,
2286
0
                                              EXIFIfdTagToDescription(t,tag_description), f, EXIFIfdFieldTypeToStr(f));
2287
2.14k
                      break;
2288
19.6k
                    case ExifFmtBad:
2289
19.6k
                      valid=MagickFalse;
2290
19.6k
                      break;
2291
39.3k
                    }
2292
2293
                  /* Validate number of components */
2294
39.3k
                  if (!((key_p->count == AnyCount) || (key_p->count <= 0) || (c <= (unsigned int) key_p->count)))
2295
4.18k
                    {
2296
4.18k
                      if (logging)
2297
0
                        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2298
0
                                              "%s IFD: TagVal=%d (0x%04x) TagDescr=\"%s\" Expected %u components (have %u)",
2299
0
                                              ifd_type,
2300
0
                                              t,t,
2301
0
                                              EXIFIfdTagToDescription(t,tag_description), (unsigned int) key_p->count, c);
2302
4.18k
                      valid=MagickFalse;
2303
4.18k
                    }
2304
39.3k
                }
2305
69.7k
            }
2306
2307
69.7k
            if (logging)
2308
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2309
0
                                    "%s IFD: TagVal=%d (0x%04x) TagDescr=\"%s\" Format=%d "
2310
0
                                    "FormatDescr=\"%s\" Components=%u Valid=%s",
2311
0
                                    ifd_type,
2312
0
                                    t,t,
2313
0
                                    EXIFIfdTagToDescription(t,tag_description),f,
2314
0
                                    EXIFIfdFieldTypeToStr(f),c,
2315
0
                                    valid == MagickFail ? "No" : "Yes");
2316
69.7k
            if (valid != MagickTrue)
2317
20.5k
              {
2318
20.5k
                if (logging & debug)
2319
0
                  (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2320
0
                                        "Skipping bogus %s tag %d (0x%04X) ...",ifd_type,t,t);
2321
20.5k
                continue;
2322
20.5k
              }
2323
69.7k
          }
2324
2325
2326
          /*
2327
            Return values for all the tags, or for a specific requested tag.
2328
2329
            Tags from the GPS sub-IFD are in a bit of a chicken and
2330
            egg situation in that the tag for the GPS sub-IFD will not
2331
            be seen unless we pass that tag through so it can be
2332
            processed.  So we pass the GPS_OFFSET tag through, but if
2333
            it was not requested, then we don't return a string value
2334
            for it.
2335
          */
2336
49.2k
          if (all || (tag == (int) t) || (GPS_OFFSET == t))
2337
6.02k
            {
2338
6.02k
              char
2339
6.02k
                s[MaxTextExtent];
2340
2341
6.02k
              switch (f)
2342
6.02k
                {
2343
0
                case EXIF_FMT_SBYTE:
2344
0
                  {
2345
                    /* 8-bit signed integer */
2346
0
                    FormatString(s,"%d",(int) (*(char *) pval));
2347
0
                    value=AllocateString(s);
2348
0
                    break;
2349
0
                  }
2350
0
                case EXIF_FMT_BYTE:
2351
0
                  {
2352
                    /* 8-bit unsigned integer */
2353
0
                    value=MagickAllocateMemory(char *,n+1);
2354
0
                    if (value != (char *) NULL)
2355
0
                      {
2356
0
                        unsigned int
2357
0
                          a;
2358
2359
0
                        for (a=0; a < n; a++)
2360
0
                          {
2361
0
                            value[a]='.';
2362
0
                            if (isprint((int) pval[a]))
2363
0
                              value[a]=pval[a];
2364
0
                          }
2365
0
                        value[a]='\0';
2366
0
                        break;
2367
0
                      }
2368
#if 0
2369
                    printf("format %u, length %u\n",f,n);
2370
                    FormatString(s,"%ld",(long) (*(unsigned char *) pval));
2371
                    value=AllocateString(s);
2372
#endif
2373
0
                    break;
2374
0
                  }
2375
0
                case EXIF_FMT_SSHORT:
2376
0
                  {
2377
                    /* 16-bit signed integer */
2378
0
                    if (((pval < tiffp) || (pval+sizeof(magick_uint16_t)) > tiffp_max))
2379
0
                      {
2380
0
                        if (logging)
2381
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2382
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2383
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2384
0
                        goto generate_attribute_failure;
2385
0
                      }
2386
0
                    FormatString(s,"%hd",Read16u(morder,pval));
2387
0
                    value=AllocateString(s);
2388
0
                    break;
2389
0
                  }
2390
497
                case EXIF_FMT_USHORT:
2391
497
                  {
2392
                    /* 16-bit unsigned integer */
2393
497
                    if ((pval < tiffp) || ((pval+sizeof(magick_uint16_t)) > tiffp_max))
2394
0
                      {
2395
0
                        if (logging)
2396
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2397
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2398
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2399
0
                        goto generate_attribute_failure;
2400
0
                      }
2401
497
                    FormatString(s,"%hu",Read16s(morder,pval));
2402
497
                    value=AllocateString(s);
2403
497
                    break;
2404
497
                  }
2405
3.63k
                case EXIF_FMT_ULONG:
2406
3.63k
                  {
2407
3.63k
                    if ((pval < tiffp) || ((pval+sizeof(magick_uint32_t)) > tiffp_max))
2408
0
                      {
2409
0
                        if (logging)
2410
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2411
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2412
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2413
0
                        goto generate_attribute_failure;
2414
0
                      }
2415
3.63k
                    offset=Read32u(morder,pval);
2416
                    /*
2417
                      Only report value if this tag was requested.
2418
                    */
2419
3.63k
                    if (all || (tag == (int) t))
2420
0
                      {
2421
0
                        FormatString(s,"%u",offset);
2422
0
                        value=AllocateString(s);
2423
0
                      }
2424
3.63k
                    if (GPS_OFFSET == t)
2425
3.63k
                      {
2426
3.63k
                        gpsoffset=offset;
2427
3.63k
                      }
2428
3.63k
                    break;
2429
3.63k
                  }
2430
1.90k
                case EXIF_FMT_SLONG:
2431
1.90k
                  {
2432
1.90k
                    if ((pval < tiffp) || ((pval+sizeof(magick_uint32_t)) > tiffp_max))
2433
0
                      {
2434
0
                        if (logging)
2435
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2436
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2437
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2438
0
                        goto generate_attribute_failure;
2439
0
                      }
2440
1.90k
                    FormatString(s,"%d",(int) Read32s(morder,pval));
2441
1.90k
                    value=AllocateString(s);
2442
1.90k
                    break;
2443
1.90k
                  }
2444
0
                case EXIF_FMT_URATIONAL:
2445
0
                  {
2446
0
                    if (gpsfound &&
2447
0
                        (t == GPS_LATITUDE ||
2448
0
                         t == GPS_LONGITUDE ||
2449
0
                         t == GPS_TIMESTAMP))
2450
0
                      {
2451
0
                        if ((pval < tiffp) || ((pval+6*sizeof(magick_uint32_t)) > tiffp_max))
2452
0
                          {
2453
0
                            if (logging)
2454
0
                              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2455
0
                                                    "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2456
0
                                                    (MAGICK_SSIZE_T) (pval-tiffp));
2457
0
                            goto generate_attribute_failure;
2458
0
                          }
2459
0
                        FormatString(s,"%u/%u,%u/%u,%u/%u"
2460
0
                                     ,(unsigned) Read32u(morder,pval),
2461
0
                                     (unsigned) Read32u(morder,4+pval)
2462
0
                                     ,(unsigned) Read32u(morder,8+pval),
2463
0
                                     (unsigned) Read32u(morder,12+pval)
2464
0
                                     ,(unsigned) Read32u(morder,16+pval),
2465
0
                                     (unsigned) Read32u(morder,20+pval)
2466
0
                                     );
2467
0
                      }
2468
0
                    else
2469
0
                      {
2470
0
                        if ((pval < tiffp) || ((pval+2*sizeof(magick_uint32_t)) > tiffp_max))
2471
0
                          {
2472
0
                            if (logging)
2473
0
                              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2474
0
                                                    "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2475
0
                                                    (MAGICK_SSIZE_T) (pval-tiffp));
2476
0
                            goto generate_attribute_failure;
2477
0
                          }
2478
0
                        FormatString(s,"%u/%u"
2479
0
                                     ,(unsigned) Read32u(morder,pval),
2480
0
                                     (unsigned) Read32u(morder,4+pval)
2481
0
                                     );
2482
0
                      }
2483
0
                    value=AllocateString(s);
2484
0
                    break;
2485
0
                  }
2486
0
                case EXIF_FMT_SRATIONAL:
2487
0
                  {
2488
0
                    if ((pval < tiffp) || ((pval+2*sizeof(magick_uint32_t)) > tiffp_max))
2489
0
                      {
2490
0
                        if (logging)
2491
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2492
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2493
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2494
0
                        goto generate_attribute_failure;
2495
0
                      }
2496
0
                    FormatString(s,"%d/%d",(int) Read32s(morder,pval),
2497
0
                                 (int) Read32s(morder,4+pval));
2498
0
                    value=AllocateString(s);
2499
0
                    break;
2500
0
                  }
2501
0
                case EXIF_FMT_SINGLE:
2502
0
                  {
2503
0
                    float fval;
2504
0
                    if ((pval < tiffp) || ((pval+sizeof(float)) > tiffp_max))
2505
0
                      {
2506
0
                        if (logging)
2507
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2508
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2509
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2510
0
                        goto generate_attribute_failure;
2511
0
                      }
2512
0
                    (void) memcpy(&fval,pval,sizeof(fval));
2513
0
                    FormatString(s,"%f",(double) fval);
2514
0
                    value=AllocateString(s);
2515
0
                    break;
2516
0
                  }
2517
0
                case EXIF_FMT_DOUBLE:
2518
0
                  {
2519
0
                    double dval;
2520
0
                    if ((pval < tiffp) || ((pval+sizeof(double)) > tiffp_max))
2521
0
                      {
2522
0
                        if (logging)
2523
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2524
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2525
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2526
0
                        goto generate_attribute_failure;
2527
0
                      }
2528
0
                    (void) memcpy(&dval,pval,sizeof(dval));
2529
0
                    FormatString(s,"%f",dval);
2530
0
                    value=AllocateString(s);
2531
0
                    break;
2532
0
                  }
2533
0
                default:
2534
0
                case EXIF_FMT_UNDEFINED:
2535
0
                case EXIF_FMT_ASCII:
2536
0
                  {
2537
0
                    unsigned int
2538
0
                      a;
2539
2540
0
                    size_t
2541
0
                      allocation_size;
2542
2543
0
                    MagickBool
2544
0
                      binary=MagickFalse;
2545
2546
0
                    if (logging)
2547
0
                      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2548
0
                                            "EXIF: pval=%p (offset=%" MAGICK_SSIZE_T_F "d), n=%" MAGICK_SIZE_T_F "u",
2549
0
                                            pval, (MAGICK_SSIZE_T) (pval-tiffp), (MAGICK_SIZE_T) n);
2550
2551
0
                    if ((pval < tiffp) || (pval+n) > tiffp_max)
2552
0
                      {
2553
0
                        if (logging)
2554
0
                          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2555
0
                                                "EXIF: Offset %" MAGICK_SSIZE_T_F "d out of valid range!",
2556
0
                                                (MAGICK_SSIZE_T) (pval-tiffp));
2557
0
                        goto generate_attribute_failure;
2558
0
                      }
2559
0
                    allocation_size=n+1;
2560
0
                    for (a=0; a < n; a++)
2561
0
                      {
2562
0
                        if (!(isprint((int) pval[a])))
2563
0
                          allocation_size += 3;
2564
0
                      }
2565
2566
0
                    value=MagickAllocateMemory(char *,allocation_size);
2567
0
                    if (value != (char *) NULL)
2568
0
                      {
2569
0
                        i=0;
2570
0
                        for (a=0; a < n; a++)
2571
0
                          {
2572
0
                            if ((f == EXIF_FMT_ASCII) && (pval[a] == '\0'))
2573
0
                              break;
2574
0
                            if ((isprint((int) pval[a])) ||
2575
0
                                ((pval[a] == '\0') &&
2576
0
                                 (a == (n-1) && (!binary))))
2577
0
                              {
2578
0
                                value[i++]=pval[a];
2579
0
                              }
2580
0
                            else
2581
0
                              {
2582
0
                                i += snprintf(&value[i],(allocation_size-i),"\\%03o",
2583
0
                                              (unsigned int) pval[a]);
2584
0
                                binary |= MagickTrue;
2585
0
                              }
2586
0
                          }
2587
0
                        value[i]='\0';
2588
0
                      }
2589
0
                    break;
2590
0
                  }
2591
6.02k
                }
2592
6.02k
              if (value != (char *) NULL)
2593
2.39k
                {
2594
2.39k
                  const char
2595
2.39k
                    *description;
2596
2597
2.39k
                  if (strlen(final) != 0)
2598
1.59k
                    (void) ConcatenateString(&final,EXIF_DELIMITER);
2599
2.39k
                  description=(const char *) NULL;
2600
2.39k
                  switch (all)
2601
2.39k
                    {
2602
0
                    case 1:
2603
0
                      {
2604
0
                        description=EXIFIfdTagToDescription(t,tag_description);
2605
0
                        FormatString(s,"%.1024s=",description);
2606
0
                        (void) ConcatenateString(&final,s);
2607
0
                        break;
2608
0
                      }
2609
0
                    case 2:
2610
0
                      {
2611
0
                        FormatString(s,"#%04x=",t);
2612
0
                        (void) ConcatenateString(&final,s);
2613
0
                        break;
2614
0
                      }
2615
2.39k
                    }
2616
2.39k
                  (void) ConcatenateString(&final,value);
2617
2.39k
                  MagickFreeMemory(value);
2618
2.39k
                }
2619
6.02k
            }
2620
49.2k
          if (t == GPS_OFFSET && (gpsoffset != 0))
2621
3.20k
            {
2622
3.20k
              if ((gpsoffset < length) && (level < (DE_STACK_SIZE-2)))
2623
2.47k
                {
2624
                  /*
2625
                    Push our current directory state onto the stack.
2626
                  */
2627
2.47k
                  ifdstack[level]=ifdp;
2628
2.47k
                  de++; /* bump to the next entry */
2629
2.47k
                  destack[level]=de;
2630
2.47k
                  gpsfoundstack[level]=gpsfound;
2631
2.47k
                  level++;
2632
                  /*
2633
                    Push new state onto of stack to cause a jump.
2634
                  */
2635
2.47k
                  ifdstack[level]=tiffp+gpsoffset;
2636
2.47k
                  destack[level]=0;
2637
2.47k
                  gpsfoundstack[level]=MagickTrue;
2638
2.47k
                  level++;
2639
2.47k
                }
2640
3.20k
              gpsoffset=0;
2641
3.20k
              break; /* break out of the for loop */
2642
3.20k
            }
2643
2644
46.0k
          if ((t == TAG_EXIF_OFFSET) || (t == TAG_INTEROP_OFFSET))
2645
7.58k
            {
2646
7.58k
              offset=Read32u(morder,pval);
2647
7.58k
              if (logging & debug)
2648
0
                (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2649
0
                                      "EXIF: %s at offset %u",
2650
0
                                      t == TAG_EXIF_OFFSET ? "TAG_EXIF_OFFSET" :
2651
0
                                      "TAG_INTEROP_OFFSET", offset);
2652
7.58k
              if ((offset < length) && (level < (DE_STACK_SIZE-2)))
2653
6.82k
                {
2654
                  /*
2655
                    Check that we are not being directed to read an
2656
                    IFD that we are already parsing and quit in order
2657
                    to avoid a loop.
2658
                  */
2659
6.82k
                  unsigned char *new_ifdp = tiffp+offset;
2660
6.82k
                  MagickBool dup_ifd = MagickFalse;
2661
2662
6.82k
                  if (new_ifdp == ifdp)
2663
86
                    {
2664
86
                      dup_ifd = MagickTrue;
2665
86
                    }
2666
6.73k
                  else
2667
6.73k
                    {
2668
15.7k
                      for (i=0; i < level; i++)
2669
9.02k
                        {
2670
9.02k
                          if (ifdstack[i] == new_ifdp)
2671
15
                            {
2672
15
                              dup_ifd = MagickTrue;
2673
15
                              break;
2674
15
                            }
2675
9.02k
                        }
2676
6.73k
                    }
2677
6.82k
                  if (dup_ifd)
2678
101
                    {
2679
101
                      if (logging)
2680
0
                        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2681
0
                                              "EXIF: Duplicate IFD detected, quitting to avoid loop!");
2682
101
                      goto generate_attribute_failure;
2683
101
                    }
2684
2685
                  /*
2686
                    Push our current directory state onto the stack.
2687
                  */
2688
6.72k
                  if (logging & debug)
2689
0
                    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2690
0
                                          "ifdstack[%u]=%p", level, ifdp);
2691
6.72k
                  ifdstack[level]=ifdp;
2692
6.72k
                  de++; /* bump to the next entry */
2693
6.72k
                  destack[level]=de;
2694
6.72k
                  gpsfoundstack[level]=gpsfound;
2695
6.72k
                  level++;
2696
                  /*
2697
                    Push new state onto of stack to cause a jump.
2698
                  */
2699
6.72k
                  ifdstack[level]=tiffp+offset;
2700
6.72k
                  if (logging & debug)
2701
0
                    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2702
0
                                          "ifdstack[%u]=%p", level, ifdp);
2703
6.72k
                  destack[level]=0;
2704
6.72k
                  gpsfoundstack[level]=MagickFalse;
2705
6.72k
                  level++;
2706
6.72k
                }
2707
7.47k
              break; /* break out of the for loop */
2708
7.58k
            }
2709
46.0k
        }
2710
26.8k
    } while (level > 0);
2711
3.55k
  if (strlen(final) == 0)
2712
2.89k
    (void) ConcatenateString(&final,"unknown");
2713
2714
3.55k
  (void) SetImageAttribute(image,specification,(const char *) NULL);
2715
3.55k
  (void) SetImageAttribute(image,specification,(const char *) final);
2716
3.55k
  MagickFreeMemory(final);
2717
3.55k
  return(True);
2718
348k
 generate_attribute_failure:
2719
348k
  MagickFreeMemory(final);
2720
348k
  return False;
2721
8.84k
}
2722
2723
/*
2724
  Generate an aggregate attribute result based on a wildcard
2725
  specification like "foo:*".
2726
*/
2727
static int
2728
GenerateWildcardAttribute(Image *image,const char *key)
2729
4.04k
{
2730
4.04k
  char
2731
4.04k
    *result=NULL;
2732
2733
4.04k
  size_t
2734
4.04k
    key_length=0;
2735
2736
4.04k
  register ImageAttribute
2737
4.04k
    *p = (ImageAttribute *) NULL;
2738
2739
4.04k
  MagickPassFail
2740
4.04k
    status=MagickFail;
2741
2742
  /*
2743
    Support a full "*" wildcard.
2744
  */
2745
4.04k
  if (strcmp("*",key) == 0)
2746
0
    {
2747
0
      (void) GenerateIPTCAttribute((Image *) image,"IPTC:*");
2748
0
      (void) Generate8BIMAttribute((Image *) image,"8BIM:*");
2749
0
      (void) GenerateEXIFAttribute((Image *) image,"EXIF:*");
2750
0
    }
2751
2752
4.04k
  key_length=strlen(key)-1;
2753
15.8k
  for (p=image->attributes; p != (ImageAttribute *) NULL; p=p->next)
2754
11.8k
    if (LocaleNCompare(key,p->key,key_length) == 0)
2755
3.61k
      {
2756
3.61k
        char
2757
3.61k
          s[MaxTextExtent];
2758
2759
3.61k
        if (result != NULL)
2760
1.69k
          (void) ConcatenateString(&result,"\n");
2761
3.61k
        FormatString(s,"%.512s=%.1024s",p->key,p->value);
2762
3.61k
        (void) ConcatenateString(&result,s);
2763
3.61k
      }
2764
2765
4.04k
  if (result != NULL)
2766
1.92k
    {
2767
1.92k
      status=SetImageAttribute(image,key,result);
2768
1.92k
      MagickFreeMemory(result);
2769
1.92k
    }
2770
4.04k
  return status;
2771
4.04k
}
2772
2773
MagickExport const ImageAttribute *
2774
GetImageAttribute(const Image *image,const char *key)
2775
2.58M
{
2776
2.58M
  register ImageAttribute
2777
2.58M
    *p = (ImageAttribute *) NULL;
2778
2779
2.58M
  size_t
2780
2.58M
    key_length=0;
2781
2782
2.58M
  assert(image != (Image *) NULL);
2783
2.58M
  assert(image->signature == MagickSignature);
2784
2785
  /*
2786
    If key is null, then return a pointer to the attribute list.
2787
  */
2788
2.58M
  if (key == (char *) NULL)
2789
478k
    return(image->attributes);
2790
2791
2.10M
  key_length=strlen(key);
2792
2793
7.59M
  for (p=image->attributes; p != (ImageAttribute *) NULL; p=p->next)
2794
6.20M
    if (LocaleCompare(key,p->key) == 0)
2795
715k
      return(p);
2796
2797
1.38M
  if (LocaleNCompare("IPTC:",key,5) == 0)
2798
0
    {
2799
      /*
2800
        Create an attribute named "IPTC:*" with all matching
2801
        key=values and return it.
2802
      */
2803
0
      if (GenerateIPTCAttribute((Image *) image,key) == True)
2804
0
        return(GetImageAttribute(image,key));
2805
0
    }
2806
1.38M
  else if (LocaleNCompare("8BIM:",key,5) == 0)
2807
0
    {
2808
      /*
2809
        Create an attribute named "8BIM:*" with all matching
2810
        key=values and return it.
2811
      */
2812
0
      if (Generate8BIMAttribute((Image *) image,key) == True)
2813
0
        return(GetImageAttribute(image,key));
2814
0
    }
2815
1.38M
  else if (LocaleNCompare("EXIF:",key,5) == 0)
2816
352k
    {
2817
      /*
2818
        Create an attribute named "EXIF:*" with all matching
2819
        key=values and return it.
2820
      */
2821
352k
      if (GenerateEXIFAttribute((Image *) image,key) == True)
2822
3.55k
        return(GetImageAttribute(image,key));
2823
352k
    }
2824
1.03M
  else if ((key_length >=2) && (key[key_length-1] == '*'))
2825
4.04k
    {
2826
      /*
2827
        Create an attribute named "foo:*" with all matching
2828
        key=values and return it.
2829
      */
2830
4.04k
      if (GenerateWildcardAttribute((Image *) image,key) == True)
2831
1.92k
        return(GetImageAttribute(image,key));
2832
4.04k
    }
2833
1.03M
  else if ((key_length ==1) && (key[0] == '*'))
2834
0
    {
2835
      /*
2836
        Create an attribute named "*" with all key=values and return
2837
        it.
2838
      */
2839
0
      if (GenerateWildcardAttribute((Image *) image,key) == True)
2840
0
        return(GetImageAttribute(image,key));
2841
0
    }
2842
1.38M
  return(p);
2843
1.38M
}
2844

2845
/*
2846
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847
%                                                                             %
2848
%                                                                             %
2849
%                                                                             %
2850
%   G e t I m a g e C l i p p i n g P a t h A t t r i b u t e                 %
2851
%                                                                             %
2852
%                                                                             %
2853
%                                                                             %
2854
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2855
%
2856
%  Method GetImageClippingPathAttribute searches the list of image attributes
2857
%  and returns a pointer to a clipping path if it exists otherwise NULL.
2858
%
2859
%  The format of the GetImageClippingPathAttribute method is:
2860
%
2861
%      const ImageAttribute *GetImageClippingPathAttribute(const Image *image)
2862
%
2863
%  A description of each parameter follows:
2864
%
2865
%    o attribute:  Method GetImageClippingPathAttribute returns the clipping
2866
%      path if it exists otherwise NULL.
2867
%
2868
%    o image: The image.
2869
%
2870
%
2871
*/
2872
MagickExport const ImageAttribute *
2873
GetImageClippingPathAttribute(const Image *image)
2874
0
{
2875
  /* Get the name of the clipping path, if any.  The clipping path
2876
     length is indicated by the first character of the Pascal
2877
     string. */
2878
0
  const ImageAttribute *path_name = GetImageAttribute(image, "8BIM:2999,2999");
2879
0
  if ((path_name != (const ImageAttribute *) NULL) &&
2880
0
      (path_name->length > 2) &&
2881
0
      ((size_t) path_name->value[0] < path_name->length))
2882
0
    {
2883
0
      static const char clip_prefix[] = "8BIM:1999,2998";
2884
0
      char attr_name[271];
2885
      /*sprintf(attr_name, "%s:%.255s", clip_prefix, path_name->value+1);*/
2886
0
      sprintf(attr_name, "%s:%.*s", clip_prefix, Min(255,(int) path_name->length-1),
2887
0
              path_name->value+1);
2888
0
      return GetImageAttribute(image, attr_name);
2889
0
    }
2890
0
  return NULL;
2891
0
}
2892

2893
/*
2894
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2895
%                                                                             %
2896
%                                                                             %
2897
%                                                                             %
2898
+   G e t I m a g e I n f o A t t r i b u t e                                 %
2899
%                                                                             %
2900
%                                                                             %
2901
%                                                                             %
2902
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2903
%
2904
%  GetImageInfoAttribute() returns a "fake" attribute based on data in the
2905
%  image info or image structures.
2906
%
2907
%  The format of the GetImageInfoAttribute method is:
2908
%
2909
%      const ImageAttribute *GetImageAttribute(const ImageInfo *image_info,
2910
%        const Image *image,const char *key)
2911
%
2912
%  A description of each parameter follows:
2913
%
2914
%    o attribute:  Method GetImageInfoAttribute returns the attribute if it
2915
%      exists otherwise NULL.
2916
%
2917
%    o image_info: The imageInfo.
2918
%
2919
%    o image: The image.
2920
%
2921
%    o key:  These character strings are the name of an image attribute to
2922
%      return.
2923
%
2924
*/
2925
MagickExport const ImageAttribute *
2926
GetImageInfoAttribute(const ImageInfo *image_info,const Image *image,
2927
                      const char *key)
2928
0
{
2929
0
  char
2930
0
    attribute[MaxTextExtent],
2931
0
    filename[MaxTextExtent];
2932
2933
0
  attribute[0]='\0';
2934
0
  switch(*(key))
2935
0
    {
2936
0
    case 'b':
2937
0
      {
2938
0
        if (LocaleNCompare("base",key,2) == 0)
2939
0
          {
2940
0
            GetPathComponent(image->magick_filename,BasePath,filename);
2941
0
            (void) strlcpy(attribute,filename,MaxTextExtent);
2942
0
            break;
2943
0
          }
2944
0
        break;
2945
0
      }
2946
0
    case 'd':
2947
0
      {
2948
0
        if (LocaleNCompare("depth",key,2) == 0)
2949
0
          {
2950
0
            FormatString(attribute,"%u",image->depth);
2951
0
            break;
2952
0
          }
2953
0
        if (LocaleNCompare("directory",key,2) == 0)
2954
0
          {
2955
0
            GetPathComponent(image->magick_filename,HeadPath,filename);
2956
0
            (void) strlcpy(attribute,filename,MaxTextExtent);
2957
0
            break;
2958
0
          }
2959
0
        break;
2960
0
      }
2961
0
    case 'e':
2962
0
      {
2963
0
        if (LocaleNCompare("extension",key,2) == 0)
2964
0
          {
2965
0
            GetPathComponent(image->magick_filename,ExtensionPath,filename);
2966
0
            (void) strlcpy(attribute,filename,MaxTextExtent);
2967
0
            break;
2968
0
          }
2969
0
        break;
2970
0
      }
2971
0
    case 'g':
2972
0
      {
2973
0
        if (LocaleNCompare("group",key,2) == 0)
2974
0
          {
2975
0
            FormatString(attribute,"0x%lx",image_info->group);
2976
0
            break;
2977
0
          }
2978
0
        break;
2979
0
      }
2980
0
    case 'h':
2981
0
      {
2982
0
        if (LocaleNCompare("height",key,2) == 0)
2983
0
          {
2984
0
            FormatString(attribute,"%lu",
2985
0
                         image->magick_rows ? image->magick_rows : 256L);
2986
0
            break;
2987
0
          }
2988
0
        break;
2989
0
      }
2990
0
    case 'i':
2991
0
      {
2992
0
        if (LocaleNCompare("input",key,2) == 0)
2993
0
          {
2994
0
            (void) strlcpy(attribute,image->filename,MaxTextExtent);
2995
0
            break;
2996
0
          }
2997
0
        break;
2998
0
      }
2999
0
    case 'm':
3000
0
      {
3001
0
        if (LocaleNCompare("magick",key,2) == 0)
3002
0
          {
3003
0
            (void) strlcpy(attribute,image->magick,MaxTextExtent);
3004
0
            break;
3005
0
          }
3006
0
        break;
3007
0
      }
3008
0
    case 'n':
3009
0
      {
3010
0
        if (LocaleNCompare("name",key,2) == 0)
3011
0
          {
3012
            /* What should this really be? */
3013
0
            GetPathComponent(image->magick_filename,BasePath,filename);
3014
0
            (void) strlcpy(attribute,filename,MaxTextExtent);
3015
0
            break;
3016
0
          }
3017
0
        break;
3018
0
      }
3019
0
    case 's':
3020
0
      {
3021
0
        if (LocaleNCompare("size",key,2) == 0)
3022
0
          {
3023
0
            char
3024
0
              format[MaxTextExtent];
3025
3026
0
            FormatSize(GetBlobSize(image),format);
3027
0
            FormatString(attribute,"%.1024s",format);
3028
0
            break;
3029
0
          }
3030
0
        if (LocaleNCompare("scene",key,2) == 0)
3031
0
          {
3032
0
            FormatString(attribute,"%lu",image->scene);
3033
0
            if (image_info->subrange != 0)
3034
0
              FormatString(attribute,"%lu",image_info->subimage);
3035
0
            break;
3036
0
          }
3037
0
        if (LocaleNCompare("scenes",key,6) == 0)
3038
0
          {
3039
0
            FormatString(attribute,"%lu",
3040
0
                         (unsigned long) GetImageListLength(image));
3041
0
            break;
3042
0
          }
3043
0
        break;
3044
0
      }
3045
0
    case 'o':
3046
0
      {
3047
0
        if (LocaleNCompare("output",key,2) == 0)
3048
0
          {
3049
0
            (void) strlcpy(attribute,image_info->filename,MaxTextExtent);
3050
0
            break;
3051
0
          }
3052
0
        break;
3053
0
      }
3054
0
    case 'p':
3055
0
      {
3056
0
        if (LocaleNCompare("page",key,2) == 0)
3057
0
          {
3058
0
            register const Image
3059
0
              *p;
3060
3061
0
            unsigned int
3062
0
              page;
3063
3064
0
            p=image;
3065
0
            for (page=1; p->previous != (Image *) NULL; page++)
3066
0
              p=p->previous;
3067
0
            FormatString(attribute,"%u",page);
3068
0
            break;
3069
0
          }
3070
0
        break;
3071
0
      }
3072
0
    case 'u':
3073
0
      {
3074
0
        if (LocaleNCompare("unique",key,2) == 0)
3075
0
          {
3076
0
            (void) strlcpy(filename,image_info->unique,MaxTextExtent);
3077
0
            if (*filename == '\0')
3078
0
              if(!AcquireTemporaryFileName(filename))
3079
0
                return((ImageAttribute *) NULL);
3080
0
            (void) strlcpy(attribute,filename,MaxTextExtent);
3081
0
            break;
3082
0
          }
3083
0
        break;
3084
0
      }
3085
0
    case 'w':
3086
0
      {
3087
0
        if (LocaleNCompare("width",key,2) == 0)
3088
0
          {
3089
0
            FormatString(attribute,"%lu",
3090
0
                         image->magick_columns ? image->magick_columns : 256L);
3091
0
            break;
3092
0
          }
3093
0
        break;
3094
0
      }
3095
0
    case 'x':
3096
0
      {
3097
0
        if (LocaleNCompare("xresolution",key,2) == 0)
3098
0
          {
3099
0
            FormatString(attribute,"%g",image->x_resolution);
3100
0
            break;
3101
0
          }
3102
0
        break;
3103
0
      }
3104
0
    case 'y':
3105
0
      {
3106
0
        if (LocaleNCompare("yresolution",key,2) == 0)
3107
0
          {
3108
0
            FormatString(attribute,"%g",image->y_resolution);
3109
0
            break;
3110
0
          }
3111
0
        break;
3112
0
      }
3113
0
    case 'z':
3114
0
      {
3115
0
        if (LocaleNCompare("zero",key,2) == 0)
3116
0
          {
3117
0
            (void) strlcpy(filename,image_info->zero,MaxTextExtent);
3118
0
            if (*filename == '\0')
3119
0
              if(!AcquireTemporaryFileName(filename))
3120
0
                return((ImageAttribute *) NULL);
3121
0
            (void) strlcpy(attribute,filename,MaxTextExtent);
3122
0
            break;
3123
0
          }
3124
0
        break;
3125
0
      }
3126
0
    }
3127
0
  if (strlen(image->magick_filename) != 0)
3128
0
    return(GetImageAttribute(image,key));
3129
0
  return((ImageAttribute *) NULL);
3130
0
}
3131

3132
/*
3133
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3134
%                                                                             %
3135
%                                                                             %
3136
%                                                                             %
3137
%   S e t I m a g e A t t r i b u t e                                         %
3138
%                                                                             %
3139
%                                                                             %
3140
%                                                                             %
3141
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3142
%
3143
%  SetImageAttribute() searches the list of image attributes and replaces the
3144
%  attribute value.  If it is not found in the list, the attribute name
3145
%  and value is added to the list.   If the attribute exists in the list,
3146
%  the value is concatenated to the attribute.  SetImageAttribute returns
3147
%  True if the attribute is successfully concatenated or added to the list,
3148
%  otherwise False.  If the value is NULL, the matching key is deleted
3149
%  from the list.
3150
%
3151
%  There is special handling for the EXIF:Orientation attribute. Setting this
3152
%  attribute will also update the EXIF tag in the image's EXIF profile to the
3153
%  given value provided an EXIF profile exists and has an existing EXIF
3154
%  orientation tag and the attribute value is a valid orientation
3155
%  (see orientationType). The attribute value will be set regardless of
3156
%  whether the EXIF profile was successfully updated. The new
3157
%  EXIF:Orientation attribute replaces the existing value rather than
3158
%  being concatenated to it as when setting other attributes.
3159
%
3160
%  The 'comment' and 'label' attributes are treated specially in that
3161
%  embedded format specifications are translated according to the formatting
3162
%  rules of TranslateText().
3163
%
3164
%  The format of the SetImageAttribute method is:
3165
%
3166
%      unsigned int SetImageAttribute(Image *image,const char *key,
3167
%        const char *value)
3168
%
3169
%  A description of each parameter follows:
3170
%
3171
%    o image: The image.
3172
%
3173
%    o key,value:  These character strings are the name and value of an image
3174
%      attribute to replace or add to the list.
3175
%
3176
%
3177
*/
3178
3179
/*
3180
  Find the location of an EXIF attribute in an EXIF profile. The EXIF attribute
3181
  to be found is specified as its numeric tag value (see tag_table). Returns
3182
  a pointer to the attribute in the EXIF profile or NULL if missing or there
3183
  is an error parsing the profile. Returns the EXIF profile byte order if the
3184
  morderp parameter is not NULL.
3185
*/
3186
3187
static unsigned char *
3188
FindEXIFAttribute(const unsigned char *profile_info,
3189
                  const size_t profile_length,
3190
                  const unsigned short tag, int *morderp)
3191
179
{
3192
179
  char
3193
179
    tag_description[MaxTextExtent];
3194
3195
179
  int
3196
179
    id,
3197
179
    level,
3198
179
    morder;
3199
3200
179
  size_t
3201
179
    length;
3202
3203
179
  unsigned long
3204
179
    offset;
3205
3206
179
  unsigned char
3207
179
    *tiffp,
3208
179
    *tiffp_max,
3209
179
    *ifdstack[DE_STACK_SIZE],
3210
179
    *ifdp,
3211
179
    *info,
3212
179
    *attribp;
3213
3214
179
  unsigned int
3215
179
    de,
3216
179
    destack[DE_STACK_SIZE],
3217
179
    nde;
3218
3219
179
  MagickBool
3220
179
    gpsfoundstack[DE_STACK_SIZE],
3221
179
    gpsfound;
3222
3223
179
  MagickBool
3224
179
    debug=MagickFalse;
3225
3226
179
  attribp = (unsigned char *)NULL;
3227
3228
179
  assert((ArraySize(format_bytes)-1) == EXIF_NUM_FORMATS);
3229
3230
179
  {
3231
179
    const char *
3232
179
      env_value;
3233
3234
    /*
3235
      Allow enabling debug of EXIF tags
3236
    */
3237
179
    if ((env_value=getenv("MAGICK_DEBUG_EXIF")))
3238
0
      {
3239
0
        if (LocaleCompare(env_value,"TRUE") == 0)
3240
0
          debug=MagickTrue;
3241
0
      }
3242
179
  }
3243
179
  gpsfound=MagickFalse;
3244
179
  length=profile_length;
3245
179
  info=(unsigned char *) profile_info;
3246
179
  while (length != 0)
3247
179
    {
3248
179
      if (ReadByte(&info,&length) != 0x45)
3249
0
        continue;
3250
179
      if (ReadByte(&info,&length) != 0x78)
3251
0
        continue;
3252
179
      if (ReadByte(&info,&length) != 0x69)
3253
0
        continue;
3254
179
      if (ReadByte(&info,&length) != 0x66)
3255
0
        continue;
3256
179
      if (ReadByte(&info,&length) != 0x00)
3257
0
        continue;
3258
179
      if (ReadByte(&info,&length) != 0x00)
3259
0
        continue;
3260
179
      break;
3261
179
    }
3262
179
  if (length < 16)
3263
0
    goto find_attribute_failure;
3264
179
  tiffp=info;
3265
179
  tiffp_max=tiffp+length;
3266
179
  id=Read16u(0,tiffp);
3267
179
  morder=0;
3268
179
  if (id == 0x4949) /* LSB */
3269
171
    morder=0;
3270
8
  else
3271
8
    if (id == 0x4D4D) /* MSB */
3272
0
      morder=1;
3273
8
    else
3274
8
      goto find_attribute_failure;
3275
171
  if (morderp)
3276
171
    *morderp = morder;
3277
171
  if (Read16u(morder,tiffp+2) != 0x002a)
3278
0
    goto find_attribute_failure;
3279
  /*
3280
    This is the offset to the first IFD.
3281
  */
3282
171
  offset=Read32u(morder,tiffp+4);
3283
171
  if (offset >= length)
3284
0
    goto find_attribute_failure;
3285
  /*
3286
    Set the pointer to the first IFD and follow it were it leads.
3287
  */
3288
171
  ifdp=tiffp+offset;
3289
171
  level=0;
3290
171
  de=0U;
3291
171
  do
3292
375
    {
3293
      /*
3294
        If there is anything on the stack then pop it off.
3295
      */
3296
375
      if (level > 0)
3297
204
        {
3298
204
          level--;
3299
204
          ifdp=ifdstack[level];
3300
204
          de=destack[level];
3301
204
          gpsfound=gpsfoundstack[level];
3302
204
        }
3303
      /*
3304
        Determine how many entries there are in the current IFD.
3305
        Limit the number of entries parsed to MAX_TAGS_PER_IFD.
3306
      */
3307
375
      if ((ifdp < tiffp) || (ifdp+2 > tiffp_max))
3308
0
        goto find_attribute_failure;
3309
375
      nde=Read16u(morder,ifdp);
3310
375
      if (nde > MAX_TAGS_PER_IFD)
3311
43
        nde=MAX_TAGS_PER_IFD;
3312
2.69k
      for (; de < nde; de++)
3313
2.59k
        {
3314
2.59k
          size_t
3315
2.59k
            n;
3316
3317
2.59k
          unsigned int
3318
2.59k
            c,
3319
2.59k
            f,
3320
2.59k
            t;
3321
3322
2.59k
          unsigned char
3323
2.59k
            *pde,
3324
2.59k
            *pval;
3325
3326
3327
2.59k
          pde=(unsigned char *) (ifdp+2+(12*(size_t) de));
3328
2.59k
          if (pde + 12 > tiffp + length)
3329
9
            {
3330
9
              if (debug)
3331
0
                fprintf(stderr, "EXIF: Invalid Exif, entry is beyond metadata limit.\n");
3332
9
              goto find_attribute_failure;
3333
9
            }
3334
2.58k
          t=Read16u(morder,pde); /* get tag value */
3335
2.58k
          f=Read16u(morder,pde+2); /* get the format */
3336
2.58k
          if ((size_t) f >= ArraySize(format_bytes))
3337
102
            break;
3338
2.47k
          c=Read32u(morder,pde+4); /* get number of components */
3339
2.47k
          n=MagickArraySize(c,format_bytes[f]);
3340
2.47k
          if ((n == 0) && (c != 0) && (format_bytes[f] != 0))
3341
0
            {
3342
0
              if (debug)
3343
0
                fprintf(stderr, "EXIF: Invalid Exif, too many components (%u).\n",c);
3344
0
              goto find_attribute_failure;
3345
0
            }
3346
2.47k
          if (n <= 4)
3347
1.44k
            pval=(unsigned char *) pde+8;
3348
1.03k
          else
3349
1.03k
            {
3350
1.03k
              unsigned long
3351
1.03k
                oval;
3352
3353
              /*
3354
                The directory entry contains an offset.
3355
              */
3356
1.03k
              oval=Read32u(morder,pde+8);
3357
1.03k
              if ((oval+n) > length)
3358
77
                continue;
3359
962
              pval=(unsigned char *)(tiffp+oval);
3360
962
            }
3361
3362
2.40k
          if (debug)
3363
0
            {
3364
0
              fprintf(stderr,
3365
0
                      "EXIF: TagVal=%d  TagDescr=\"%s\" Format=%d  "
3366
0
                      "FormatDescr=\"%s\"  Components=%u\n",t,
3367
0
                      EXIFIfdTagToDescription(t,tag_description),f,
3368
0
                      EXIFIfdFieldTypeToStr(f),c);
3369
0
            }
3370
3371
2.40k
          if (gpsfound)
3372
204
            {
3373
204
              if (/* (t < GPS_TAG_START) || */ (t > GPS_TAG_STOP))
3374
70
                {
3375
70
                  if (debug)
3376
0
                    fprintf(stderr,
3377
0
                            "EXIF: Skipping bogus GPS IFD tag %d (0x%04X) ...\n",t,t);
3378
70
                  continue;
3379
70
                }
3380
204
            }
3381
2.19k
          else
3382
2.19k
            {
3383
2.19k
              if ((t < EXIF_TAG_START) || ( t > EXIF_TAG_STOP))
3384
322
                {
3385
322
                  if (debug)
3386
0
                    fprintf(stderr,
3387
0
                            "EXIF: Skipping bogus EXIF IFD tag %d (0x%04X) ...\n",t,t);
3388
322
                  continue;
3389
322
                }
3390
2.19k
            }
3391
3392
          /*
3393
            Return values for all the tags, or for a specific requested tag.
3394
3395
            Tags from the GPS sub-IFD are in a bit of a chicken and
3396
            egg situation in that the tag for the GPS sub-IFD will not
3397
            be seen unless we pass that tag through so it can be
3398
            processed.  So we pass the GPS_OFFSET tag through, but if
3399
            it was not requested, then we don't return a string value
3400
            for it.
3401
          */
3402
2.01k
          if (tag == t)
3403
57
            {
3404
57
              attribp = pde;
3405
57
              break;
3406
57
            }
3407
3408
1.95k
          if (t == GPS_OFFSET)
3409
51
            {
3410
51
              offset=Read32u(morder,pval);
3411
51
              if ((offset < length) && (level < (DE_STACK_SIZE-2)))
3412
49
                {
3413
                  /*
3414
                    Push our current directory state onto the stack.
3415
                  */
3416
49
                  ifdstack[level]=ifdp;
3417
49
                  de++; /* bump to the next entry */
3418
49
                  destack[level]=de;
3419
49
                  gpsfoundstack[level]=gpsfound;
3420
49
                  level++;
3421
                  /*
3422
                    Push new state onto of stack to cause a jump.
3423
                  */
3424
49
                  ifdstack[level]=tiffp+offset;
3425
49
                  destack[level]=0;
3426
49
                  gpsfoundstack[level]=MagickTrue;
3427
49
                  level++;
3428
49
                }
3429
51
              break; /* break out of the for loop */
3430
51
            }
3431
3432
1.90k
          if ((t == TAG_EXIF_OFFSET) || (t == TAG_INTEROP_OFFSET))
3433
56
            {
3434
56
              offset=Read32u(morder,pval);
3435
56
              if ((offset < length) && (level < (DE_STACK_SIZE-2)))
3436
54
                {
3437
                  /*
3438
                    Push our current directory state onto the stack.
3439
                  */
3440
54
                  ifdstack[level]=ifdp;
3441
54
                  de++; /* bump to the next entry */
3442
54
                  destack[level]=de;
3443
54
                  gpsfoundstack[level]=gpsfound;
3444
54
                  level++;
3445
                  /*
3446
                    Push new state onto of stack to cause a jump.
3447
                  */
3448
54
                  ifdstack[level]=tiffp+offset;
3449
54
                  destack[level]=0;
3450
54
                  gpsfoundstack[level]=MagickFalse;
3451
54
                  level++;
3452
54
                }
3453
56
              break; /* break out of the for loop */
3454
56
            }
3455
1.90k
        }
3456
375
    } while (!attribp && (level > 0));
3457
162
  return attribp;
3458
17
 find_attribute_failure:
3459
17
  return (unsigned char *)NULL;
3460
171
}
3461
3462
/*
3463
  SetEXIFOrientation() updates the EXIF orientation tag in the image's EXIF
3464
  profile to the value provided. Returns MagickPass on success or MagickFail
3465
  if either there is no EXIF profile in the image, there was an error parsing
3466
  the EXIF profile, there was no existing EXIF orientation attribute in
3467
  the EXIF profile or there was a memory allocation error.
3468
*/
3469
3470
static MagickPassFail
3471
SetEXIFOrientation(Image *image, const int orientation)
3472
179
{
3473
179
  MagickPassFail
3474
179
    result;
3475
3476
179
  const unsigned char
3477
179
    *current_profile;
3478
3479
179
  size_t
3480
179
    profile_length;
3481
3482
179
  unsigned char
3483
179
    *orientp,
3484
179
    *pval,
3485
179
    *new_profile;
3486
3487
179
  int
3488
179
    morder,
3489
179
    current_orientation;
3490
3491
179
  unsigned short
3492
179
    f;
3493
3494
179
  unsigned long
3495
179
    c;
3496
3497
179
  if (orientation < TopLeftOrientation || orientation > LeftBottomOrientation)
3498
0
    return(MagickFail);
3499
3500
179
  current_profile=GetImageProfile(image,"EXIF",&profile_length);
3501
179
  if (current_profile == 0)
3502
0
    return(MagickFail);
3503
3504
  /* Clone profile so orientation can be set */
3505
179
  new_profile = MagickAllocateMemory(unsigned char *,profile_length);
3506
179
  if (new_profile == 0)
3507
0
    return(MagickFail);
3508
3509
179
  result = MagickFail;
3510
179
  memcpy(new_profile, current_profile, profile_length);
3511
179
  orientp = FindEXIFAttribute(new_profile, profile_length,
3512
179
                              (unsigned short)EXIF_ORIENTATION, &morder);
3513
179
  if (orientp)
3514
57
    {
3515
      /* Make sure EXIF orientation attribute is valid */
3516
57
      f=Read16u(morder,orientp+2); /* get the format */
3517
57
      c=Read32u(morder,orientp+4); /* get number of components */
3518
57
      if ((f == EXIF_FMT_USHORT) && (c == 1))
3519
39
        {
3520
39
          pval=(unsigned char *) orientp+8;
3521
39
          current_orientation=(int)Read16u(morder, pval);
3522
39
          if (current_orientation != (unsigned short)orientation)
3523
6
            {
3524
6
              Write16u(morder, pval, orientation);
3525
6
              Write16u(morder, pval+2, 0);
3526
6
              result=SetImageProfile(image,"EXIF",new_profile,profile_length);
3527
6
            }
3528
33
          else
3529
33
            result = MagickPass;
3530
39
        }
3531
57
    }
3532
179
  MagickFreeMemory(new_profile);
3533
3534
179
  return result;
3535
179
}
3536
3537
MagickExport MagickPassFail
3538
SetImageAttribute(Image *image,const char *key,const char *value)
3539
7.02M
{
3540
7.02M
  ImageAttribute
3541
7.02M
    *attribute;
3542
3543
7.02M
  register ImageAttribute
3544
7.02M
    *p;
3545
3546
  /*
3547
    Initialize new attribute.
3548
  */
3549
7.02M
  assert(image != (Image *) NULL);
3550
7.02M
  assert(image->signature == MagickSignature);
3551
7.02M
  if ((key == (const char *) NULL) || (*key == '\0'))
3552
2.43k
    return(MagickFail);
3553
3554
7.01M
  if (value == (const char *) NULL)
3555
2.25M
    {
3556
      /*
3557
        Delete attribute from the image attributes list.
3558
      */
3559
3.66M
      for (p=image->attributes; p != (ImageAttribute *) NULL; p=p->next)
3560
3.49M
        if (LocaleCompare(key,p->key) == 0)
3561
2.09M
          break;
3562
2.25M
      if (p == (ImageAttribute *) NULL)
3563
161k
        return(False);
3564
2.09M
      if (p->previous != (ImageAttribute *) NULL)
3565
193k
        p->previous->next=p->next;
3566
1.89M
      else
3567
1.89M
        {
3568
1.89M
          image->attributes=p->next;
3569
1.89M
          if (p->next != (ImageAttribute *) NULL)
3570
13.7k
            p->next->previous=(ImageAttribute *) NULL;
3571
1.89M
        }
3572
2.09M
      if (p->next != (ImageAttribute *) NULL)
3573
109k
        p->next->previous=p->previous;
3574
2.09M
      attribute=p;
3575
2.09M
      DestroyImageAttribute(attribute);
3576
2.09M
      return(MagickPass);
3577
2.25M
    }
3578
4.76M
  attribute=MagickAllocateMemory(ImageAttribute *,sizeof(ImageAttribute));
3579
4.76M
  if (attribute == (ImageAttribute *) NULL)
3580
0
    return(MagickFail);
3581
4.76M
  attribute->key=AllocateString(key);
3582
4.76M
  attribute->length=strlen(value);
3583
4.76M
  attribute->value=MagickAllocateMemory(char *,attribute->length+1);
3584
4.76M
  if (attribute->value != (char *) NULL)
3585
4.76M
    (void) strlcpy(attribute->value,value,attribute->length+1);
3586
4.76M
  if ((attribute->value == (char *) NULL) ||
3587
4.76M
      (attribute->key == (char *) NULL))
3588
0
    {
3589
0
      DestroyImageAttribute(attribute);
3590
0
      return(MagickFail);
3591
0
    }
3592
3593
4.76M
  attribute->previous=(ImageAttribute *) NULL;
3594
4.76M
  attribute->next=(ImageAttribute *) NULL;
3595
4.76M
  if (image->attributes == (ImageAttribute *) NULL)
3596
2.42M
    {
3597
2.42M
      image->attributes=attribute;
3598
2.42M
      return(MagickPass);
3599
2.42M
    }
3600
15.8M
  for (p=image->attributes; p != (ImageAttribute *) NULL; p=p->next)
3601
15.8M
    {
3602
15.8M
      if (LocaleCompare(attribute->key,p->key) == 0)
3603
593k
        {
3604
593k
          size_t
3605
593k
            min_l,
3606
593k
            realloc_l;
3607
3608
593k
          if (LocaleCompare(attribute->key,"EXIF:Orientation") == 0)
3609
179
            {
3610
179
              int
3611
179
                orientation = 0;
3612
3613
              /*
3614
                Special handling for EXIF orientation tag.
3615
                If new value differs from existing value,
3616
                EXIF profile is updated as well if it exists and
3617
                is valid. Don't append new value to existing value,
3618
                replace it instead.
3619
              */
3620
179
              if ((MagickAtoIChk(value, &orientation) == MagickPass) &&
3621
179
                  (orientation > 0 || orientation <= (int)LeftBottomOrientation))
3622
179
                {
3623
179
                  SetEXIFOrientation(image, orientation);
3624
179
                }
3625
              /* Assign changed value to attribute in list */
3626
179
              if (LocaleCompare(p->value, attribute->value) != 0)
3627
141
                {
3628
141
                  MagickFreeMemory(p->value);
3629
141
                  p->value=attribute->value;
3630
141
                  attribute->value = (char *) NULL;
3631
141
                }
3632
179
              DestroyImageAttribute(attribute);
3633
179
              return(MagickPass);
3634
179
            }
3635
593k
          else
3636
593k
            {
3637
              /*
3638
                Extend existing text string.
3639
              */
3640
593k
              min_l=p->length+attribute->length+1;
3641
6.61M
              for (realloc_l=2; realloc_l <= min_l; realloc_l *= 2)
3642
6.02M
                { /* nada */};
3643
593k
              MagickReallocMemory(char *,p->value,realloc_l);
3644
593k
              if (p->value != (char *) NULL)
3645
593k
                {
3646
593k
                  (void) memcpy(p->value+p->length,attribute->value,min_l-p->length-1);
3647
593k
                  p->length += attribute->length;
3648
593k
                  p->value[p->length] = '\0';
3649
593k
                }
3650
593k
              DestroyImageAttribute(attribute);
3651
593k
            }
3652
593k
          if (p->value != (char *) NULL)
3653
593k
            return(MagickPass);
3654
0
          (void) SetImageAttribute(image,key,NULL);
3655
0
          return(MagickFail);
3656
593k
        }
3657
15.2M
      if (p->next == (ImageAttribute *) NULL)
3658
1.74M
        break;
3659
15.2M
    }
3660
  /*
3661
    Place new attribute at the end of the attribute list.
3662
  */
3663
1.74M
  attribute->previous=p;
3664
1.74M
  p->next=attribute;
3665
1.74M
  return(MagickPass);
3666
2.33M
}