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