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