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