]> 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-2011 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/exception.h"
46 #include "MagickCore/exception-private.h"
47 #include "MagickCore/image.h"
48 #include "MagickCore/image-private.h"
49 #include "MagickCore/list.h"
50 #include "MagickCore/magick.h"
51 #include "MagickCore/memory_.h"
52 #include "MagickCore/module.h"
53 #include "MagickCore/profile.h"
54 #include "MagickCore/splay-tree.h"
55 #include "MagickCore/quantum-private.h"
56 #include "MagickCore/static.h"
57 #include "MagickCore/string_.h"
58 #include "MagickCore/string-private.h"
59 #include "MagickCore/token.h"
60 #include "MagickCore/utility.h"
61 \f
62 /*
63   Forward declarations.
64 */
65 static MagickBooleanType
66   WriteMETAImage(const ImageInfo *,Image *,ExceptionInfo *);
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   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   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) FormatLocaleString(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\0");
1311       (void) WriteBlobByte(buff,0xc6);
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   entry=SetMagickInfo("8BIMTEXT");
1415   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1416   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1417   entry->adjoin=MagickFalse;
1418   entry->stealth=MagickTrue;
1419   entry->seekable_stream=MagickTrue;
1420   entry->description=ConstantString("Photoshop resource text format");
1421   entry->module=ConstantString("META");
1422   (void) RegisterMagickInfo(entry);
1423   entry=SetMagickInfo("8BIMWTEXT");
1424   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1425   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1426   entry->adjoin=MagickFalse;
1427   entry->stealth=MagickTrue;
1428   entry->seekable_stream=MagickTrue;
1429   entry->description=ConstantString("Photoshop resource wide text format");
1430   entry->module=ConstantString("META");
1431   (void) RegisterMagickInfo(entry);
1432   entry=SetMagickInfo("APP1");
1433   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1434   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1435   entry->adjoin=MagickFalse;
1436   entry->stealth=MagickTrue;
1437   entry->seekable_stream=MagickTrue;
1438   entry->description=ConstantString("Raw application information");
1439   entry->module=ConstantString("META");
1440   (void) RegisterMagickInfo(entry);
1441   entry=SetMagickInfo("APP1JPEG");
1442   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1443   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1444   entry->adjoin=MagickFalse;
1445   entry->stealth=MagickTrue;
1446   entry->seekable_stream=MagickTrue;
1447   entry->description=ConstantString("Raw JPEG binary data");
1448   entry->module=ConstantString("META");
1449   (void) RegisterMagickInfo(entry);
1450   entry=SetMagickInfo("EXIF");
1451   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1452   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1453   entry->adjoin=MagickFalse;
1454   entry->stealth=MagickTrue;
1455   entry->seekable_stream=MagickTrue;
1456   entry->description=ConstantString("Exif digital camera binary data");
1457   entry->module=ConstantString("META");
1458   (void) RegisterMagickInfo(entry);
1459   entry=SetMagickInfo("XMP");
1460   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1461   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1462   entry->adjoin=MagickFalse;
1463   entry->stealth=MagickTrue;
1464   entry->seekable_stream=MagickTrue;
1465   entry->description=ConstantString("Adobe XML metadata");
1466   entry->module=ConstantString("META");
1467   (void) RegisterMagickInfo(entry);
1468   entry=SetMagickInfo("ICM");
1469   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1470   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1471   entry->adjoin=MagickFalse;
1472   entry->stealth=MagickTrue;
1473   entry->seekable_stream=MagickTrue;
1474   entry->description=ConstantString("ICC Color Profile");
1475   entry->module=ConstantString("META");
1476   (void) RegisterMagickInfo(entry);
1477   entry=SetMagickInfo("ICC");
1478   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1479   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1480   entry->adjoin=MagickFalse;
1481   entry->stealth=MagickTrue;
1482   entry->seekable_stream=MagickTrue;
1483   entry->description=ConstantString("ICC Color Profile");
1484   entry->module=ConstantString("META");
1485   (void) RegisterMagickInfo(entry);
1486   entry=SetMagickInfo("IPTC");
1487   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1488   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1489   entry->adjoin=MagickFalse;
1490   entry->stealth=MagickTrue;
1491   entry->seekable_stream=MagickTrue;
1492   entry->description=ConstantString("IPTC Newsphoto");
1493   entry->module=ConstantString("META");
1494   (void) RegisterMagickInfo(entry);
1495   entry=SetMagickInfo("IPTCTEXT");
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 text format");
1502   entry->module=ConstantString("META");
1503   (void) RegisterMagickInfo(entry);
1504   entry=SetMagickInfo("IPTCWTEXT");
1505   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1506   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1507   entry->adjoin=MagickFalse;
1508   entry->stealth=MagickTrue;
1509   entry->seekable_stream=MagickTrue;
1510   entry->description=ConstantString("IPTC Newsphoto text format");
1511   entry->module=ConstantString("META");
1512   (void) RegisterMagickInfo(entry);
1513   return(MagickImageCoderSignature);
1514 }
1515 \f
1516 /*
1517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518 %                                                                             %
1519 %                                                                             %
1520 %                                                                             %
1521 %   U n r e g i s t e r M E T A I m a g e                                     %
1522 %                                                                             %
1523 %                                                                             %
1524 %                                                                             %
1525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1526 %
1527 %  UnregisterMETAImage() removes format registrations made by the
1528 %  META module from the list of supported formats.
1529 %
1530 %  The format of the UnregisterMETAImage method is:
1531 %
1532 %      UnregisterMETAImage(void)
1533 %
1534 */
1535 ModuleExport void UnregisterMETAImage(void)
1536 {
1537   (void) UnregisterMagickInfo("8BIM");
1538   (void) UnregisterMagickInfo("8BIMTEXT");
1539   (void) UnregisterMagickInfo("8BIMWTEXT");
1540   (void) UnregisterMagickInfo("EXIF");
1541   (void) UnregisterMagickInfo("APP1");
1542   (void) UnregisterMagickInfo("APP1JPEG");
1543   (void) UnregisterMagickInfo("ICCTEXT");
1544   (void) UnregisterMagickInfo("ICM");
1545   (void) UnregisterMagickInfo("ICC");
1546   (void) UnregisterMagickInfo("IPTC");
1547   (void) UnregisterMagickInfo("IPTCTEXT");
1548   (void) UnregisterMagickInfo("IPTCWTEXT");
1549   (void) UnregisterMagickInfo("XMP");
1550 }
1551 \f
1552 /*
1553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554 %                                                                             %
1555 %                                                                             %
1556 %                                                                             %
1557 %   W r i t e M E T A I m a g e                                               %
1558 %                                                                             %
1559 %                                                                             %
1560 %                                                                             %
1561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1562 %
1563 %  WriteMETAImage() writes a META image to a file.
1564 %
1565 %  The format of the WriteMETAImage method is:
1566 %
1567 %      MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
1568 %        Image *image,ExceptionInfo *exception)
1569 %
1570 %  Compression code contributed by Kyle Shorter.
1571 %
1572 %  A description of each parameter follows:
1573 %
1574 %    o image_info: Specifies a pointer to an ImageInfo structure.
1575 %
1576 %    o image: A pointer to a Image structure.
1577 %
1578 %    o exception: return any errors or warnings in this structure.
1579 %
1580 */
1581
1582 static size_t GetIPTCStream(unsigned char **info,size_t length)
1583 {
1584   int
1585     c;
1586
1587   register ssize_t
1588     i;
1589
1590   register unsigned char
1591     *p;
1592
1593   size_t
1594     extent,
1595     info_length;
1596
1597   unsigned char
1598     buffer[4] = { '\0', '\0', '\0', '\0' };
1599
1600   unsigned int
1601     marker;
1602
1603   size_t
1604     tag_length;
1605
1606   p=(*info);
1607   extent=length;
1608   if ((*p == 0x1c) && (*(p+1) == 0x02))
1609     return(length);
1610   /*
1611     Extract IPTC from 8BIM resource block.
1612   */
1613   while (extent >= 12)
1614   {
1615     if (strncmp((const char *) p,"8BIM",4))
1616       break;
1617     p+=4;
1618     extent-=4;
1619     marker=(unsigned int) (*p) << 8 | *(p+1);
1620     p+=2;
1621     extent-=2;
1622     c=*p++;
1623     extent--;
1624     c|=0x01;
1625     if ((size_t) c >= extent)
1626       break;
1627     p+=c;
1628     extent-=c;
1629     if (extent < 4)
1630       break;
1631     tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) |
1632       (((size_t) *(p+2)) << 8) | ((size_t) *(p+3));
1633     p+=4;
1634     extent-=4;
1635     if (tag_length > extent)
1636       break;
1637     if (marker == IPTC_ID)
1638       {
1639         *info=p;
1640         return(tag_length);
1641       }
1642     if ((tag_length & 0x01) != 0)
1643       tag_length++;
1644     p+=tag_length;
1645     extent-=tag_length;
1646   }
1647   /*
1648     Find the beginning of the IPTC info.
1649   */
1650   p=(*info);
1651   tag_length=0;
1652 iptc_find:
1653   info_length=0;
1654   marker=MagickFalse;
1655   while (length != 0)
1656   {
1657     c=(*p++);
1658     length--;
1659     if (length == 0)
1660       break;
1661     if (c == 0x1c)
1662       {
1663         p--;
1664         *info=p; /* let the caller know were it is */
1665         break;
1666       }
1667   }
1668   /*
1669     Determine the length of the IPTC info.
1670   */
1671   while (length != 0)
1672   {
1673     c=(*p++);
1674     length--;
1675     if (length == 0)
1676       break;
1677     if (c == 0x1c)
1678       marker=MagickTrue;
1679     else
1680       if (marker)
1681         break;
1682       else
1683         continue;
1684     info_length++;
1685     /*
1686       Found the 0x1c tag; skip the dataset and record number tags.
1687     */
1688     c=(*p++); /* should be 2 */
1689     length--;
1690     if (length == 0)
1691       break;
1692     if ((info_length == 1) && (c != 2))
1693       goto iptc_find;
1694     info_length++;
1695     c=(*p++); /* should be 0 */
1696     length--;
1697     if (length == 0)
1698       break;
1699     if ((info_length == 2) && (c != 0))
1700       goto iptc_find;
1701     info_length++;
1702     /*
1703       Decode the length of the block that follows - ssize_t or short format.
1704     */
1705     c=(*p++);
1706     length--;
1707     if (length == 0)
1708       break;
1709     info_length++;
1710     if ((c & 0x80) != 0)
1711       {
1712         for (i=0; i < 4; i++)
1713         {
1714           buffer[i]=(*p++);
1715           length--;
1716           if (length == 0)
1717             break;
1718           info_length++;
1719         }
1720         tag_length=(((size_t) buffer[0]) << 24) |
1721           (((size_t) buffer[1]) << 16) |
1722           (((size_t) buffer[2]) << 8) | (((size_t) buffer[3])); 
1723       }
1724     else
1725       {
1726         tag_length=(size_t) (c << 8);
1727         c=(*p++);
1728         length--;
1729         if (length == 0)
1730           break;
1731         info_length++;
1732         tag_length|=c;
1733       }
1734     if (tag_length > (length+1))
1735       break;
1736     p+=tag_length;
1737     length-=tag_length;
1738     if (length == 0)
1739       break;
1740     info_length+=tag_length;
1741   }
1742   return(info_length);
1743 }
1744
1745 static void formatString(Image *ofile, const char *s, int len)
1746 {
1747   char
1748     temp[MaxTextExtent];
1749
1750   (void) WriteBlobByte(ofile,'"');
1751   for (; len > 0; len--, s++) {
1752     int c = (*s) & 255;
1753     switch (c) {
1754     case '&':
1755       (void) WriteBlobString(ofile,"&amp;");
1756       break;
1757 #ifdef HANDLE_GT_LT
1758     case '<':
1759       (void) WriteBlobString(ofile,"&lt;");
1760       break;
1761     case '>':
1762       (void) WriteBlobString(ofile,"&gt;");
1763       break;
1764 #endif
1765     case '"':
1766       (void) WriteBlobString(ofile,"&quot;");
1767       break;
1768     default:
1769       if (isprint(c))
1770         (void) WriteBlobByte(ofile,(unsigned char) *s);
1771       else
1772         {
1773           (void) FormatLocaleString(temp,MaxTextExtent,"&#%d;", c & 255);
1774           (void) WriteBlobString(ofile,temp);
1775         }
1776       break;
1777     }
1778   }
1779 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1780   (void) WriteBlobString(ofile,"\"\r\n");
1781 #else
1782 #if defined(macintosh)
1783   (void) WriteBlobString(ofile,"\"\r");
1784 #else
1785   (void) WriteBlobString(ofile,"\"\n");
1786 #endif
1787 #endif
1788 }
1789
1790 typedef struct _tag_spec
1791 {
1792   short
1793     id;
1794
1795   const char
1796     *name;
1797 } tag_spec;
1798
1799 static const tag_spec tags[] = {
1800   { 5, "Image Name" },
1801   { 7, "Edit Status" },
1802   { 10, "Priority" },
1803   { 15, "Category" },
1804   { 20, "Supplemental Category" },
1805   { 22, "Fixture Identifier" },
1806   { 25, "Keyword" },
1807   { 30, "Release Date" },
1808   { 35, "Release Time" },
1809   { 40, "Special Instructions" },
1810   { 45, "Reference Service" },
1811   { 47, "Reference Date" },
1812   { 50, "Reference Number" },
1813   { 55, "Created Date" },
1814   { 60, "Created Time" },
1815   { 65, "Originating Program" },
1816   { 70, "Program Version" },
1817   { 75, "Object Cycle" },
1818   { 80, "Byline" },
1819   { 85, "Byline Title" },
1820   { 90, "City" },
1821   { 95, "Province State" },
1822   { 100, "Country Code" },
1823   { 101, "Country" },
1824   { 103, "Original Transmission Reference" },
1825   { 105, "Headline" },
1826   { 110, "Credit" },
1827   { 115, "Source" },
1828   { 116, "Copyright String" },
1829   { 120, "Caption" },
1830   { 121, "Image Orientation" },
1831   { 122, "Caption Writer" },
1832   { 131, "Local Caption" },
1833   { 200, "Custom Field 1" },
1834   { 201, "Custom Field 2" },
1835   { 202, "Custom Field 3" },
1836   { 203, "Custom Field 4" },
1837   { 204, "Custom Field 5" },
1838   { 205, "Custom Field 6" },
1839   { 206, "Custom Field 7" },
1840   { 207, "Custom Field 8" },
1841   { 208, "Custom Field 9" },
1842   { 209, "Custom Field 10" },
1843   { 210, "Custom Field 11" },
1844   { 211, "Custom Field 12" },
1845   { 212, "Custom Field 13" },
1846   { 213, "Custom Field 14" },
1847   { 214, "Custom Field 15" },
1848   { 215, "Custom Field 16" },
1849   { 216, "Custom Field 17" },
1850   { 217, "Custom Field 18" },
1851   { 218, "Custom Field 19" },
1852   { 219, "Custom Field 20" }
1853 };
1854
1855 static int formatIPTC(Image *ifile, Image *ofile)
1856 {
1857   char
1858     temp[MaxTextExtent];
1859
1860   unsigned int
1861     foundiptc,
1862     tagsfound;
1863
1864   unsigned char
1865     recnum,
1866     dataset;
1867
1868   unsigned char
1869     *readable,
1870     *str;
1871
1872   ssize_t
1873     tagindx,
1874     taglen;
1875
1876   int
1877     i,
1878     tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
1879
1880   int
1881     c;
1882
1883   foundiptc = 0; /* found the IPTC-Header */
1884   tagsfound = 0; /* number of tags found */
1885
1886   c = ReadBlobByte(ifile);
1887   while (c != EOF)
1888   {
1889     if (c == 0x1c)
1890       foundiptc = 1;
1891     else
1892       {
1893         if (foundiptc)
1894           return -1;
1895         else
1896           continue;
1897       }
1898
1899     /* we found the 0x1c tag and now grab the dataset and record number tags */
1900     c = ReadBlobByte(ifile);
1901     if (c == EOF) return -1;
1902     dataset = (unsigned char) c;
1903     c = ReadBlobByte(ifile);
1904     if (c == EOF) return -1;
1905     recnum = (unsigned char) c;
1906     /* try to match this record to one of the ones in our named table */
1907     for (i=0; i< tagcount; i++)
1908     {
1909       if (tags[i].id == (short) recnum)
1910           break;
1911     }
1912     if (i < tagcount)
1913       readable = (unsigned char *) tags[i].name;
1914     else
1915       readable = (unsigned char *) "";
1916     /*
1917       We decode the length of the block that follows - ssize_t or short fmt.
1918     */
1919     c=ReadBlobByte(ifile);
1920     if (c == EOF) return -1;
1921     if (c & (unsigned char) 0x80)
1922       return 0;
1923     else
1924       {
1925         int
1926           c0;
1927
1928         c0=ReadBlobByte(ifile);
1929         if (c0 == EOF) return -1;
1930         taglen = (c << 8) | c0;
1931       }
1932     if (taglen < 0) return -1;
1933     /* make a buffer to hold the tag datand snag it from the input stream */
1934     str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MaxTextExtent),
1935       sizeof(*str));
1936     if (str == (unsigned char *) NULL)
1937       {
1938         printf("MemoryAllocationFailed");
1939         return 0;
1940       }
1941     for (tagindx=0; tagindx<taglen; tagindx++)
1942     {
1943       c=ReadBlobByte(ifile);
1944       if (c == EOF) return -1;
1945       str[tagindx] = (unsigned char) c;
1946     }
1947     str[taglen] = 0;
1948
1949     /* now finish up by formatting this binary data into ASCII equivalent */
1950     if (strlen((char *)readable) > 0)
1951       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d#%s=",
1952         (unsigned int) dataset, (unsigned int) recnum, readable);
1953     else
1954       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d=",
1955         (unsigned int) dataset,(unsigned int) recnum);
1956     (void) WriteBlobString(ofile,temp);
1957     formatString( ofile, (char *)str, taglen );
1958     str=(unsigned char *) RelinquishMagickMemory(str);
1959
1960     tagsfound++;
1961
1962     c=ReadBlobByte(ifile);
1963   }
1964   return((int) tagsfound);
1965 }
1966
1967 static int readWordFromBuffer(char **s, ssize_t *len)
1968 {
1969   unsigned char
1970     buffer[2];
1971
1972   int
1973     i,
1974     c;
1975
1976   for (i=0; i<2; i++)
1977   {
1978     c = *(*s)++; (*len)--;
1979     if (*len < 0) return -1;
1980     buffer[i] = (unsigned char) c;
1981   }
1982   return (((int) buffer[ 0 ]) <<  8) |
1983          (((int) buffer[ 1 ]));
1984 }
1985
1986 static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len)
1987 {
1988   char
1989     temp[MaxTextExtent];
1990
1991   unsigned int
1992     foundiptc,
1993     tagsfound;
1994
1995   unsigned char
1996     recnum,
1997     dataset;
1998
1999   unsigned char
2000     *readable,
2001     *str;
2002
2003   ssize_t
2004     tagindx,
2005     taglen;
2006
2007   int
2008     i,
2009     tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
2010
2011   int
2012     c;
2013
2014   foundiptc = 0; /* found the IPTC-Header */
2015   tagsfound = 0; /* number of tags found */
2016
2017   while (len > 0)
2018   {
2019     c = *s++; len--;
2020     if (c == 0x1c)
2021       foundiptc = 1;
2022     else
2023       {
2024         if (foundiptc)
2025           return -1;
2026         else
2027           continue;
2028       }
2029     /*
2030       We found the 0x1c tag and now grab the dataset and record number tags.
2031     */
2032     c = *s++; len--;
2033     if (len < 0) return -1;
2034     dataset = (unsigned char) c;
2035     c = *s++; len--;
2036     if (len < 0) return -1;
2037     recnum = (unsigned char) c;
2038     /* try to match this record to one of the ones in our named table */
2039     for (i=0; i< tagcount; i++)
2040       if (tags[i].id == (short) recnum)
2041         break;
2042     if (i < tagcount)
2043       readable=(unsigned char *) tags[i].name;
2044     else
2045       readable=(unsigned char *) "";
2046     /*
2047       We decode the length of the block that follows - ssize_t or short fmt.
2048     */
2049     c=(*s++);
2050     len--;
2051     if (len < 0)
2052       return(-1);
2053     if (c & (unsigned char) 0x80)
2054       return(0);
2055     else
2056       {
2057         s--;
2058         len++;
2059         taglen=readWordFromBuffer(&s, &len);
2060       }
2061     if (taglen < 0)
2062       return(-1);
2063     /* make a buffer to hold the tag datand snag it from the input stream */
2064     str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MaxTextExtent),
2065       sizeof(*str));
2066     if (str == (unsigned char *) NULL)
2067       {
2068         printf("MemoryAllocationFailed");
2069         return 0;
2070       }
2071     for (tagindx=0; tagindx<taglen; tagindx++)
2072     {
2073       c = *s++; len--;
2074       if (len < 0)
2075         return(-1);
2076       str[tagindx]=(unsigned char) c;
2077     }
2078     str[taglen]=0;
2079
2080     /* now finish up by formatting this binary data into ASCII equivalent */
2081     if (strlen((char *)readable) > 0)
2082       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d#%s=",
2083         (unsigned int) dataset,(unsigned int) recnum, readable);
2084     else
2085       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d=",
2086         (unsigned int) dataset,(unsigned int) recnum);
2087     (void) WriteBlobString(ofile,temp);
2088     formatString( ofile, (char *)str, taglen );
2089     str=(unsigned char *) RelinquishMagickMemory(str);
2090
2091     tagsfound++;
2092   }
2093   return ((int) tagsfound);
2094 }
2095
2096 static int format8BIM(Image *ifile, Image *ofile)
2097 {
2098   char
2099     temp[MaxTextExtent];
2100
2101   unsigned int
2102     foundOSType;
2103
2104   int
2105     ID,
2106     resCount,
2107     i,
2108     c;
2109
2110   ssize_t
2111     count;
2112
2113   unsigned char
2114     *PString,
2115     *str;
2116
2117   resCount=0;
2118   foundOSType=0; /* found the OSType */
2119   (void) foundOSType;
2120   c=ReadBlobByte(ifile);
2121   while (c != EOF)
2122   {
2123     if (c == '8')
2124       {
2125         unsigned char
2126           buffer[5];
2127
2128         buffer[0]=(unsigned char) c;
2129         for (i=1; i<4; i++)
2130         {
2131           c=ReadBlobByte(ifile);
2132           if (c == EOF)
2133             return(-1);
2134           buffer[i] = (unsigned char) c;
2135         }
2136         buffer[4]=0;
2137         if (strcmp((const char *)buffer, "8BIM") == 0)
2138           foundOSType=1;
2139         else
2140           continue;
2141       }
2142     else
2143       {
2144         c=ReadBlobByte(ifile);
2145         continue;
2146       }
2147     /*
2148       We found the OSType (8BIM) and now grab the ID, PString, and Size fields.
2149     */
2150     ID=(int) ReadBlobMSBShort(ifile);
2151     if (ID < 0)
2152       return(-1);
2153     {
2154       unsigned char
2155         plen;
2156
2157       c=ReadBlobByte(ifile);
2158       if (c == EOF)
2159         return(-1);
2160       plen = (unsigned char) c;
2161       PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+
2162         MaxTextExtent),sizeof(*PString));
2163       if (PString == (unsigned char *) NULL)
2164         {
2165           printf("MemoryAllocationFailed");
2166           return 0;
2167         }
2168       for (i=0; i<plen; i++)
2169       {
2170         c=ReadBlobByte(ifile);
2171         if (c == EOF) return -1;
2172         PString[i] = (unsigned char) c;
2173       }
2174       PString[ plen ] = 0;
2175       if ((plen & 0x01) == 0)
2176       {
2177         c=ReadBlobByte(ifile);
2178         if (c == EOF)
2179           return(-1);
2180       }
2181     }
2182     count = (int) ReadBlobMSBLong(ifile);
2183     if (count < 0) return -1;
2184     /* make a buffer to hold the datand snag it from the input stream */
2185     str=(unsigned char *) AcquireQuantumMemory((size_t) count,sizeof(*str));
2186     if (str == (unsigned char *) NULL)
2187       {
2188         printf("MemoryAllocationFailed");
2189         return 0;
2190       }
2191     for (i=0; i < (ssize_t) count; i++)
2192     {
2193       c=ReadBlobByte(ifile);
2194       if (c == EOF)
2195         return(-1);
2196       str[i]=(unsigned char) c;
2197     }
2198
2199     /* we currently skip thumbnails, since it does not make
2200      * any sense preserving them in a real world application
2201      */
2202     if (ID != THUMBNAIL_ID)
2203       {
2204         /* now finish up by formatting this binary data into
2205          * ASCII equivalent
2206          */
2207         if (strlen((const char *)PString) > 0)
2208           (void) FormatLocaleString(temp,MaxTextExtent,"8BIM#%d#%s=",ID,
2209             PString);
2210         else
2211           (void) FormatLocaleString(temp,MaxTextExtent,"8BIM#%d=",ID);
2212         (void) WriteBlobString(ofile,temp);
2213         if (ID == IPTC_ID)
2214           {
2215             formatString(ofile, "IPTC", 4);
2216             formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count);
2217           }
2218         else
2219           formatString(ofile, (char *)str, (ssize_t) count);
2220       }
2221     str=(unsigned char *) RelinquishMagickMemory(str);
2222     PString=(unsigned char *) RelinquishMagickMemory(PString);
2223     resCount++;
2224     c=ReadBlobByte(ifile);
2225   }
2226   return resCount;
2227 }
2228
2229 static MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
2230   Image *image,ExceptionInfo *exception)
2231 {
2232   const StringInfo
2233     *profile;
2234
2235   MagickBooleanType
2236     status;
2237
2238   size_t
2239     length;
2240
2241   /*
2242     Open image file.
2243   */
2244   assert(image_info != (const ImageInfo *) NULL);
2245   assert(image_info->signature == MagickSignature);
2246   assert(image != (Image *) NULL);
2247   assert(image->signature == MagickSignature);
2248   if (image->debug != MagickFalse)
2249     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2250   length=0;
2251   if (LocaleCompare(image_info->magick,"8BIM") == 0)
2252     {
2253       /*
2254         Write 8BIM image.
2255       */
2256       profile=GetImageProfile(image,"8bim");
2257       if (profile == (StringInfo *) NULL)
2258         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2259       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2260       if (status == MagickFalse)
2261         return(status);
2262       (void) WriteBlob(image,GetStringInfoLength(profile),
2263         GetStringInfoDatum(profile));
2264       (void) CloseBlob(image);
2265       return(MagickTrue);
2266     }
2267   if (LocaleCompare(image_info->magick,"iptc") == 0)
2268     {
2269       size_t
2270         length;
2271
2272       unsigned char
2273         *info;
2274
2275       profile=GetImageProfile(image,"iptc");
2276       if (profile == (StringInfo *) NULL)
2277         profile=GetImageProfile(image,"8bim");
2278       if (profile == (StringInfo *) NULL)
2279         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2280       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2281       info=GetStringInfoDatum(profile);
2282       length=GetStringInfoLength(profile);
2283       length=GetIPTCStream(&info,length);
2284       if (length == 0)
2285         ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2286       (void) WriteBlob(image,length,info);
2287       (void) CloseBlob(image);
2288       return(MagickTrue);
2289     }
2290   if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
2291     {
2292       Image
2293         *buff;
2294
2295       profile=GetImageProfile(image,"8bim");
2296       if (profile == (StringInfo *) NULL)
2297         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2298       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2299       if (status == MagickFalse)
2300         return(status);
2301       buff=AcquireImage((ImageInfo *) NULL);
2302       if (buff == (Image *) NULL)
2303         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2304       AttachBlob(buff->blob,GetStringInfoDatum(profile),
2305         GetStringInfoLength(profile));
2306       format8BIM(buff,image);
2307       (void) DetachBlob(buff->blob);
2308       buff=DestroyImage(buff);
2309       (void) CloseBlob(image);
2310       return(MagickTrue);
2311     }
2312   if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
2313     return(MagickFalse);
2314   if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
2315     {
2316       Image
2317         *buff;
2318
2319       unsigned char
2320         *info;
2321
2322       profile=GetImageProfile(image,"8bim");
2323       if (profile == (StringInfo *) NULL)
2324         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2325       info=GetStringInfoDatum(profile);
2326       length=GetStringInfoLength(profile);
2327       length=GetIPTCStream(&info,length);
2328       if (length == 0)
2329         ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2330       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2331       if (status == MagickFalse)
2332         return(status);
2333       buff=AcquireImage((ImageInfo *) NULL);
2334       if (buff == (Image *) NULL)
2335         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2336       AttachBlob(buff->blob,info,length);
2337       formatIPTC(buff,image);
2338       (void) DetachBlob(buff->blob);
2339       buff=DestroyImage(buff);
2340       (void) CloseBlob(image);
2341       return(MagickTrue);
2342     }
2343   if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
2344     return(MagickFalse);
2345   if ((LocaleCompare(image_info->magick,"APP1") == 0) ||
2346       (LocaleCompare(image_info->magick,"EXIF") == 0) ||
2347       (LocaleCompare(image_info->magick,"XMP") == 0))
2348     {
2349       /*
2350         (void) Write APP1 image.
2351       */
2352       profile=GetImageProfile(image,image_info->magick);
2353       if (profile == (StringInfo *) NULL)
2354         ThrowWriterException(CoderError,"NoAPP1DataIsAvailable");
2355       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2356       if (status == MagickFalse)
2357         return(status);
2358       (void) WriteBlob(image,GetStringInfoLength(profile),
2359         GetStringInfoDatum(profile));
2360       (void) CloseBlob(image);
2361       return(MagickTrue);
2362     }
2363   if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
2364       (LocaleCompare(image_info->magick,"ICM") == 0))
2365     {
2366       /*
2367         Write ICM image.
2368       */
2369       profile=GetImageProfile(image,"icc");
2370       if (profile == (StringInfo *) NULL)
2371         ThrowWriterException(CoderError,"NoColorProfileIsAvailable");
2372       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2373       if (status == MagickFalse)
2374         return(status);
2375       (void) WriteBlob(image,GetStringInfoLength(profile),
2376         GetStringInfoDatum(profile));
2377       (void) CloseBlob(image);
2378       return(MagickTrue);
2379     }
2380   return(MagickFalse);
2381 }