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