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