Coverage Report

Created: 2026-05-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/coders/meta.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                        M   M  EEEEE  TTTTT   AAA                            %
7
%                        MM MM  E        T    A   A                           %
8
%                        M M M  EEE      T    AAAAA                           %
9
%                        M   M  E        T    A   A                           %
10
%                        M   M  EEEEE    T    A   A                           %
11
%                                                                             %
12
%                                                                             %
13
%                    Read/Write Embedded Image Profiles.                      %
14
%                                                                             %
15
%                              Software Design                                %
16
%                             William Radcliffe                               %
17
%                                 July 2001                                   %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "MagickCore/studio.h"
43
#include "MagickCore/blob.h"
44
#include "MagickCore/blob-private.h"
45
#include "MagickCore/channel.h"
46
#include "MagickCore/exception.h"
47
#include "MagickCore/exception-private.h"
48
#include "MagickCore/image.h"
49
#include "MagickCore/image-private.h"
50
#include "MagickCore/list.h"
51
#include "MagickCore/locale-private.h"
52
#include "MagickCore/magick.h"
53
#include "MagickCore/memory_.h"
54
#include "MagickCore/module.h"
55
#include "MagickCore/profile-private.h"
56
#include "MagickCore/splay-tree.h"
57
#include "MagickCore/quantum-private.h"
58
#include "MagickCore/static.h"
59
#include "MagickCore/string_.h"
60
#include "MagickCore/string-private.h"
61
#include "MagickCore/token.h"
62
#include "MagickCore/utility.h"
63

64
/*
65
  Forward declarations.
66
*/
67
static MagickBooleanType
68
  WriteMETAImage(const ImageInfo *,Image *,ExceptionInfo *);
69

70
/*
71
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72
%                                                                             %
73
%                                                                             %
74
%                                                                             %
75
%   I s M E T A                                                               %
76
%                                                                             %
77
%                                                                             %
78
%                                                                             %
79
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80
%
81
%  IsMETA() returns MagickTrue if the image format type, identified by the
82
%  magick string, is META.
83
%
84
%  The format of the IsMETA method is:
85
%
86
%      MagickBooleanType IsMETA(const unsigned char *magick,const size_t length)
87
%
88
%  A description of each parameter follows:
89
%
90
%    o magick: compare image format pattern against these bytes.
91
%
92
%    o length: Specifies the length of the magick string.
93
%
94
%
95
*/
96
#ifdef IMPLEMENT_IS_FUNCTION
97
static MagickBooleanType IsMETA(const unsigned char *magick,const size_t length)
98
{
99
  if (length < 4)
100
    return(MagickFalse);
101
  if (LocaleNCompare((char *) magick,"8BIM",4) == 0)
102
    return(MagickTrue);
103
  if (LocaleNCompare((char *) magick,"APP1",4) == 0)
104
    return(MagickTrue);
105
  if (LocaleNCompare((char *) magick,"\034\002",2) == 0)
106
    return(MagickTrue);
107
  return(MagickFalse);
108
}
109
#endif
110

111
/*
112
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113
%                                                                             %
114
%                                                                             %
115
%                                                                             %
116
%   R e a d M E T A I m a g e                                                 %
117
%                                                                             %
118
%                                                                             %
119
%                                                                             %
120
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121
%
122
%  ReadMETAImage() reads a META image file and returns it.  It
123
%  allocates the memory necessary for the new Image structure and returns a
124
%  pointer to the new image.
125
%
126
%  The format of the ReadMETAImage method is:
127
%
128
%      Image *ReadMETAImage(const ImageInfo *image_info,
129
%        ExceptionInfo *exception)
130
%
131
%  Decompression code contributed by Kyle Shorter.
132
%
133
%  A description of each parameter follows:
134
%
135
%    o image: Method ReadMETAImage returns a pointer to the image after
136
%      reading.  A null image is returned if there is a memory shortage or
137
%      if the image cannot be read.
138
%
139
%    o image_info: Specifies a pointer to an ImageInfo structure.
140
%
141
%    o exception: return any errors or warnings in this structure.
142
%
143
*/
144
145
static const struct
146
{
147
  const unsigned char
148
    len;
149
150
  const char
151
    code[7],
152
    val;
153
} html_codes[] = {
154
#ifdef HANDLE_GT_LT
155
  { 4,"&lt;",'<' },
156
  { 4,"&gt;",'>' },
157
#endif
158
  { 5,"&amp;",'&' },
159
  { 6,"&quot;",'"' },
160
  { 6,"&apos;",'\''}
161
};
162
163
static int stringnicmp(const char *p,const char *q,size_t n)
164
2.84k
{
165
2.84k
  ssize_t
166
2.84k
    i,
167
2.84k
    j;
168
169
2.84k
  if (p == q)
170
0
    return(0);
171
2.84k
  if (p == (char *) NULL)
172
0
    return(-1);
173
2.84k
  if (q == (char *) NULL)
174
0
    return(1);
175
5.70k
  while ((*p != '\0') && (*q != '\0'))
176
5.70k
  {
177
5.70k
    if ((*p == '\0') || (*q == '\0'))
178
0
      break;
179
5.70k
    i=(*p);
180
5.70k
    if (islower((int) ((unsigned char) i)) != 0)
181
101
      i=LocaleToUppercase((int) i);
182
5.70k
    j=(*q);
183
5.70k
    if (islower((int) ((unsigned char) j)) != 0)
184
2.85k
      j=LocaleToUppercase((int) j);
185
5.70k
    if (i != j)
186
2.84k
      break;
187
2.85k
    n--;
188
2.85k
    if (n == 0)
189
0
      break;
190
2.85k
    p++;
191
2.85k
    q++;
192
2.85k
  }
193
2.84k
  return(LocaleToUppercase((int) *p)-LocaleToUppercase((int) *q));
194
2.84k
}
195
196
static size_t convertHTMLcodes(char *s)
197
7.24k
{
198
7.24k
  int
199
7.24k
    value;
200
201
7.24k
  size_t
202
7.24k
    i;
203
204
7.24k
  size_t
205
7.24k
    length;
206
207
7.24k
  length=0;
208
40.1k
  for (i=0; (i < 7U) && (s[i] != '\0'); i++)
209
37.1k
    if (s[i] == ';')
210
4.27k
      {
211
4.27k
        length=i+1;
212
4.27k
        break;
213
4.27k
      }
214
7.24k
  if ((length == 0) || (s == (char *) NULL) || (*s == '\0'))
215
2.97k
    return(0);
216
4.27k
  if ((length > 3) && (s[1] == '#') && (MagickSscanf(s,"&#%d;",&value) == 1))
217
92
    {
218
92
      size_t
219
92
        o;
220
221
92
      o=3;
222
188
      while (s[o] != ';')
223
143
      {
224
143
        o++;
225
143
        if (o > 5)
226
47
          break;
227
143
      }
228
92
      if (o < 6)
229
45
        (void) memmove(s+1,s+1+o,strlen(s+1+o)+1);
230
92
      *s=(char) value;
231
92
      return(o);
232
92
    }
233
16.7k
  for (i=0; i < (ssize_t) (sizeof(html_codes)/sizeof(html_codes[0])); i++)
234
12.5k
  {
235
12.5k
    if (html_codes[i].len <= (ssize_t) length)
236
2.84k
      if (stringnicmp(s,html_codes[i].code,(size_t) (html_codes[i].len)) == 0)
237
0
        {
238
0
          (void) memmove(s+1,s+html_codes[i].len,strlen(s+html_codes[i].len)+1);
239
0
          *s=html_codes[i].val;
240
0
          return(html_codes[i].len-1);
241
0
        }
242
12.5k
  }
243
4.17k
  return(0);
244
4.17k
}
245
246
static char *super_fgets(char **b, size_t *blen, Image *file)
247
0
{
248
0
  int
249
0
    c;
250
251
0
  size_t
252
0
    len;
253
254
0
  unsigned char
255
0
    *p,
256
0
    *q;
257
258
0
  len=*blen;
259
0
  p=(unsigned char *) (*b);
260
0
  for (q=p; ; q++)
261
0
  {
262
0
    c=ReadBlobByte(file);
263
0
    if (c == EOF || c == '\n')
264
0
      break;
265
0
    if ((size_t) (q-p+1) >= len)
266
0
      {
267
0
        size_t
268
0
          tlen;
269
270
0
        unsigned char
271
0
          *buffer;
272
273
0
        tlen=(size_t) (q-p);
274
0
        len<<=1;
275
0
        buffer=(unsigned char *) ResizeQuantumMemory(p,len+2UL,sizeof(*p));
276
0
        p=(unsigned char *) NULL;
277
0
        if (buffer == (unsigned char *) NULL)
278
0
          break;
279
0
        p=buffer;
280
0
        q=p+tlen;
281
0
      }
282
0
    *q=(unsigned char) c;
283
0
  }
284
0
  *b=(char *) p;
285
0
  *blen=0;
286
0
  if (p != (unsigned char *) NULL)
287
0
    {
288
0
      size_t
289
0
        tlen;
290
291
0
      tlen=(size_t) (q-p);
292
0
      if (tlen == 0)
293
0
        return (char *) NULL;
294
0
      p[tlen] = '\0';
295
0
      *blen=++tlen;
296
0
    }
297
0
  return(*b);
298
0
}
299
300
415
#define IPTC_ID 1028
301
0
#define THUMBNAIL_ID 1033
302
303
static ssize_t parse8BIM(Image *ifile, Image *ofile)
304
0
{
305
0
  char
306
0
    brkused,
307
0
    quoted,
308
0
    *line,
309
0
    *token,
310
0
    *newstr,
311
0
    *name;
312
313
0
  int
314
0
    state,
315
0
    next;
316
317
0
  MagickBooleanType
318
0
    status;
319
320
0
  MagickOffsetType
321
0
    savedpos,
322
0
    currentpos;
323
324
0
  size_t
325
0
    inputlen = MagickPathExtent;
326
327
0
  ssize_t
328
0
    savedolen = 0L,
329
0
    outputlen = 0L;
330
331
0
  TokenInfo
332
0
    *token_info;
333
334
0
  unsigned char
335
0
    dataset;
336
337
0
  unsigned int
338
0
    recnum;
339
340
0
  dataset = 0;
341
0
  recnum = 0;
342
0
  line = (char *) AcquireQuantumMemory(inputlen,sizeof(*line));
343
0
  if (line == (char *) NULL)
344
0
    return(-1);
345
0
  newstr = name = token = (char *) NULL;
346
0
  savedpos = 0;
347
0
  status=MagickTrue;
348
0
  token_info=AcquireTokenInfo();
349
0
  while (super_fgets(&line,&inputlen,ifile)!=NULL)
350
0
  {
351
0
    state=0;
352
0
    next=0;
353
354
0
    token=(char *) AcquireQuantumMemory(inputlen,sizeof(*token));
355
0
    if (token == (char *) NULL)
356
0
      break;
357
0
    newstr=(char *) AcquireQuantumMemory(inputlen,sizeof(*newstr));
358
0
    if (newstr == (char *) NULL)
359
0
      break;
360
0
    while (Tokenizer(token_info,0,token,inputlen,line,"","=","\"",0,
361
0
           &brkused,&next,&quoted)==0)
362
0
    {
363
0
      if (state == 0)
364
0
        {
365
0
          int
366
0
            s,
367
0
            n;
368
369
0
          s=0;
370
0
          n=0;
371
0
          while (Tokenizer(token_info,0,newstr,inputlen,token,"","#",
372
0
            "", 0,&brkused,&n,&quoted)==0)
373
0
          {
374
0
            switch (s)
375
0
            {
376
0
              case 0:
377
0
                if (strcmp(newstr,"8BIM")==0)
378
0
                  dataset = 255;
379
0
                else
380
0
                  dataset = (unsigned char) StringToLong(newstr);
381
0
                break;
382
0
              case 1:
383
0
                recnum = (unsigned int) StringToUnsignedLong(newstr);
384
0
                break;
385
0
              case 2:
386
0
              {
387
0
                size_t extent = strlen(newstr)+MagickPathExtent;
388
0
                name=(char *) AcquireQuantumMemory(extent,sizeof(*name));
389
0
                if (name != (char *) NULL)
390
0
                  (void) CopyMagickString(name,newstr,extent);
391
0
                break;
392
0
              }
393
0
            }
394
0
            s++;
395
0
          }
396
0
        }
397
0
      else
398
0
        if (state == 1)
399
0
          {
400
0
            int
401
0
              n;
402
403
0
            ssize_t
404
0
              len;
405
406
0
            n=0;
407
0
            len = (ssize_t) strlen(token);
408
0
            while (Tokenizer(token_info,0,newstr,inputlen,token,"","&",
409
0
              "",0,&brkused,&n,&quoted)==0)
410
0
            {
411
0
              if (brkused && n > 0)
412
0
                {
413
0
                  size_t
414
0
                    codes_length;
415
416
0
                  char
417
0
                    *s = &token[n-1];
418
419
0
                  codes_length=convertHTMLcodes(s);
420
0
                  if ((ssize_t) codes_length > len)
421
0
                    len=0;
422
0
                  else
423
0
                    len-=(ssize_t) codes_length;
424
0
                }
425
0
            }
426
427
0
            if (dataset == 255)
428
0
              {
429
0
                unsigned char
430
0
                  nlen = 0;
431
432
0
                int
433
0
                  i;
434
435
0
                if (savedolen > 0)
436
0
                  {
437
0
                    MagickOffsetType
438
0
                      offset;
439
440
0
                    ssize_t diff = outputlen - savedolen;
441
0
                    currentpos = TellBlob(ofile);
442
0
                    if (currentpos < 0)
443
0
                      {
444
0
                        status=MagickFalse;
445
0
                        break;
446
0
                      }
447
0
                    offset=SeekBlob(ofile,savedpos,SEEK_SET);
448
0
                    if (offset < 0)
449
0
                      {
450
0
                        status=MagickFalse;
451
0
                        break;
452
0
                      }
453
0
                    (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
454
0
                    offset=SeekBlob(ofile,currentpos,SEEK_SET);
455
0
                    if (offset < 0)
456
0
                      {
457
0
                        status=MagickFalse;
458
0
                        break;
459
0
                      }
460
0
                    savedolen = 0L;
461
0
                  }
462
0
                if (outputlen & 1)
463
0
                  {
464
0
                    (void) WriteBlobByte(ofile,0x00);
465
0
                    outputlen++;
466
0
                  }
467
0
                (void) WriteBlobString(ofile,"8BIM");
468
0
                (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
469
0
                outputlen += 6;
470
0
                if (name)
471
0
                  nlen = (unsigned char) strlen(name);
472
0
                (void) WriteBlobByte(ofile,nlen);
473
0
                outputlen++;
474
0
                for (i=0; i<nlen; i++)
475
0
                  (void) WriteBlobByte(ofile,(unsigned char) name[i]);
476
0
                outputlen += nlen;
477
0
                if ((nlen & 0x01) == 0)
478
0
                  {
479
0
                    (void) WriteBlobByte(ofile,0x00);
480
0
                    outputlen++;
481
0
                  }
482
0
                if (recnum != IPTC_ID)
483
0
                  {
484
0
                    (void) WriteBlobMSBLong(ofile, (unsigned int) len);
485
0
                    outputlen += 4;
486
487
0
                    n=0;
488
0
                    outputlen += len;
489
0
                    while (len-- > 0)
490
0
                      (void) WriteBlobByte(ofile,(unsigned char) token[n++]);
491
492
0
                    if (outputlen & 1)
493
0
                      {
494
0
                        (void) WriteBlobByte(ofile,0x00);
495
0
                        outputlen++;
496
0
                      }
497
0
                  }
498
0
                else
499
0
                  {
500
                    /* patch in a fake length for now and fix it later */
501
0
                    savedpos = TellBlob(ofile);
502
0
                    if (savedpos < 0)
503
0
                      {
504
0
                        status=MagickFalse;
505
0
                        break;
506
0
                      }
507
0
                    (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU);
508
0
                    outputlen += 4;
509
0
                    savedolen = outputlen;
510
0
                  }
511
0
              }
512
0
            else
513
0
              {
514
0
                if (len <= 0x7FFF)
515
0
                  {
516
0
                    (void) WriteBlobByte(ofile,0x1c);
517
0
                    (void) WriteBlobByte(ofile,(unsigned char) dataset);
518
0
                    (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
519
0
                    (void) WriteBlobMSBShort(ofile,(unsigned short) len);
520
0
                    outputlen += 5;
521
0
                    n=0;
522
0
                    outputlen += len;
523
0
                    while (len-- > 0)
524
0
                      (void) WriteBlobByte(ofile,(unsigned char) token[n++]);
525
0
                  }
526
0
              }
527
0
          }
528
0
      state++;
529
0
    }
530
0
    if (token != (char *) NULL)
531
0
      token=DestroyString(token);
532
0
    if (newstr != (char *) NULL)
533
0
      newstr=DestroyString(newstr);
534
0
    if (name != (char *) NULL)
535
0
      name=DestroyString(name);
536
0
    if (status == MagickFalse)
537
0
      break;
538
0
  }
539
0
  token_info=DestroyTokenInfo(token_info);
540
0
  if (token != (char *) NULL)
541
0
    token=DestroyString(token);
542
0
  if (newstr != (char *) NULL)
543
0
    newstr=DestroyString(newstr);
544
0
  if (name != (char *) NULL)
545
0
    name=DestroyString(name);
546
0
  line=DestroyString(line);
547
0
  if (savedolen > 0)
548
0
    {
549
0
      MagickOffsetType
550
0
        offset;
551
552
0
      ssize_t diff = outputlen - savedolen;
553
554
0
      currentpos = TellBlob(ofile);
555
0
      if (currentpos < 0)
556
0
        return(-1);
557
0
      offset=SeekBlob(ofile,savedpos,SEEK_SET);
558
0
      if (offset < 0)
559
0
        return(-1);
560
0
      (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
561
0
      offset=SeekBlob(ofile,currentpos,SEEK_SET);
562
0
      if (offset < 0)
563
0
        return(-1);
564
0
      savedolen = 0L;
565
0
    }
566
0
  return(status == MagickFalse ? -1 : outputlen);
567
0
}
568
569
static char *super_fgets_w(char **b, size_t *blen, Image *file)
570
3.48k
{
571
3.48k
  int
572
3.48k
    c;
573
574
3.48k
  size_t
575
3.48k
    len;
576
577
3.48k
  unsigned char
578
3.48k
    *p,
579
3.48k
    *q;
580
581
3.48k
  len=*blen;
582
3.48k
  p=(unsigned char *) (*b);
583
68.1k
  for (q=p; ; q++)
584
71.6k
  {
585
71.6k
    c=ReadBlobLSBSignedShort(file);
586
71.6k
    if ((c == -1) || (c == '\n'))
587
3.20k
      break;
588
68.4k
   if (EOFBlob(file))
589
283
      break;
590
68.1k
   if ((size_t) (q-p+1) >= len)
591
2.10k
      {
592
2.10k
        size_t
593
2.10k
          tlen;
594
595
2.10k
        unsigned char
596
2.10k
          *buffer;
597
598
2.10k
        tlen=(size_t) (q-p);
599
2.10k
        len<<=1;
600
2.10k
        buffer=(unsigned char *) ResizeQuantumMemory(p,len+2,sizeof(*p));
601
2.10k
        p=(unsigned char *) NULL;
602
2.10k
        if (buffer == (unsigned char *) NULL)
603
0
          break;
604
2.10k
        p=buffer;
605
2.10k
        q=p+tlen;
606
2.10k
      }
607
68.1k
    *q=(unsigned char) c;
608
68.1k
  }
609
3.48k
  *b=(char *) p;
610
3.48k
  *blen=0;
611
3.48k
  if (p != (unsigned char *) NULL)
612
3.48k
    {
613
3.48k
      size_t
614
3.48k
        tlen;
615
616
3.48k
      tlen=(size_t) (q-p);
617
3.48k
      if (tlen == 0)
618
149
        return (char *) NULL;
619
3.33k
      p[tlen]='\0';
620
3.33k
      *blen=++tlen;
621
3.33k
    }
622
3.33k
  return(*b);
623
3.48k
}
624
625
static ssize_t parse8BIMW(Image *ifile, Image *ofile)
626
149
{
627
149
  char
628
149
    brkused,
629
149
    quoted,
630
149
    *line,
631
149
    *token,
632
149
    *newstr,
633
149
    *name;
634
635
149
  int
636
149
    state,
637
149
    next;
638
639
149
  unsigned char
640
149
    dataset;
641
642
149
  unsigned int
643
149
    recnum;
644
645
149
  size_t
646
149
    inputlen = MagickPathExtent;
647
648
149
  ssize_t
649
149
    savedolen = 0L,
650
149
    outputlen = 0L;
651
652
149
  MagickBooleanType
653
149
    status;
654
655
149
  MagickOffsetType
656
149
    savedpos,
657
149
    currentpos;
658
659
149
  TokenInfo
660
149
    *token_info;
661
662
149
  dataset = 0;
663
149
  recnum = 0;
664
149
  line=(char *) AcquireQuantumMemory(inputlen,sizeof(*line));
665
149
  if (line == (char *) NULL)
666
0
    return(-1);
667
149
  newstr = name = token = (char *) NULL;
668
149
  savedpos = 0;
669
149
  token_info=AcquireTokenInfo();
670
149
  status=MagickTrue;
671
3.48k
  while (super_fgets_w(&line,&inputlen,ifile) != NULL)
672
3.33k
  {
673
3.33k
    state=0;
674
3.33k
    next=0;
675
676
3.33k
    token=(char *) AcquireQuantumMemory(inputlen,sizeof(*token));
677
3.33k
    if (token == (char *) NULL)
678
0
      break;
679
3.33k
    newstr=(char *) AcquireQuantumMemory(inputlen,sizeof(*newstr));
680
3.33k
    if (newstr == (char *) NULL)
681
0
      break;
682
9.90k
    while (Tokenizer(token_info,0,token,inputlen,line,"","=","\"",0,
683
9.90k
      &brkused,&next,&quoted)==0)
684
6.57k
    {
685
6.57k
      if (state == 0)
686
3.22k
        {
687
3.22k
          int
688
3.22k
            s,
689
3.22k
            n;
690
691
3.22k
          s=0;
692
3.22k
          n=0;
693
7.48k
          while (Tokenizer(token_info,0,newstr,inputlen,token,"","#",
694
7.48k
            "",0,&brkused,&n,&quoted)==0)
695
4.26k
          {
696
4.26k
            switch (s)
697
4.26k
            {
698
2.62k
              case 0:
699
2.62k
                if (strcmp(newstr,"8BIM")==0)
700
340
                  dataset = 255;
701
2.28k
                else
702
2.28k
                  dataset = (unsigned char) StringToLong(newstr);
703
2.62k
                break;
704
681
              case 1:
705
681
                recnum=(unsigned int) StringToUnsignedLong(newstr);
706
681
                break;
707
538
              case 2:
708
538
                name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent,
709
538
                  sizeof(*name));
710
538
                if (name)
711
538
                  (void) CopyMagickString(name,newstr,strlen(newstr)+MagickPathExtent);
712
538
                break;
713
4.26k
            }
714
4.26k
            s++;
715
4.26k
          }
716
3.22k
        }
717
3.34k
      else
718
3.34k
        if (state == 1)
719
1.49k
          {
720
1.49k
            int
721
1.49k
              n;
722
723
1.49k
            ssize_t
724
1.49k
              len;
725
726
1.49k
            n=0;
727
1.49k
            len = (ssize_t) strlen(token);
728
9.53k
            while (Tokenizer(token_info,0,newstr,inputlen,token,"","&",
729
9.53k
              "",0,&brkused,&n,&quoted)==0)
730
8.03k
            {
731
8.03k
              if (brkused && n > 0)
732
7.24k
                {
733
7.24k
                  size_t
734
7.24k
                    codes_length;
735
736
7.24k
                  char
737
7.24k
                    *s = &token[n-1];
738
739
7.24k
                  codes_length=convertHTMLcodes(s);
740
7.24k
                  if ((ssize_t) codes_length > len)
741
0
                    len=0;
742
7.24k
                  else
743
7.24k
                    len-=(ssize_t) codes_length;
744
7.24k
                }
745
8.03k
            }
746
747
1.49k
            if (dataset == 255)
748
415
              {
749
415
                unsigned char
750
415
                  nlen = 0;
751
752
415
                int
753
415
                  i;
754
755
415
                if (savedolen > 0)
756
0
                  {
757
0
                    MagickOffsetType
758
0
                      offset;
759
760
0
                    ssize_t
761
0
                      diff = outputlen - savedolen;
762
763
0
                    currentpos = TellBlob(ofile);
764
0
                    if (currentpos < 0)
765
0
                      {
766
0
                        status=MagickFalse;
767
0
                        break;
768
0
                      }
769
0
                    offset=SeekBlob(ofile,savedpos,SEEK_SET);
770
0
                    if (offset < 0)
771
0
                      {
772
0
                        status=MagickFalse;
773
0
                        break;
774
0
                      }
775
0
                    (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
776
0
                    offset=SeekBlob(ofile,currentpos,SEEK_SET);
777
0
                    if (offset < 0)
778
0
                      {
779
0
                        status=MagickFalse;
780
0
                        break;
781
0
                      }
782
0
                    savedolen = 0L;
783
0
                  }
784
415
                if (outputlen & 1)
785
67
                  {
786
67
                    (void) WriteBlobByte(ofile,0x00);
787
67
                    outputlen++;
788
67
                  }
789
415
                (void) WriteBlobString(ofile,"8BIM");
790
415
                (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
791
415
                outputlen += 6;
792
415
                if (name)
793
90
                  nlen = (unsigned char) strlen(name);
794
415
                (void) WriteBlobByte(ofile,(unsigned char) nlen);
795
415
                outputlen++;
796
1.32k
                for (i=0; i<nlen; i++)
797
912
                  (void) WriteBlobByte(ofile,(unsigned char) name[i]);
798
415
                outputlen += nlen;
799
415
                if ((nlen & 0x01) == 0)
800
329
                  {
801
329
                    (void) WriteBlobByte(ofile,0x00);
802
329
                    outputlen++;
803
329
                  }
804
415
                if (recnum != IPTC_ID)
805
415
                  {
806
415
                    (void) WriteBlobMSBLong(ofile,(unsigned int) len);
807
415
                    outputlen += 4;
808
809
415
                    n=0;
810
415
                    outputlen += len;
811
19.2k
                    while (len-- > 0)
812
18.8k
                      (void) WriteBlobByte(ofile,(unsigned char) token[n++]);
813
814
415
                    if (outputlen & 1)
815
126
                      {
816
126
                        (void) WriteBlobByte(ofile,0x00);
817
126
                        outputlen++;
818
126
                      }
819
415
                  }
820
0
                else
821
0
                  {
822
                    /* patch in a fake length for now and fix it later */
823
0
                    savedpos = TellBlob(ofile);
824
0
                    if (savedpos < 0)
825
0
                      {
826
0
                        status=MagickFalse;
827
0
                        break;
828
0
                      }
829
0
                    (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU);
830
0
                    outputlen += 4;
831
0
                    savedolen = outputlen;
832
0
                  }
833
415
              }
834
1.08k
            else
835
1.08k
              {
836
1.08k
                if (len <= 0x7FFF)
837
1.08k
                  {
838
1.08k
                    (void) WriteBlobByte(ofile,0x1c);
839
1.08k
                    (void) WriteBlobByte(ofile,dataset);
840
1.08k
                    (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
841
1.08k
                    (void) WriteBlobMSBShort(ofile,(unsigned short) len);
842
1.08k
                    outputlen += 5;
843
1.08k
                    n=0;
844
1.08k
                    outputlen += len;
845
2.64k
                    while (len-- > 0)
846
1.55k
                      (void) WriteBlobByte(ofile,(unsigned char) token[n++]);
847
1.08k
                  }
848
1.08k
              }
849
1.49k
          }
850
6.57k
      state++;
851
6.57k
    }
852
3.33k
    if (token != (char *) NULL)
853
3.33k
      token=DestroyString(token);
854
3.33k
    if (newstr != (char *) NULL)
855
3.33k
      newstr=DestroyString(newstr);
856
3.33k
    if (name != (char *) NULL)
857
538
      name=DestroyString(name);
858
3.33k
    if (status == MagickFalse)
859
0
      break;
860
3.33k
  }
861
149
  token_info=DestroyTokenInfo(token_info);
862
149
  if (token != (char *) NULL)
863
0
    token=DestroyString(token);
864
149
  if (newstr != (char *) NULL)
865
0
    newstr=DestroyString(newstr);
866
149
  if (name != (char *) NULL)
867
0
    name=DestroyString(name);
868
149
  line=DestroyString(line);
869
149
  if (savedolen > 0)
870
0
    {
871
0
      MagickOffsetType
872
0
        offset;
873
874
0
      ssize_t diff = outputlen - savedolen;
875
876
0
      currentpos = TellBlob(ofile);
877
0
      if (currentpos < 0)
878
0
        return(-1);
879
0
      offset=SeekBlob(ofile,savedpos,SEEK_SET);
880
0
      if (offset < 0)
881
0
        return(-1);
882
0
      (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
883
0
      offset=SeekBlob(ofile,currentpos,SEEK_SET);
884
0
      if (offset < 0)
885
0
        return(-1);
886
0
      savedolen = 0L;
887
0
    }
888
149
  return(status == MagickFalse ? -1 : outputlen);
889
149
}
890
891
/* some defines for the different JPEG block types */
892
#define M_SOF0  0xC0            /* Start Of Frame N */
893
#define M_SOF1  0xC1            /* N indicates which compression process */
894
#define M_SOF2  0xC2            /* Only SOF0-SOF2 are now in common use */
895
#define M_SOF3  0xC3
896
#define M_SOF5  0xC5            /* NB: codes C4 and CC are NOT SOF markers */
897
#define M_SOF6  0xC6
898
#define M_SOF7  0xC7
899
#define M_SOF9  0xC9
900
#define M_SOF10 0xCA
901
#define M_SOF11 0xCB
902
#define M_SOF13 0xCD
903
#define M_SOF14 0xCE
904
#define M_SOF15 0xCF
905
0
#define M_SOI   0xD8
906
0
#define M_EOI   0xD9            /* End Of Image (end of datastream) */
907
0
#define M_SOS   0xDA            /* Start Of Scan (begins compressed data) */
908
0
#define M_APP0  0xe0
909
#define M_APP1  0xe1
910
#define M_APP2  0xe2
911
#define M_APP3  0xe3
912
#define M_APP4  0xe4
913
#define M_APP5  0xe5
914
#define M_APP6  0xe6
915
#define M_APP7  0xe7
916
#define M_APP8  0xe8
917
#define M_APP9  0xe9
918
#define M_APP10 0xea
919
#define M_APP11 0xeb
920
#define M_APP12 0xec
921
0
#define M_APP13 0xed
922
#define M_APP14 0xee
923
#define M_APP15 0xef
924
925
static int jpeg_transfer_1(Image *ifile, Image *ofile)
926
0
{
927
0
  int c;
928
929
0
  c = ReadBlobByte(ifile);
930
0
  if (c == EOF)
931
0
    return EOF;
932
0
  (void) WriteBlobByte(ofile,(unsigned char) c);
933
0
  return c;
934
0
}
935
936
#if defined(future)
937
static int jpeg_skip_1(Image *ifile)
938
{
939
  int c;
940
941
  c = ReadBlobByte(ifile);
942
  if (c == EOF)
943
    return EOF;
944
  return c;
945
}
946
#endif
947
948
static int jpeg_read_remaining(Image *ifile, Image *ofile)
949
0
{
950
0
   int c;
951
952
0
  while ((c = jpeg_transfer_1(ifile, ofile)) != EOF)
953
0
    continue;
954
0
  return M_EOI;
955
0
}
956
957
static int jpeg_skip_variable(Image *ifile, Image *ofile)
958
0
{
959
0
  unsigned int  length;
960
0
  int c1,c2;
961
962
0
  if ((c1 = jpeg_transfer_1(ifile, ofile)) == EOF)
963
0
    return M_EOI;
964
0
  if ((c2 = jpeg_transfer_1(ifile, ofile)) == EOF)
965
0
    return M_EOI;
966
967
0
  length = (((unsigned int) c1) << 8) + ((unsigned int) c2);
968
0
  length -= 2;
969
970
0
  while (length--)
971
0
    if (jpeg_transfer_1(ifile, ofile) == EOF)
972
0
      return M_EOI;
973
974
0
  return 0;
975
0
}
976
977
static int jpeg_skip_variable2(Image *ifile, Image *ofile)
978
0
{
979
0
  unsigned int  length;
980
0
  int c1,c2;
981
982
0
  (void) ofile;
983
0
  if ((c1 = ReadBlobByte(ifile)) == EOF) return M_EOI;
984
0
  if ((c2 = ReadBlobByte(ifile)) == EOF) return M_EOI;
985
986
0
  length = (((unsigned int) c1) << 8) + ((unsigned int) c2);
987
0
  length -= 2;
988
989
0
  while (length--)
990
0
    if (ReadBlobByte(ifile) == EOF)
991
0
      return M_EOI;
992
993
0
  return 0;
994
0
}
995
996
static int jpeg_nextmarker(Image *ifile, Image *ofile)
997
0
{
998
0
  int c;
999
1000
  /* transfer anything until we hit 0xff */
1001
0
  do
1002
0
  {
1003
0
    c = ReadBlobByte(ifile);
1004
0
    if (c == EOF)
1005
0
      return M_EOI; /* we hit EOF */
1006
0
    else
1007
0
      if (c != 0xff)
1008
0
        (void) WriteBlobByte(ofile,(unsigned char) c);
1009
0
  } while (c != 0xff);
1010
1011
  /* get marker byte, swallowing possible padding */
1012
0
  do
1013
0
  {
1014
0
    c = ReadBlobByte(ifile);
1015
0
    if (c == EOF)
1016
0
      return M_EOI; /* we hit EOF */
1017
0
  } while (c == 0xff);
1018
1019
0
  return c;
1020
0
}
1021
1022
#if defined(future)
1023
static int jpeg_skip_till_marker(Image *ifile, int marker)
1024
{
1025
  int c, i;
1026
1027
  do
1028
  {
1029
    /* skip anything until we hit 0xff */
1030
    i = 0;
1031
    do
1032
    {
1033
      c = ReadBlobByte(ifile);
1034
      i++;
1035
      if (c == EOF)
1036
        return M_EOI; /* we hit EOF */
1037
    } while (c != 0xff);
1038
1039
    /* get marker byte, swallowing possible padding */
1040
    do
1041
    {
1042
      c = ReadBlobByte(ifile);
1043
      if (c == EOF)
1044
        return M_EOI; /* we hit EOF */
1045
    } while (c == 0xff);
1046
  } while (c != marker);
1047
  return c;
1048
}
1049
#endif
1050
1051
/* Embed binary IPTC data into a JPEG image. */
1052
static int jpeg_embed(Image *ifile, Image *ofile, Image *iptc)
1053
0
{
1054
0
  unsigned int marker;
1055
0
  unsigned int done = 0;
1056
0
  unsigned int len;
1057
0
  int inx;
1058
1059
0
  if (jpeg_transfer_1(ifile, ofile) != 0xFF)
1060
0
    return 0;
1061
0
  if (jpeg_transfer_1(ifile, ofile) != M_SOI)
1062
0
    return 0;
1063
1064
0
  while (done == MagickFalse)
1065
0
  {
1066
0
    marker=(unsigned int) jpeg_nextmarker(ifile, ofile);
1067
0
    if (marker == M_EOI)
1068
0
      { /* EOF */
1069
0
        break;
1070
0
      }
1071
0
    else
1072
0
      {
1073
0
        if (marker != M_APP13)
1074
0
          {
1075
0
            (void) WriteBlobByte(ofile,0xff);
1076
0
            (void) WriteBlobByte(ofile,(unsigned char) marker);
1077
0
          }
1078
0
      }
1079
1080
0
    switch (marker)
1081
0
    {
1082
0
      case M_APP13:
1083
        /* we are going to write a new APP13 marker, so don't output the old one */
1084
0
        jpeg_skip_variable2(ifile, ofile);
1085
0
        break;
1086
1087
0
      case M_APP0:
1088
        /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
1089
0
        jpeg_skip_variable(ifile, ofile);
1090
1091
0
        if (iptc != (Image *) NULL)
1092
0
          {
1093
0
            char
1094
0
              psheader[] = "\xFF\xED\0\0Photoshop 3.0\0" "8BIM\x04\x04\0\0\0\0";
1095
1096
0
            len=(unsigned int) GetBlobSize(iptc);
1097
0
            if (len & 1)
1098
0
              len++; /* make the length even */
1099
0
            psheader[2]=(char) ((len+16)>>8);
1100
0
            psheader[3]=(char) ((len+16)&0xff);
1101
0
            for (inx = 0; inx < 18; inx++)
1102
0
              (void) WriteBlobByte(ofile,(unsigned char) psheader[inx]);
1103
0
            jpeg_read_remaining(iptc, ofile);
1104
0
            len=(unsigned int) GetBlobSize(iptc);
1105
0
            if (len & 1)
1106
0
              (void) WriteBlobByte(ofile,0);
1107
0
          }
1108
0
        break;
1109
1110
0
      case M_SOS:
1111
        /* we hit data, no more marker-inserting can be done! */
1112
0
        jpeg_read_remaining(ifile, ofile);
1113
0
        done = 1;
1114
0
        break;
1115
1116
0
      default:
1117
0
        jpeg_skip_variable(ifile, ofile);
1118
0
        break;
1119
0
    }
1120
0
  }
1121
0
  return 1;
1122
0
}
1123
1124
/* handle stripping the APP13 data out of a JPEG */
1125
#if defined(future)
1126
static void jpeg_strip(Image *ifile, Image *ofile)
1127
{
1128
  unsigned int marker;
1129
1130
  marker = jpeg_skip_till_marker(ifile, M_SOI);
1131
  if (marker == M_SOI)
1132
  {
1133
    (void) WriteBlobByte(ofile,0xff);
1134
    (void) WriteBlobByte(ofile,M_SOI);
1135
    jpeg_read_remaining(ifile, ofile);
1136
  }
1137
}
1138
1139
/* Extract any APP13 binary data into a file. */
1140
static int jpeg_extract(Image *ifile, Image *ofile)
1141
{
1142
  unsigned int marker;
1143
  unsigned int done = 0;
1144
1145
  if (jpeg_skip_1(ifile) != 0xff)
1146
    return 0;
1147
  if (jpeg_skip_1(ifile) != M_SOI)
1148
    return 0;
1149
1150
  while (done == MagickFalse)
1151
  {
1152
    marker = jpeg_skip_till_marker(ifile, M_APP13);
1153
    if (marker == M_APP13)
1154
      {
1155
        marker = jpeg_nextmarker(ifile, ofile);
1156
        break;
1157
      }
1158
  }
1159
  return 1;
1160
}
1161
#endif
1162
1163
static inline void CopyBlob(Image *source,Image *destination)
1164
815
{
1165
815
  ssize_t
1166
815
    i;
1167
1168
815
  unsigned char
1169
815
    *buffer;
1170
1171
815
  ssize_t
1172
815
    count,
1173
815
    length;
1174
1175
815
  buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
1176
815
    sizeof(*buffer));
1177
815
  if (buffer != (unsigned char *) NULL)
1178
815
    {
1179
815
      (void) memset(buffer,0,MagickMaxBufferExtent*sizeof(*buffer));
1180
815
      i=0;
1181
1.63k
      while ((length=ReadBlob(source,MagickMaxBufferExtent,buffer)) != 0)
1182
815
      {
1183
815
        count=0;
1184
1.63k
        for (i=0; i < (ssize_t) length; i+=count)
1185
815
        {
1186
815
          count=WriteBlob(destination,(size_t) (length-i),buffer+i);
1187
815
          if (count <= 0)
1188
0
            break;
1189
815
        }
1190
815
        if (i < (ssize_t) length)
1191
0
          break;
1192
815
      }
1193
815
      buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1194
815
    }
1195
815
}
1196
1197
static Image *ReadMETAImage(const ImageInfo *image_info,
1198
  ExceptionInfo *exception)
1199
964
{
1200
964
  Image
1201
964
    *buff,
1202
964
    *image;
1203
1204
964
  MagickBooleanType
1205
964
    status;
1206
1207
964
  StringInfo
1208
964
    *profile;
1209
1210
964
  size_t
1211
964
    length;
1212
1213
964
  unsigned char
1214
964
    *blob;
1215
1216
  /*
1217
    Open file containing binary metadata
1218
  */
1219
964
  assert(image_info != (const ImageInfo *) NULL);
1220
964
  assert(image_info->signature == MagickCoreSignature);
1221
964
  assert(exception != (ExceptionInfo *) NULL);
1222
964
  assert(exception->signature == MagickCoreSignature);
1223
964
  if (IsEventLogging() != MagickFalse)
1224
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1225
0
      image_info->filename);
1226
964
  image=AcquireImage(image_info,exception);
1227
964
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1228
964
  if (status == MagickFalse)
1229
0
    {
1230
0
      image=DestroyImageList(image);
1231
0
      return((Image *) NULL);
1232
0
    }
1233
964
  image->columns=1;
1234
964
  image->rows=1;
1235
964
  if (SetImageBackgroundColor(image,exception) == MagickFalse)
1236
0
    {
1237
0
      image=DestroyImageList(image);
1238
0
      return((Image *) NULL);
1239
0
    }
1240
964
  length=1;
1241
964
  if (LocaleNCompare(image_info->magick,"8BIM",4) == 0)
1242
513
    {
1243
      /*
1244
        Read 8BIM binary metadata.
1245
      */
1246
513
      buff=AcquireImage((ImageInfo *) NULL,exception);
1247
513
      if (buff == (Image *) NULL)
1248
513
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1249
513
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1250
513
      if (blob == (unsigned char *) NULL)
1251
0
        {
1252
0
          buff=DestroyImage(buff);
1253
0
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1254
0
        }
1255
513
      (void) memset(blob,0,length);
1256
513
      AttachBlob(buff->blob,blob,length);
1257
513
      if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
1258
0
        {
1259
0
          length=(size_t) parse8BIM(image, buff);
1260
0
          if (length == 0)
1261
0
            {
1262
0
              blob=(unsigned char *) DetachBlob(buff->blob);
1263
0
              blob=(unsigned char *) RelinquishMagickMemory(blob);
1264
0
              buff=DestroyImage(buff);
1265
0
              ThrowReaderException(CorruptImageError,"CorruptImage");
1266
0
            }
1267
0
          if (length & 1)
1268
0
            (void) WriteBlobByte(buff,0x0);
1269
0
        }
1270
513
      else if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
1271
149
        {
1272
149
          length=(size_t) parse8BIMW(image, buff);
1273
149
          if (length == 0)
1274
18
            {
1275
18
              blob=(unsigned char *) DetachBlob(buff->blob);
1276
18
              blob=(unsigned char *) RelinquishMagickMemory(blob);
1277
18
              buff=DestroyImage(buff);
1278
18
              ThrowReaderException(CorruptImageError,"CorruptImage");
1279
0
            }
1280
131
          if (length & 1)
1281
21
            (void) WriteBlobByte(buff,0x0);
1282
131
        }
1283
364
      else
1284
364
        CopyBlob(image,buff);
1285
495
      profile=BlobToProfileStringInfo("8bim",GetBlobStreamData(buff),(size_t)
1286
495
        GetBlobSize(buff),exception);
1287
495
      SetImageProfilePrivate(image,profile,exception);
1288
495
      blob=(unsigned char *) DetachBlob(buff->blob);
1289
495
      blob=(unsigned char *) RelinquishMagickMemory(blob);
1290
495
      buff=DestroyImage(buff);
1291
495
      if (status == MagickFalse)
1292
495
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1293
495
    }
1294
946
  if (LocaleNCompare(image_info->magick,"APP1",4) == 0)
1295
0
    {
1296
0
      char
1297
0
        name[MagickPathExtent];
1298
1299
0
      (void) FormatLocaleString(name,MagickPathExtent,"APP%d",1);
1300
0
      buff=AcquireImage((ImageInfo *) NULL,exception);
1301
0
      if (buff == (Image *) NULL)
1302
0
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1303
0
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1304
0
      if (blob == (unsigned char *) NULL)
1305
0
        {
1306
0
          buff=DestroyImage(buff);
1307
0
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1308
0
        }
1309
0
      AttachBlob(buff->blob,blob,length);
1310
0
      if (LocaleCompare(image_info->magick,"APP1JPEG") == 0)
1311
0
        {
1312
0
          Image
1313
0
            *iptc;
1314
1315
0
          int
1316
0
            result;
1317
1318
0
          if (image_info->profile == (void *) NULL)
1319
0
            {
1320
0
              blob=(unsigned char *) DetachBlob(buff->blob);
1321
0
              blob=(unsigned char *) RelinquishMagickMemory(blob);
1322
0
              buff=DestroyImage(buff);
1323
0
              ThrowReaderException(CoderError,"NoIPTCProfileAvailable");
1324
0
            }
1325
0
          profile=CloneStringInfo((StringInfo *) image_info->profile);
1326
0
          iptc=AcquireImage((ImageInfo *) NULL,exception);
1327
0
          if (iptc == (Image *) NULL)
1328
0
            {
1329
0
              blob=(unsigned char *) DetachBlob(buff->blob);
1330
0
              blob=(unsigned char *) RelinquishMagickMemory(blob);
1331
0
              buff=DestroyImage(buff);
1332
0
              ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1333
0
            }
1334
0
          AttachBlob(iptc->blob,GetStringInfoDatum(profile),
1335
0
            GetStringInfoLength(profile));
1336
0
          profile->datum=(unsigned char *) NULL;
1337
0
          profile->length=0;
1338
0
          profile=DestroyStringInfo(profile);
1339
0
          result=jpeg_embed(image,buff,iptc);
1340
0
          blob=(unsigned char *) DetachBlob(iptc->blob);
1341
0
          blob=(unsigned char *) RelinquishMagickMemory(blob);
1342
0
          iptc=DestroyImage(iptc);
1343
0
          if (result == 0)
1344
0
            {
1345
0
              blob=(unsigned char *) DetachBlob(buff->blob);
1346
0
              blob=(unsigned char *) RelinquishMagickMemory(blob);
1347
0
              buff=DestroyImage(buff);
1348
0
              ThrowReaderException(CoderError,"JPEGEmbeddingFailed");
1349
0
            }
1350
0
        }
1351
0
      else
1352
0
        CopyBlob(image,buff);
1353
0
      profile=BlobToProfileStringInfo(name,GetBlobStreamData(buff),(size_t)
1354
0
        GetBlobSize(buff),exception);
1355
0
      (void) SetImageProfilePrivate(image,profile,exception);
1356
0
      blob=(unsigned char *) DetachBlob(buff->blob);
1357
0
      blob=(unsigned char *) RelinquishMagickMemory(blob);
1358
0
      buff=DestroyImage(buff);
1359
0
    }
1360
946
  if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
1361
944
      (LocaleCompare(image_info->magick,"ICM") == 0))
1362
3
    {
1363
3
      buff=AcquireImage((ImageInfo *) NULL,exception);
1364
3
      if (buff == (Image *) NULL)
1365
3
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1366
3
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1367
3
      if (blob == (unsigned char *) NULL)
1368
0
        {
1369
0
          buff=DestroyImage(buff);
1370
0
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1371
0
        }
1372
3
      AttachBlob(buff->blob,blob,length);
1373
3
      CopyBlob(image,buff);
1374
3
      profile=BlobToProfileStringInfo("icc",GetBlobStreamData(buff),(size_t)
1375
3
        GetBlobSize(buff),exception);
1376
3
      (void) SetImageProfilePrivate(image,profile,exception);
1377
3
      blob=(unsigned char *) DetachBlob(buff->blob);
1378
3
      blob=(unsigned char *) RelinquishMagickMemory(blob);
1379
3
      buff=DestroyImage(buff);
1380
3
    }
1381
946
  if (LocaleCompare(image_info->magick,"IPTC") == 0)
1382
446
    {
1383
446
      buff=AcquireImage((ImageInfo *) NULL,exception);
1384
446
      if (buff == (Image *) NULL)
1385
446
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1386
446
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1387
446
      if (blob == (unsigned char *) NULL)
1388
0
        {
1389
0
          buff=DestroyImage(buff);
1390
0
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1391
0
        }
1392
446
      AttachBlob(buff->blob,blob,length);
1393
446
      CopyBlob(image,buff);
1394
446
      profile=BlobToProfileStringInfo("iptc",GetBlobStreamData(buff),(size_t)
1395
446
        GetBlobSize(buff),exception);
1396
446
      (void) SetImageProfilePrivate(image,profile,exception);
1397
446
      blob=(unsigned char *) DetachBlob(buff->blob);
1398
446
      blob=(unsigned char *) RelinquishMagickMemory(blob);
1399
446
      buff=DestroyImage(buff);
1400
446
    }
1401
946
  if (LocaleCompare(image_info->magick,"XMP") == 0)
1402
2
    {
1403
2
      buff=AcquireImage((ImageInfo *) NULL,exception);
1404
2
      if (buff == (Image *) NULL)
1405
2
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1406
2
      blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1407
2
      if (blob == (unsigned char *) NULL)
1408
0
        {
1409
0
          buff=DestroyImage(buff);
1410
0
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1411
0
        }
1412
2
      AttachBlob(buff->blob,blob,length);
1413
2
      CopyBlob(image,buff);
1414
2
      profile=BlobToProfileStringInfo("xmp",GetBlobStreamData(buff),(size_t)
1415
2
        GetBlobSize(buff),exception);
1416
2
      (void) SetImageProfilePrivate(image,profile,exception);
1417
2
      blob=(unsigned char *) DetachBlob(buff->blob);
1418
2
      blob=(unsigned char *) RelinquishMagickMemory(blob);
1419
2
      buff=DestroyImage(buff);
1420
2
    }
1421
946
  if (CloseBlob(image) == MagickFalse)
1422
0
    status=MagickFalse;
1423
946
  if (status == MagickFalse)
1424
0
    return(DestroyImageList(image));
1425
946
  return(GetFirstImageInList(image));
1426
946
}
1427

1428
/*
1429
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430
%                                                                             %
1431
%                                                                             %
1432
%                                                                             %
1433
%   R e g i s t e r M E T A I m a g e                                         %
1434
%                                                                             %
1435
%                                                                             %
1436
%                                                                             %
1437
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438
%
1439
%  RegisterMETAImage() adds attributes for the META image format to
1440
%  the list of supported formats.  The attributes include the image format
1441
%  tag, a method to read and/or write the format, whether the format
1442
%  supports the saving of more than one frame to the same file or blob,
1443
%  whether the format supports native in-memory I/O, and a brief
1444
%  description of the format.
1445
%
1446
%  The format of the RegisterMETAImage method is:
1447
%
1448
%      size_t RegisterMETAImage(void)
1449
%
1450
*/
1451
ModuleExport size_t RegisterMETAImage(void)
1452
8
{
1453
8
  MagickInfo
1454
8
    *entry;
1455
1456
8
  entry=AcquireMagickInfo("META","8BIM","Photoshop resource format");
1457
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1458
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1459
8
  entry->flags^=CoderAdjoinFlag;
1460
8
  entry->flags|=CoderStealthFlag;
1461
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1462
8
  (void) RegisterMagickInfo(entry);
1463
8
  entry=AcquireMagickInfo("META","8BIMTEXT","Photoshop resource text format");
1464
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1465
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1466
8
  entry->flags^=CoderAdjoinFlag;
1467
8
  entry->flags|=CoderStealthFlag;
1468
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1469
8
  (void) RegisterMagickInfo(entry);
1470
8
  entry=AcquireMagickInfo("META","8BIMWTEXT",
1471
8
    "Photoshop resource wide text format");
1472
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1473
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1474
8
  entry->flags^=CoderAdjoinFlag;
1475
8
  entry->flags|=CoderStealthFlag;
1476
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1477
8
  (void) RegisterMagickInfo(entry);
1478
8
  entry=AcquireMagickInfo("META","APP1","Raw application information");
1479
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1480
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1481
8
  entry->flags^=CoderAdjoinFlag;
1482
8
  entry->flags|=CoderStealthFlag;
1483
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1484
8
  (void) RegisterMagickInfo(entry);
1485
8
  entry=AcquireMagickInfo("META","APP1JPEG","Raw JPEG binary data");
1486
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1487
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1488
8
  entry->flags^=CoderAdjoinFlag;
1489
8
  entry->flags|=CoderStealthFlag;
1490
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1491
8
  (void) RegisterMagickInfo(entry);
1492
8
  entry=AcquireMagickInfo("META","EXIF","Exif digital camera binary data");
1493
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1494
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1495
8
  entry->flags^=CoderAdjoinFlag;
1496
8
  entry->flags|=CoderStealthFlag;
1497
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1498
8
  (void) RegisterMagickInfo(entry);
1499
8
  entry=AcquireMagickInfo("META","XMP","Adobe XML metadata");
1500
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1501
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1502
8
  entry->flags^=CoderAdjoinFlag;
1503
8
  entry->flags|=CoderStealthFlag;
1504
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1505
8
  (void) RegisterMagickInfo(entry);
1506
8
  entry=AcquireMagickInfo("META","ICM","ICC Color Profile");
1507
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1508
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1509
8
  entry->flags^=CoderAdjoinFlag;
1510
8
  entry->flags|=CoderStealthFlag;
1511
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1512
8
  (void) RegisterMagickInfo(entry);
1513
8
  entry=AcquireMagickInfo("META","ICC","ICC Color Profile");
1514
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1515
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1516
8
  entry->flags^=CoderAdjoinFlag;
1517
8
  entry->flags|=CoderStealthFlag;
1518
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1519
8
  (void) RegisterMagickInfo(entry);
1520
8
  entry=AcquireMagickInfo("META","IPTC","IPTC Newsphoto");
1521
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1522
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1523
8
  entry->flags^=CoderAdjoinFlag;
1524
8
  entry->flags|=CoderStealthFlag;
1525
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1526
8
  (void) RegisterMagickInfo(entry);
1527
8
  entry=AcquireMagickInfo("META","IPTCTEXT","IPTC Newsphoto text format");
1528
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1529
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1530
8
  entry->flags^=CoderAdjoinFlag;
1531
8
  entry->flags|=CoderStealthFlag;
1532
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1533
8
  (void) RegisterMagickInfo(entry);
1534
8
  entry=AcquireMagickInfo("META","IPTCWTEXT","IPTC Newsphoto text format");
1535
8
  entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1536
8
  entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1537
8
  entry->flags^=CoderAdjoinFlag;
1538
8
  entry->flags|=CoderStealthFlag;
1539
8
  entry->flags|=CoderDecoderSeekableStreamFlag;
1540
8
  (void) RegisterMagickInfo(entry);
1541
8
  return(MagickImageCoderSignature);
1542
8
}
1543

1544
/*
1545
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1546
%                                                                             %
1547
%                                                                             %
1548
%                                                                             %
1549
%   U n r e g i s t e r M E T A I m a g e                                     %
1550
%                                                                             %
1551
%                                                                             %
1552
%                                                                             %
1553
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554
%
1555
%  UnregisterMETAImage() removes format registrations made by the
1556
%  META module from the list of supported formats.
1557
%
1558
%  The format of the UnregisterMETAImage method is:
1559
%
1560
%      UnregisterMETAImage(void)
1561
%
1562
*/
1563
ModuleExport void UnregisterMETAImage(void)
1564
0
{
1565
0
  (void) UnregisterMagickInfo("8BIM");
1566
0
  (void) UnregisterMagickInfo("8BIMTEXT");
1567
0
  (void) UnregisterMagickInfo("8BIMWTEXT");
1568
0
  (void) UnregisterMagickInfo("EXIF");
1569
0
  (void) UnregisterMagickInfo("APP1");
1570
0
  (void) UnregisterMagickInfo("APP1JPEG");
1571
0
  (void) UnregisterMagickInfo("ICCTEXT");
1572
0
  (void) UnregisterMagickInfo("ICM");
1573
0
  (void) UnregisterMagickInfo("ICC");
1574
0
  (void) UnregisterMagickInfo("IPTC");
1575
0
  (void) UnregisterMagickInfo("IPTCTEXT");
1576
0
  (void) UnregisterMagickInfo("IPTCWTEXT");
1577
0
  (void) UnregisterMagickInfo("XMP");
1578
0
}
1579

1580
/*
1581
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1582
%                                                                             %
1583
%                                                                             %
1584
%                                                                             %
1585
%   W r i t e M E T A I m a g e                                               %
1586
%                                                                             %
1587
%                                                                             %
1588
%                                                                             %
1589
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590
%
1591
%  WriteMETAImage() writes a META image to a file.
1592
%
1593
%  The format of the WriteMETAImage method is:
1594
%
1595
%      MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
1596
%        Image *image,ExceptionInfo *exception)
1597
%
1598
%  Compression code contributed by Kyle Shorter.
1599
%
1600
%  A description of each parameter follows:
1601
%
1602
%    o image_info: Specifies a pointer to an ImageInfo structure.
1603
%
1604
%    o image: A pointer to a Image structure.
1605
%
1606
%    o exception: return any errors or warnings in this structure.
1607
%
1608
*/
1609
1610
static size_t GetIPTCStream(unsigned char **info,size_t length)
1611
0
{
1612
0
  int
1613
0
    c;
1614
1615
0
  size_t
1616
0
    extent,
1617
0
    info_length,
1618
0
    tag_length;
1619
1620
0
  unsigned char
1621
0
    *p;
1622
1623
0
  ssize_t
1624
0
    i;
1625
1626
0
  unsigned int
1627
0
    marker;
1628
1629
0
  p=(*info);
1630
0
  extent=length;
1631
0
  if ((*p == 0x1c) && (*(p+1) == 0x02))
1632
0
    return(length);
1633
  /*
1634
    Extract IPTC from 8BIM resource block.
1635
  */
1636
0
  while (extent >= 12)
1637
0
  {
1638
0
    if (strncmp((const char *) p,"8BIM",4))
1639
0
      break;
1640
0
    p+=(ptrdiff_t) 4;
1641
0
    extent-=4;
1642
0
    marker=(unsigned int) (*p) << 8 | *(p+1);
1643
0
    p+=(ptrdiff_t) 2;
1644
0
    extent-=2;
1645
0
    c=*p++;
1646
0
    extent--;
1647
0
    c|=0x01;
1648
0
    if ((size_t) c >= extent)
1649
0
      break;
1650
0
    p+=(ptrdiff_t) c;
1651
0
    extent=(size_t) ((ssize_t) extent-c);
1652
0
    if (extent < 4)
1653
0
      break;
1654
0
    tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) |
1655
0
      (((size_t) *(p+2)) << 8) | ((size_t) *(p+3));
1656
0
    p+=(ptrdiff_t) 4;
1657
0
    extent-=4;
1658
0
    if (tag_length > extent)
1659
0
      break;
1660
0
    if (marker == IPTC_ID)
1661
0
      {
1662
0
        *info=p;
1663
0
        return(tag_length);
1664
0
      }
1665
0
    if ((tag_length & 0x01) != 0)
1666
0
      {
1667
0
        tag_length++;
1668
0
        if (tag_length > extent)
1669
0
          break;
1670
0
      }
1671
0
    p+=(ptrdiff_t) tag_length;
1672
0
    extent-=tag_length;
1673
0
  }
1674
  /*
1675
    Find the beginning of the IPTC info.
1676
  */
1677
0
  p=(*info);
1678
0
  tag_length=0;
1679
0
iptc_find:
1680
0
  info_length=0;
1681
0
  marker=MagickFalse;
1682
0
  while (length != 0)
1683
0
  {
1684
0
    c=(*p++);
1685
0
    length--;
1686
0
    if (length == 0)
1687
0
      break;
1688
0
    if (c == 0x1c)
1689
0
      {
1690
0
        p--;
1691
0
        *info=p; /* let the caller know were it is */
1692
0
        break;
1693
0
      }
1694
0
  }
1695
  /*
1696
    Determine the length of the IPTC info.
1697
  */
1698
0
  while (length != 0)
1699
0
  {
1700
0
    c=(*p++);
1701
0
    length--;
1702
0
    if (length == 0)
1703
0
      break;
1704
0
    if (c == 0x1c)
1705
0
      marker=MagickTrue;
1706
0
    else
1707
0
      if (marker)
1708
0
        break;
1709
0
      else
1710
0
        continue;
1711
0
    info_length++;
1712
    /*
1713
      Found the 0x1c tag; skip the dataset and record number tags.
1714
    */
1715
0
    c=(*p++); /* should be 2 */
1716
0
    length--;
1717
0
    if (length == 0)
1718
0
      break;
1719
0
    if ((info_length == 1) && (c != 2))
1720
0
      goto iptc_find;
1721
0
    info_length++;
1722
0
    c=(*p++); /* should be 0 */
1723
0
    length--;
1724
0
    if (length == 0)
1725
0
      break;
1726
0
    if ((info_length == 2) && (c != 0))
1727
0
      goto iptc_find;
1728
0
    info_length++;
1729
    /*
1730
      Decode the length of the block that follows - ssize_t or short format.
1731
    */
1732
0
    c=(*p++);
1733
0
    length--;
1734
0
    if (length == 0)
1735
0
      break;
1736
0
    info_length++;
1737
0
    if ((c & 0x80) != 0)
1738
0
      {
1739
        /*
1740
          Long format.
1741
        */
1742
0
        tag_length=0;
1743
0
        for (i=0; i < 4; i++)
1744
0
        {
1745
0
          tag_length<<=8;
1746
0
          tag_length|=(*p++);
1747
0
          length--;
1748
0
          if (length == 0)
1749
0
            break;
1750
0
          info_length++;
1751
0
        }
1752
0
      }
1753
0
    else
1754
0
      {
1755
        /*
1756
          Short format.
1757
        */
1758
0
        tag_length=(size_t) (c << 8);
1759
0
        c=(*p++);
1760
0
        length--;
1761
0
        if (length == 0)
1762
0
          break;
1763
0
        info_length++;
1764
0
        tag_length|=(unsigned int) c;
1765
0
      }
1766
0
    if (tag_length > length)
1767
0
      break;
1768
0
    p+=(ptrdiff_t) tag_length;
1769
0
    length-=tag_length;
1770
0
    if (length == 0)
1771
0
      break;
1772
0
    info_length+=tag_length;
1773
0
  }
1774
0
  return(info_length);
1775
0
}
1776
1777
static void formatString(Image *ofile, const char *s, ssize_t len)
1778
0
{
1779
0
  char
1780
0
    temp[MagickPathExtent];
1781
1782
0
  (void) WriteBlobByte(ofile,'"');
1783
0
  for (; len > 0; len--, s++) {
1784
0
    int c = (*s) & 255;
1785
0
    switch (c) {
1786
0
    case '&':
1787
0
      (void) WriteBlobString(ofile,"&amp;");
1788
0
      break;
1789
#ifdef HANDLE_GT_LT
1790
    case '<':
1791
      (void) WriteBlobString(ofile,"&lt;");
1792
      break;
1793
    case '>':
1794
      (void) WriteBlobString(ofile,"&gt;");
1795
      break;
1796
#endif
1797
0
    case '"':
1798
0
      (void) WriteBlobString(ofile,"&quot;");
1799
0
      break;
1800
0
    default:
1801
0
      if (isprint((int) ((unsigned char) c)) != 0)
1802
0
        (void) WriteBlobByte(ofile,(unsigned char) *s);
1803
0
      else
1804
0
        {
1805
0
          (void) FormatLocaleString(temp,MagickPathExtent,"&#%d;", c & 255);
1806
0
          (void) WriteBlobString(ofile,temp);
1807
0
        }
1808
0
      break;
1809
0
    }
1810
0
  }
1811
#if defined(MAGICKCORE_WINDOWS_SUPPORT)
1812
  (void) WriteBlobString(ofile,"\"\r\n");
1813
#else
1814
0
  (void) WriteBlobString(ofile,"\"\n");
1815
0
#endif
1816
0
}
1817
1818
typedef struct _tag_spec
1819
{
1820
  const short
1821
    id;
1822
1823
  const char
1824
    *name;
1825
} tag_spec;
1826
1827
static const tag_spec tags[] = {
1828
  { 5, "Image Name" },
1829
  { 7, "Edit Status" },
1830
  { 10, "Priority" },
1831
  { 15, "Category" },
1832
  { 20, "Supplemental Category" },
1833
  { 22, "Fixture Identifier" },
1834
  { 25, "Keyword" },
1835
  { 30, "Release Date" },
1836
  { 35, "Release Time" },
1837
  { 40, "Special Instructions" },
1838
  { 45, "Reference Service" },
1839
  { 47, "Reference Date" },
1840
  { 50, "Reference Number" },
1841
  { 55, "Created Date" },
1842
  { 60, "Created Time" },
1843
  { 65, "Originating Program" },
1844
  { 70, "Program Version" },
1845
  { 75, "Object Cycle" },
1846
  { 80, "Byline" },
1847
  { 85, "Byline Title" },
1848
  { 90, "City" },
1849
  { 92, "Sub-Location" },
1850
  { 95, "Province State" },
1851
  { 100, "Country Code" },
1852
  { 101, "Country" },
1853
  { 103, "Original Transmission Reference" },
1854
  { 105, "Headline" },
1855
  { 110, "Credit" },
1856
  { 115, "Source" },
1857
  { 116, "Copyright String" },
1858
  { 120, "Caption" },
1859
  { 121, "Image Orientation" },
1860
  { 122, "Caption Writer" },
1861
  { 131, "Local Caption" },
1862
  { 200, "Custom Field 1" },
1863
  { 201, "Custom Field 2" },
1864
  { 202, "Custom Field 3" },
1865
  { 203, "Custom Field 4" },
1866
  { 204, "Custom Field 5" },
1867
  { 205, "Custom Field 6" },
1868
  { 206, "Custom Field 7" },
1869
  { 207, "Custom Field 8" },
1870
  { 208, "Custom Field 9" },
1871
  { 209, "Custom Field 10" },
1872
  { 210, "Custom Field 11" },
1873
  { 211, "Custom Field 12" },
1874
  { 212, "Custom Field 13" },
1875
  { 213, "Custom Field 14" },
1876
  { 214, "Custom Field 15" },
1877
  { 215, "Custom Field 16" },
1878
  { 216, "Custom Field 17" },
1879
  { 217, "Custom Field 18" },
1880
  { 218, "Custom Field 19" },
1881
  { 219, "Custom Field 20" }
1882
};
1883
1884
static void formatIPTC(Image *ifile, Image *ofile)
1885
0
{
1886
0
  char
1887
0
    temp[MagickPathExtent];
1888
1889
0
  unsigned int
1890
0
    foundiptc;
1891
1892
0
  unsigned char
1893
0
    recnum,
1894
0
    dataset;
1895
1896
0
  unsigned char
1897
0
    *readable,
1898
0
    *str;
1899
1900
0
  ssize_t
1901
0
    tagindx,
1902
0
    taglen;
1903
1904
0
  int
1905
0
    i,
1906
0
    tagcount = (int) (sizeof(tags) / sizeof(tags[0]));
1907
1908
0
  int
1909
0
    c;
1910
1911
0
  foundiptc = 0; /* found the IPTC-Header */
1912
1913
0
  c=ReadBlobByte(ifile);
1914
0
  while (c != EOF)
1915
0
  {
1916
0
    if (c == 0x1c)
1917
0
      foundiptc = 1;
1918
0
    else
1919
0
      {
1920
0
        if (foundiptc)
1921
0
          return;
1922
0
        else
1923
0
          {
1924
0
            c=ReadBlobByte(ifile);
1925
0
            continue;
1926
0
          }
1927
0
      }
1928
1929
    /* we found the 0x1c tag and now grab the dataset and record number tags */
1930
0
    c=ReadBlobByte(ifile);
1931
0
    if (c == EOF)
1932
0
      return;
1933
0
    dataset = (unsigned char) c;
1934
0
    c=ReadBlobByte(ifile);
1935
0
    if (c == EOF)
1936
0
      return;
1937
0
    recnum = (unsigned char) c;
1938
    /* try to match this record to one of the ones in our named table */
1939
0
    for (i=0; i< tagcount; i++)
1940
0
    {
1941
0
      if (tags[i].id == (short) recnum)
1942
0
          break;
1943
0
    }
1944
0
    if (i < tagcount)
1945
0
      readable = (unsigned char *) tags[i].name;
1946
0
    else
1947
0
      readable = (unsigned char *) "";
1948
    /*
1949
      We decode the length of the block that follows - ssize_t or short fmt.
1950
    */
1951
0
    c=ReadBlobByte(ifile);
1952
0
    if (c == EOF)
1953
0
      return;
1954
0
    if (c & (unsigned char) 0x80)
1955
0
      return;
1956
0
    else
1957
0
      {
1958
0
        int
1959
0
          c0;
1960
1961
0
        c0=ReadBlobByte(ifile);
1962
0
        if (c0 == EOF)
1963
0
          return;
1964
0
        taglen = (c << 8) | c0;
1965
0
      }
1966
0
    if (taglen < 0)
1967
0
      return;
1968
    /* make a buffer to hold the tag datand snag it from the input stream */
1969
0
    str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+
1970
0
      MagickPathExtent),sizeof(*str));
1971
0
    if (str == (unsigned char *) NULL)
1972
0
      return;
1973
0
    for (tagindx=0; tagindx<taglen; tagindx++)
1974
0
    {
1975
0
      c=ReadBlobByte(ifile);
1976
0
      if (c == EOF)
1977
0
        {
1978
0
          str=(unsigned char *) RelinquishMagickMemory(str);
1979
0
          return;
1980
0
        }
1981
0
      str[tagindx] = (unsigned char) c;
1982
0
    }
1983
0
    str[taglen] = 0;
1984
1985
    /* now finish up by formatting this binary data into ASCII equivalent */
1986
0
    if (strlen((char *)readable) > 0)
1987
0
      (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
1988
0
        (unsigned int) dataset, (unsigned int) recnum, readable);
1989
0
    else
1990
0
      (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
1991
0
        (unsigned int) dataset,(unsigned int) recnum);
1992
0
    (void) WriteBlobString(ofile,temp);
1993
0
    formatString( ofile, (char *)str, taglen );
1994
0
    str=(unsigned char *) RelinquishMagickMemory(str);
1995
0
    c=ReadBlobByte(ifile);
1996
0
  }
1997
0
}
1998
1999
static int readWordFromBuffer(char **s, ssize_t *len)
2000
0
{
2001
0
  unsigned char
2002
0
    buffer[2];
2003
2004
0
  int
2005
0
    i,
2006
0
    c;
2007
2008
0
  for (i=0; i<2; i++)
2009
0
  {
2010
0
    c = *(*s)++; (*len)--;
2011
0
    if (*len < 0) return -1;
2012
0
    buffer[i] = (unsigned char) c;
2013
0
  }
2014
0
  return (((int) buffer[ 0 ]) <<  8) |
2015
0
         (((int) buffer[ 1 ]));
2016
0
}
2017
2018
static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len)
2019
0
{
2020
0
  char
2021
0
    temp[MagickPathExtent];
2022
2023
0
  unsigned int
2024
0
    foundiptc,
2025
0
    tagsfound;
2026
2027
0
  unsigned char
2028
0
    recnum,
2029
0
    dataset;
2030
2031
0
  unsigned char
2032
0
    *readable,
2033
0
    *str;
2034
2035
0
  ssize_t
2036
0
    tagindx,
2037
0
    taglen;
2038
2039
0
  int
2040
0
    i,
2041
0
    tagcount = (int) (sizeof(tags) / sizeof(tags[0]));
2042
2043
0
  int
2044
0
    c;
2045
2046
0
  foundiptc = 0; /* found the IPTC-Header */
2047
0
  tagsfound = 0; /* number of tags found */
2048
2049
0
  while (len > 0)
2050
0
  {
2051
0
    c = *s++; len--;
2052
0
    if (c == 0x1c)
2053
0
      foundiptc = 1;
2054
0
    else
2055
0
      {
2056
0
        if (foundiptc)
2057
0
          return -1;
2058
0
        else
2059
0
          continue;
2060
0
      }
2061
    /*
2062
      We found the 0x1c tag and now grab the dataset and record number tags.
2063
    */
2064
0
    c = *s++; len--;
2065
0
    if (len < 0) return -1;
2066
0
    dataset = (unsigned char) c;
2067
0
    c = *s++; len--;
2068
0
    if (len < 0) return -1;
2069
0
    recnum = (unsigned char) c;
2070
    /* try to match this record to one of the ones in our named table */
2071
0
    for (i=0; i< tagcount; i++)
2072
0
      if (tags[i].id == (short) recnum)
2073
0
        break;
2074
0
    if (i < tagcount)
2075
0
      readable=(unsigned char *) tags[i].name;
2076
0
    else
2077
0
      readable=(unsigned char *) "";
2078
    /*
2079
      We decode the length of the block that follows - ssize_t or short fmt.
2080
    */
2081
0
    c=(*s++);
2082
0
    len--;
2083
0
    if (len < 0)
2084
0
      return(-1);
2085
0
    if (c & (unsigned char) 0x80)
2086
0
      return(0);
2087
0
    else
2088
0
      {
2089
0
        s--;
2090
0
        len++;
2091
0
        taglen=readWordFromBuffer(&s, &len);
2092
0
      }
2093
0
    if (taglen < 0)
2094
0
      return(-1);
2095
0
    if (taglen > 65535)
2096
0
      return(-1);
2097
    /* make a buffer to hold the tag datand snag it from the input stream */
2098
0
    str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+
2099
0
      MagickPathExtent),sizeof(*str));
2100
0
    if (str == (unsigned char *) NULL)
2101
0
      {
2102
0
        (void) printf("MemoryAllocationFailed");
2103
0
        return 0;
2104
0
      }
2105
0
    for (tagindx=0; tagindx<taglen; tagindx++)
2106
0
    {
2107
0
      c = *s++; len--;
2108
0
      if (len < 0)
2109
0
        {
2110
0
          str=(unsigned char *) RelinquishMagickMemory(str);
2111
0
          return(-1);
2112
0
        }
2113
0
      str[tagindx]=(unsigned char) c;
2114
0
    }
2115
0
    str[taglen]=0;
2116
2117
    /* now finish up by formatting this binary data into ASCII equivalent */
2118
0
    if (strlen((char *)readable) > 0)
2119
0
      (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
2120
0
        (unsigned int) dataset,(unsigned int) recnum, readable);
2121
0
    else
2122
0
      (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
2123
0
        (unsigned int) dataset,(unsigned int) recnum);
2124
0
    (void) WriteBlobString(ofile,temp);
2125
0
    formatString( ofile, (char *)str, taglen );
2126
0
    str=(unsigned char *) RelinquishMagickMemory(str);
2127
2128
0
    tagsfound++;
2129
0
  }
2130
0
  return ((int) tagsfound);
2131
0
}
2132
2133
static int format8BIM(Image *ifile, Image *ofile)
2134
0
{
2135
0
  char
2136
0
    temp[MagickPathExtent];
2137
2138
0
  unsigned int
2139
0
    foundOSType;
2140
2141
0
  int
2142
0
    ID,
2143
0
    resCount,
2144
0
    i,
2145
0
    c;
2146
2147
0
  ssize_t
2148
0
    count;
2149
2150
0
  unsigned char
2151
0
    *PString,
2152
0
    *str;
2153
2154
0
  resCount=0;
2155
0
  foundOSType=0; /* found the OSType */
2156
0
  (void) foundOSType;
2157
0
  c=ReadBlobByte(ifile);
2158
0
  while (c != EOF)
2159
0
  {
2160
0
    if (c == '8')
2161
0
      {
2162
0
        unsigned char
2163
0
          buffer[5];
2164
2165
0
        buffer[0]=(unsigned char) c;
2166
0
        for (i=1; i<4; i++)
2167
0
        {
2168
0
          c=ReadBlobByte(ifile);
2169
0
          if (c == EOF)
2170
0
            return(-1);
2171
0
          buffer[i] = (unsigned char) c;
2172
0
        }
2173
0
        buffer[4]=0;
2174
0
        if (strcmp((const char *)buffer, "8BIM") == 0)
2175
0
          foundOSType=1;
2176
0
        else
2177
0
          continue;
2178
0
      }
2179
0
    else
2180
0
      {
2181
0
        c=ReadBlobByte(ifile);
2182
0
        continue;
2183
0
      }
2184
    /*
2185
      We found the OSType (8BIM) and now grab the ID, PString, and Size fields.
2186
    */
2187
0
    ID=ReadBlobMSBSignedShort(ifile);
2188
0
    if (ID < 0)
2189
0
      return(-1);
2190
0
    {
2191
0
      unsigned char
2192
0
        plen;
2193
2194
0
      c=ReadBlobByte(ifile);
2195
0
      if (c == EOF)
2196
0
        return(-1);
2197
0
      plen = (unsigned char) c;
2198
0
      PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+
2199
0
        MagickPathExtent),sizeof(*PString));
2200
0
      if (PString == (unsigned char *) NULL)
2201
0
        return 0;
2202
0
      for (i=0; i<plen; i++)
2203
0
      {
2204
0
        c=ReadBlobByte(ifile);
2205
0
        if (c == EOF)
2206
0
          {
2207
0
            PString=(unsigned char *) RelinquishMagickMemory(PString);
2208
0
            return -1;
2209
0
          }
2210
0
        PString[i] = (unsigned char) c;
2211
0
      }
2212
0
      PString[ plen ] = 0;
2213
0
      if ((plen & 0x01) == 0)
2214
0
      {
2215
0
        c=ReadBlobByte(ifile);
2216
0
        if (c == EOF)
2217
0
          {
2218
0
            PString=(unsigned char *) RelinquishMagickMemory(PString);
2219
0
            return -1;
2220
0
          }
2221
0
      }
2222
0
    }
2223
0
    count=(ssize_t) ReadBlobMSBSignedLong(ifile);
2224
0
    if ((count < 0) || (count > (ssize_t) GetBlobSize(ifile)))
2225
0
      {
2226
0
        PString=(unsigned char *) RelinquishMagickMemory(PString);
2227
0
        return -1;
2228
0
      }
2229
    /* make a buffer to hold the data and snag it from the input stream */
2230
0
    str=(unsigned char *) AcquireQuantumMemory((size_t) count+1,sizeof(*str));
2231
0
    if (str == (unsigned char *) NULL)
2232
0
      {
2233
0
        PString=(unsigned char *) RelinquishMagickMemory(PString);
2234
0
        return 0;
2235
0
      }
2236
0
    for (i=0; i < (ssize_t) count; i++)
2237
0
    {
2238
0
      c=ReadBlobByte(ifile);
2239
0
      if (c == EOF)
2240
0
        {
2241
0
          str=(unsigned char *) RelinquishMagickMemory(str);
2242
0
          PString=(unsigned char *) RelinquishMagickMemory(PString);
2243
0
          return -1;
2244
0
        }
2245
0
      str[i]=(unsigned char) c;
2246
0
    }
2247
2248
    /* we currently skip thumbnails, since it does not make
2249
     * any sense preserving them in a real world application
2250
     */
2251
0
    if (ID != THUMBNAIL_ID)
2252
0
      {
2253
        /* now finish up by formatting this binary data into
2254
         * ASCII equivalent
2255
         */
2256
0
        if (strlen((const char *)PString) > 0)
2257
0
          (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d#%s=",ID,
2258
0
            PString);
2259
0
        else
2260
0
          (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d=",ID);
2261
0
        (void) WriteBlobString(ofile,temp);
2262
0
        if (ID == IPTC_ID)
2263
0
          {
2264
0
            formatString(ofile, "IPTC", 4);
2265
0
            formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count);
2266
0
          }
2267
0
        else
2268
0
          formatString(ofile, (char *)str, (ssize_t) count);
2269
0
      }
2270
0
    str=(unsigned char *) RelinquishMagickMemory(str);
2271
0
    PString=(unsigned char *) RelinquishMagickMemory(PString);
2272
0
    resCount++;
2273
0
    c=ReadBlobByte(ifile);
2274
0
  }
2275
0
  return resCount;
2276
0
}
2277
2278
static MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
2279
  Image *image,ExceptionInfo *exception)
2280
0
{
2281
0
  const StringInfo
2282
0
    *profile;
2283
2284
0
  MagickBooleanType
2285
0
    status;
2286
2287
0
  size_t
2288
0
    length;
2289
2290
  /*
2291
    Open image file.
2292
  */
2293
0
  assert(image_info != (const ImageInfo *) NULL);
2294
0
  assert(image_info->signature == MagickCoreSignature);
2295
0
  assert(image != (Image *) NULL);
2296
0
  assert(image->signature == MagickCoreSignature);
2297
0
  if (IsEventLogging() != MagickFalse)
2298
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2299
0
  if (LocaleCompare(image_info->magick,"8BIM") == 0)
2300
0
    {
2301
      /*
2302
        Write 8BIM image.
2303
      */
2304
0
      profile=GetImageProfile(image,"8bim");
2305
0
      if (profile == (StringInfo *) NULL)
2306
0
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2307
0
      assert(exception != (ExceptionInfo *) NULL);
2308
0
      assert(exception->signature == MagickCoreSignature);
2309
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2310
0
      if (status == MagickFalse)
2311
0
        return(status);
2312
0
      (void) WriteBlob(image,GetStringInfoLength(profile),
2313
0
        GetStringInfoDatum(profile));
2314
0
      (void) CloseBlob(image);
2315
0
      return(MagickTrue);
2316
0
    }
2317
0
  if (LocaleCompare(image_info->magick,"iptc") == 0)
2318
0
    {
2319
0
      unsigned char
2320
0
        *info;
2321
2322
0
      profile=GetImageProfile(image,"iptc");
2323
0
      if (profile == (StringInfo *) NULL)
2324
0
        profile=GetImageProfile(image,"8bim");
2325
0
      if (profile == (StringInfo *) NULL)
2326
0
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2327
0
      assert(exception != (ExceptionInfo *) NULL);
2328
0
      assert(exception->signature == MagickCoreSignature);
2329
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2330
0
      if (status == MagickFalse)
2331
0
        return(status);
2332
0
      info=GetStringInfoDatum(profile);
2333
0
      length=GetStringInfoLength(profile);
2334
0
      length=GetIPTCStream(&info,length);
2335
0
      if (length == 0)
2336
0
        ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2337
0
      (void) WriteBlob(image,length,info);
2338
0
      (void) CloseBlob(image);
2339
0
      return(MagickTrue);
2340
0
    }
2341
0
  if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
2342
0
    {
2343
0
      Image
2344
0
        *buff;
2345
2346
0
      profile=GetImageProfile(image,"8bim");
2347
0
      if (profile == (StringInfo *) NULL)
2348
0
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2349
0
      assert(exception != (ExceptionInfo *) NULL);
2350
0
      assert(exception->signature == MagickCoreSignature);
2351
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2352
0
      if (status == MagickFalse)
2353
0
        return(status);
2354
0
      buff=AcquireImage((ImageInfo *) NULL,exception);
2355
0
      if (buff == (Image *) NULL)
2356
0
        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2357
0
      AttachBlob(buff->blob,GetStringInfoDatum(profile),
2358
0
        GetStringInfoLength(profile));
2359
0
      format8BIM(buff,image);
2360
0
      (void) DetachBlob(buff->blob);
2361
0
      buff=DestroyImage(buff);
2362
0
      (void) CloseBlob(image);
2363
0
      return(MagickTrue);
2364
0
    }
2365
0
  if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
2366
0
    return(MagickFalse);
2367
0
  if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
2368
0
    {
2369
0
      Image
2370
0
        *buff;
2371
2372
0
      unsigned char
2373
0
        *info;
2374
2375
0
      profile=GetImageProfile(image,"8bim");
2376
0
      if (profile == (StringInfo *) NULL)
2377
0
        ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2378
0
      info=GetStringInfoDatum(profile);
2379
0
      length=GetStringInfoLength(profile);
2380
0
      length=GetIPTCStream(&info,length);
2381
0
      if (length == 0)
2382
0
        ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2383
0
      assert(exception != (ExceptionInfo *) NULL);
2384
0
      assert(exception->signature == MagickCoreSignature);
2385
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2386
0
      if (status == MagickFalse)
2387
0
        return(status);
2388
0
      buff=AcquireImage((ImageInfo *) NULL,exception);
2389
0
      if (buff == (Image *) NULL)
2390
0
        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2391
0
      AttachBlob(buff->blob,info,length);
2392
0
      formatIPTC(buff,image);
2393
0
      (void) DetachBlob(buff->blob);
2394
0
      buff=DestroyImage(buff);
2395
0
      (void) CloseBlob(image);
2396
0
      return(MagickTrue);
2397
0
    }
2398
0
  if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
2399
0
    return(MagickFalse);
2400
0
  if ((LocaleCompare(image_info->magick,"APP1") == 0) ||
2401
0
      (LocaleCompare(image_info->magick,"EXIF") == 0) ||
2402
0
      (LocaleCompare(image_info->magick,"XMP") == 0))
2403
0
    {
2404
      /*
2405
        (void) Write APP1 image.
2406
      */
2407
0
      profile=GetImageProfile(image,image_info->magick);
2408
0
      if (profile == (StringInfo *) NULL)
2409
0
        ThrowWriterException(CoderError,"NoAPP1DataIsAvailable");
2410
0
      assert(exception != (ExceptionInfo *) NULL);
2411
0
      assert(exception->signature == MagickCoreSignature);
2412
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2413
0
      if (status == MagickFalse)
2414
0
        return(status);
2415
0
      (void) WriteBlob(image,GetStringInfoLength(profile),
2416
0
        GetStringInfoDatum(profile));
2417
0
      (void) CloseBlob(image);
2418
0
      return(MagickTrue);
2419
0
    }
2420
0
  if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
2421
0
      (LocaleCompare(image_info->magick,"ICM") == 0))
2422
0
    {
2423
      /*
2424
        Write ICM image.
2425
      */
2426
0
      profile=GetImageProfile(image,"icc");
2427
0
      if (profile == (StringInfo *) NULL)
2428
0
        ThrowWriterException(CoderError,"NoColorProfileIsAvailable");
2429
0
      assert(exception != (ExceptionInfo *) NULL);
2430
0
      assert(exception->signature == MagickCoreSignature);
2431
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2432
0
      if (status == MagickFalse)
2433
0
        return(status);
2434
0
      (void) WriteBlob(image,GetStringInfoLength(profile),
2435
0
        GetStringInfoDatum(profile));
2436
0
      (void) CloseBlob(image);
2437
0
      return(MagickTrue);
2438
0
    }
2439
0
  return(MagickFalse);
2440
0
}