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