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