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