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