]> granicus.if.org Git - imagemagick/blob - coders/meta.c
788101298db96c41f87fbcb4bdf79bf56ac26457
[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=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1150         GetBlobSize(buff));
1151       if (profile == (StringInfo *) NULL)
1152         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1153       status=SetImageProfile(image,"8bim",profile);
1154       profile=DestroyStringInfo(profile);
1155       if (status == MagickFalse)
1156         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1157       blob=DetachBlob(buff->blob);
1158       blob=(unsigned char *) RelinquishMagickMemory(blob);
1159       buff=DestroyImage(buff);
1160     }
1161   if (LocaleNCompare(image_info->magick,"APP1",4) == 0)
1162     {
1163       char
1164         name[MaxTextExtent];
1165
1166       (void) FormatLocaleString(name,MaxTextExtent,"APP%d",1);
1167       buff=AcquireImage((ImageInfo *) NULL);
1168       if (buff == (Image *) NULL)
1169         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1170       blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1171       if (blob == (unsigned char *) NULL)
1172         {
1173           buff=DestroyImage(buff);
1174           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1175         }
1176       AttachBlob(buff->blob,blob,length);
1177       if (LocaleCompare(image_info->magick,"APP1JPEG") == 0)
1178         {
1179           Image
1180             *iptc;
1181
1182           int
1183             result;
1184
1185           if (image_info->profile == (void *) NULL)
1186             {
1187               blob=DetachBlob(buff->blob);
1188               blob=RelinquishMagickMemory(blob);
1189               buff=DestroyImage(buff);
1190               ThrowReaderException(CoderError,"NoIPTCProfileAvailable");
1191             }
1192           profile=CloneStringInfo((StringInfo *) image_info->profile);
1193           iptc=AcquireImage((ImageInfo *) NULL);
1194           if (iptc == (Image *) NULL)
1195             {
1196               blob=DetachBlob(buff->blob);
1197               blob=RelinquishMagickMemory(blob);
1198               buff=DestroyImage(buff);
1199               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1200             }
1201           AttachBlob(iptc->blob,GetStringInfoDatum(profile),
1202             GetStringInfoLength(profile));
1203           result=jpeg_embed(image,buff,iptc);
1204           blob=DetachBlob(iptc->blob);
1205           blob=RelinquishMagickMemory(blob);
1206           iptc=DestroyImage(iptc);
1207           if (result == 0)
1208             {
1209               blob=DetachBlob(buff->blob);
1210               blob=RelinquishMagickMemory(blob);
1211               buff=DestroyImage(buff);
1212               ThrowReaderException(CoderError,"JPEGEmbeddingFailed");
1213             }
1214         }
1215       else
1216         {
1217 #ifdef SLOW_METHOD
1218           for ( ; ; )
1219           {
1220             /* Really - really slow - FIX ME PLEASE!!!! */
1221             c=ReadBlobByte(image);
1222             if (c == EOF)
1223               break;
1224             (void) WriteBlobByte(buff,c);
1225           }
1226 #else
1227           ssize_t
1228             i;
1229
1230           unsigned char
1231             *buffer;
1232
1233           ssize_t
1234             count,
1235             length;
1236
1237           buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
1238             sizeof(*buffer));
1239           if (buffer != (unsigned char *) NULL)
1240             {
1241               i=0;
1242               while ((length=ReadBlob(image,MagickMaxBufferExtent,buffer)) != 0)
1243               {
1244                 count=0;
1245                 for (i=0; i < (ssize_t) length; i+=count)
1246                 {
1247                   count=WriteBlob(buff,(size_t) (length-i),buffer+i);
1248                   if (count <= 0)
1249                     break;
1250                 }
1251                 if (i < (ssize_t) length)
1252                   break;
1253               }
1254               buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1255             }
1256 #endif
1257         }
1258       profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1259         GetBlobSize(buff));
1260       if (profile == (StringInfo *) NULL)
1261         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1262       status=SetImageProfile(image,name,profile);
1263       profile=DestroyStringInfo(profile);
1264       if (status == MagickFalse)
1265         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1266       blob=DetachBlob(buff->blob);
1267       blob=RelinquishMagickMemory(blob);
1268       buff=DestroyImage(buff);
1269     }
1270   if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
1271       (LocaleCompare(image_info->magick,"ICM") == 0))
1272     {
1273       buff=AcquireImage((ImageInfo *) NULL);
1274       if (buff == (Image *) NULL)
1275         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1276       blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1277       if (blob == (unsigned char *) NULL)
1278         {
1279           buff=DestroyImage(buff);
1280           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1281         }
1282       AttachBlob(buff->blob,blob,length);
1283       for ( ; ; )
1284       {
1285         c=ReadBlobByte(image);
1286         if (c == EOF)
1287           break;
1288         (void) WriteBlobByte(buff,(unsigned char) c);
1289       }
1290       profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1291         GetBlobSize(buff));
1292       if (profile == (StringInfo *) NULL)
1293         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1294       (void) SetImageProfile(image,"icc",profile);
1295       profile=DestroyStringInfo(profile);
1296       blob=DetachBlob(buff->blob);
1297       blob=(unsigned char *) RelinquishMagickMemory(blob);
1298       buff=DestroyImage(buff);
1299     }
1300   if (LocaleCompare(image_info->magick,"IPTC") == 0)
1301     {
1302       register unsigned char
1303         *p;
1304
1305       buff=AcquireImage((ImageInfo *) NULL);
1306       if (buff == (Image *) NULL)
1307         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1308       blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1309       if (blob == (unsigned char *) NULL)
1310         {
1311           buff=DestroyImage(buff);
1312           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1313         }
1314       AttachBlob(buff->blob,blob,length);
1315       /* write out the header - length field patched below */
1316       (void) WriteBlob(buff,11,(unsigned char *) "8BIM\04\04\0\0\0\0\0");
1317       (void) WriteBlobByte(buff,0xc6);
1318       if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
1319         {
1320           length=(size_t) parse8BIM(image,buff);
1321           if (length & 1)
1322             (void) WriteBlobByte(buff,0x00);
1323         }
1324       else if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
1325         {
1326         }
1327       else
1328         {
1329           for ( ; ; )
1330           {
1331             c=ReadBlobByte(image);
1332             if (c == EOF)
1333               break;
1334             (void) WriteBlobByte(buff,(unsigned char) c);
1335           }
1336         }
1337       profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1338         GetBlobSize(buff));
1339       if (profile == (StringInfo *) NULL)
1340         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1341       /*
1342         subtract off the length of the 8BIM stuff.
1343       */
1344       length=GetStringInfoLength(profile)-12;
1345       p=GetStringInfoDatum(profile);
1346       p[10]=(unsigned char) (length >> 8);
1347       p[11]=(unsigned char) (length & 0xff);
1348       SetStringInfoDatum(profile,GetBlobStreamData(buff));
1349       (void) SetImageProfile(image,"8bim",profile);
1350       profile=DestroyStringInfo(profile);
1351       blob=DetachBlob(buff->blob);
1352       blob=(unsigned char *) RelinquishMagickMemory(blob);
1353       buff=DestroyImage(buff);
1354     }
1355   if (LocaleCompare(image_info->magick,"XMP") == 0)
1356     {
1357       buff=AcquireImage((ImageInfo *) NULL);
1358       if (buff == (Image *) NULL)
1359         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1360       blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1361       if (blob == (unsigned char *) NULL)
1362         {
1363           buff=DestroyImage(buff);
1364           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1365         }
1366       AttachBlob(buff->blob,blob,length);
1367       for ( ; ; )
1368       {
1369         c=ReadBlobByte(image);
1370         if (c == EOF)
1371           break;
1372         (void) WriteBlobByte(buff,(unsigned char) c);
1373       }
1374       profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1375         GetBlobSize(buff));
1376       if (profile == (StringInfo *) NULL)
1377         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1378       (void) SetImageProfile(image,"xmp",profile);
1379       profile=DestroyStringInfo(profile);
1380       blob=DetachBlob(buff->blob);
1381       blob=(unsigned char *) RelinquishMagickMemory(blob);
1382       buff=DestroyImage(buff);
1383     }
1384   (void) CloseBlob(image);
1385   return(GetFirstImageInList(image));
1386 }
1387 \f
1388 /*
1389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1390 %                                                                             %
1391 %                                                                             %
1392 %                                                                             %
1393 %   R e g i s t e r M E T A I m a g e                                         %
1394 %                                                                             %
1395 %                                                                             %
1396 %                                                                             %
1397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398 %
1399 %  RegisterMETAImage() adds attributes for the META image format to
1400 %  the list of supported formats.  The attributes include the image format
1401 %  tag, a method to read and/or write the format, whether the format
1402 %  supports the saving of more than one frame to the same file or blob,
1403 %  whether the format supports native in-memory I/O, and a brief
1404 %  description of the format.
1405 %
1406 %  The format of the RegisterMETAImage method is:
1407 %
1408 %      size_t RegisterMETAImage(void)
1409 %
1410 */
1411 ModuleExport size_t RegisterMETAImage(void)
1412 {
1413   MagickInfo
1414     *entry;
1415
1416   entry=SetMagickInfo("8BIM");
1417   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1418   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1419   entry->adjoin=MagickFalse;
1420   entry->stealth=MagickTrue;
1421   entry->seekable_stream=MagickTrue;
1422   entry->description=ConstantString("Photoshop resource format");
1423   entry->module=ConstantString("META");
1424   (void) RegisterMagickInfo(entry);
1425   entry=SetMagickInfo("8BIMTEXT");
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 text format");
1432   entry->module=ConstantString("META");
1433   (void) RegisterMagickInfo(entry);
1434   entry=SetMagickInfo("8BIMWTEXT");
1435   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1436   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1437   entry->adjoin=MagickFalse;
1438   entry->stealth=MagickTrue;
1439   entry->seekable_stream=MagickTrue;
1440   entry->description=ConstantString("Photoshop resource wide text format");
1441   entry->module=ConstantString("META");
1442   (void) RegisterMagickInfo(entry);
1443   entry=SetMagickInfo("APP1");
1444   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1445   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1446   entry->adjoin=MagickFalse;
1447   entry->stealth=MagickTrue;
1448   entry->seekable_stream=MagickTrue;
1449   entry->description=ConstantString("Raw application information");
1450   entry->module=ConstantString("META");
1451   (void) RegisterMagickInfo(entry);
1452   entry=SetMagickInfo("APP1JPEG");
1453   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1454   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1455   entry->adjoin=MagickFalse;
1456   entry->stealth=MagickTrue;
1457   entry->seekable_stream=MagickTrue;
1458   entry->description=ConstantString("Raw JPEG binary data");
1459   entry->module=ConstantString("META");
1460   (void) RegisterMagickInfo(entry);
1461   entry=SetMagickInfo("EXIF");
1462   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1463   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1464   entry->adjoin=MagickFalse;
1465   entry->stealth=MagickTrue;
1466   entry->seekable_stream=MagickTrue;
1467   entry->description=ConstantString("Exif digital camera binary data");
1468   entry->module=ConstantString("META");
1469   (void) RegisterMagickInfo(entry);
1470   entry=SetMagickInfo("XMP");
1471   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1472   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1473   entry->adjoin=MagickFalse;
1474   entry->stealth=MagickTrue;
1475   entry->seekable_stream=MagickTrue;
1476   entry->description=ConstantString("Adobe XML metadata");
1477   entry->module=ConstantString("META");
1478   (void) RegisterMagickInfo(entry);
1479   entry=SetMagickInfo("ICM");
1480   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1481   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1482   entry->adjoin=MagickFalse;
1483   entry->stealth=MagickTrue;
1484   entry->seekable_stream=MagickTrue;
1485   entry->description=ConstantString("ICC Color Profile");
1486   entry->module=ConstantString("META");
1487   (void) RegisterMagickInfo(entry);
1488   entry=SetMagickInfo("ICC");
1489   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1490   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1491   entry->adjoin=MagickFalse;
1492   entry->stealth=MagickTrue;
1493   entry->seekable_stream=MagickTrue;
1494   entry->description=ConstantString("ICC Color Profile");
1495   entry->module=ConstantString("META");
1496   (void) RegisterMagickInfo(entry);
1497   entry=SetMagickInfo("IPTC");
1498   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1499   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1500   entry->adjoin=MagickFalse;
1501   entry->stealth=MagickTrue;
1502   entry->seekable_stream=MagickTrue;
1503   entry->description=ConstantString("IPTC Newsphoto");
1504   entry->module=ConstantString("META");
1505   (void) RegisterMagickInfo(entry);
1506   entry=SetMagickInfo("IPTCTEXT");
1507   entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1508   entry->encoder=(EncodeImageHandler *) WriteMETAImage;
1509   entry->adjoin=MagickFalse;
1510   entry->stealth=MagickTrue;
1511   entry->seekable_stream=MagickTrue;
1512   entry->description=ConstantString("IPTC Newsphoto text format");
1513   entry->module=ConstantString("META");
1514   (void) RegisterMagickInfo(entry);
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,ExceptionInfo *exception)
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 %    o exception: return any errors or warnings in this structure.
1590 %
1591 */
1592
1593 static size_t GetIPTCStream(unsigned char **info,size_t length)
1594 {
1595   int
1596     c;
1597
1598   register ssize_t
1599     i;
1600
1601   register unsigned char
1602     *p;
1603
1604   size_t
1605     extent,
1606     info_length;
1607
1608   unsigned char
1609     buffer[4] = { '\0', '\0', '\0', '\0' };
1610
1611   unsigned int
1612     marker;
1613
1614   size_t
1615     tag_length;
1616
1617   p=(*info);
1618   extent=length;
1619   if ((*p == 0x1c) && (*(p+1) == 0x02))
1620     return(length);
1621   /*
1622     Extract IPTC from 8BIM resource block.
1623   */
1624   while (extent >= 12)
1625   {
1626     if (strncmp((const char *) p,"8BIM",4))
1627       break;
1628     p+=4;
1629     extent-=4;
1630     marker=(unsigned int) (*p) << 8 | *(p+1);
1631     p+=2;
1632     extent-=2;
1633     c=*p++;
1634     extent--;
1635     c|=0x01;
1636     if ((size_t) c >= extent)
1637       break;
1638     p+=c;
1639     extent-=c;
1640     if (extent < 4)
1641       break;
1642     tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) |
1643       (((size_t) *(p+2)) << 8) | ((size_t) *(p+3));
1644     p+=4;
1645     extent-=4;
1646     if (tag_length > extent)
1647       break;
1648     if (marker == IPTC_ID)
1649       {
1650         *info=p;
1651         return(tag_length);
1652       }
1653     if ((tag_length & 0x01) != 0)
1654       tag_length++;
1655     p+=tag_length;
1656     extent-=tag_length;
1657   }
1658   /*
1659     Find the beginning of the IPTC info.
1660   */
1661   p=(*info);
1662   tag_length=0;
1663 iptc_find:
1664   info_length=0;
1665   marker=MagickFalse;
1666   while (length != 0)
1667   {
1668     c=(*p++);
1669     length--;
1670     if (length == 0)
1671       break;
1672     if (c == 0x1c)
1673       {
1674         p--;
1675         *info=p; /* let the caller know were it is */
1676         break;
1677       }
1678   }
1679   /*
1680     Determine the length of the IPTC info.
1681   */
1682   while (length != 0)
1683   {
1684     c=(*p++);
1685     length--;
1686     if (length == 0)
1687       break;
1688     if (c == 0x1c)
1689       marker=MagickTrue;
1690     else
1691       if (marker)
1692         break;
1693       else
1694         continue;
1695     info_length++;
1696     /*
1697       Found the 0x1c tag; skip the dataset and record number tags.
1698     */
1699     c=(*p++); /* should be 2 */
1700     length--;
1701     if (length == 0)
1702       break;
1703     if ((info_length == 1) && (c != 2))
1704       goto iptc_find;
1705     info_length++;
1706     c=(*p++); /* should be 0 */
1707     length--;
1708     if (length == 0)
1709       break;
1710     if ((info_length == 2) && (c != 0))
1711       goto iptc_find;
1712     info_length++;
1713     /*
1714       Decode the length of the block that follows - ssize_t or short format.
1715     */
1716     c=(*p++);
1717     length--;
1718     if (length == 0)
1719       break;
1720     info_length++;
1721     if ((c & 0x80) != 0)
1722       {
1723         for (i=0; i < 4; i++)
1724         {
1725           buffer[i]=(*p++);
1726           length--;
1727           if (length == 0)
1728             break;
1729           info_length++;
1730         }
1731         tag_length=(((size_t) buffer[0]) << 24) |
1732           (((size_t) buffer[1]) << 16) |
1733           (((size_t) buffer[2]) << 8) | (((size_t) buffer[3])); 
1734       }
1735     else
1736       {
1737         tag_length=(size_t) (c << 8);
1738         c=(*p++);
1739         length--;
1740         if (length == 0)
1741           break;
1742         info_length++;
1743         tag_length|=c;
1744       }
1745     if (tag_length > (length+1))
1746       break;
1747     p+=tag_length;
1748     length-=tag_length;
1749     if (length == 0)
1750       break;
1751     info_length+=tag_length;
1752   }
1753   return(info_length);
1754 }
1755
1756 static void formatString(Image *ofile, const char *s, int len)
1757 {
1758   char
1759     temp[MaxTextExtent];
1760
1761   (void) WriteBlobByte(ofile,'"');
1762   for (; len > 0; len--, s++) {
1763     int c = (*s) & 255;
1764     switch (c) {
1765     case '&':
1766       (void) WriteBlobString(ofile,"&amp;");
1767       break;
1768 #ifdef HANDLE_GT_LT
1769     case '<':
1770       (void) WriteBlobString(ofile,"&lt;");
1771       break;
1772     case '>':
1773       (void) WriteBlobString(ofile,"&gt;");
1774       break;
1775 #endif
1776     case '"':
1777       (void) WriteBlobString(ofile,"&quot;");
1778       break;
1779     default:
1780       if (isprint(c))
1781         (void) WriteBlobByte(ofile,(unsigned char) *s);
1782       else
1783         {
1784           (void) FormatLocaleString(temp,MaxTextExtent,"&#%d;", c & 255);
1785           (void) WriteBlobString(ofile,temp);
1786         }
1787       break;
1788     }
1789   }
1790 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1791   (void) WriteBlobString(ofile,"\"\r\n");
1792 #else
1793 #if defined(macintosh)
1794   (void) WriteBlobString(ofile,"\"\r");
1795 #else
1796   (void) WriteBlobString(ofile,"\"\n");
1797 #endif
1798 #endif
1799 }
1800
1801 typedef struct _tag_spec
1802 {
1803   short
1804     id;
1805
1806   const char
1807     *name;
1808 } tag_spec;
1809
1810 static const tag_spec tags[] = {
1811   { 5, "Image Name" },
1812   { 7, "Edit Status" },
1813   { 10, "Priority" },
1814   { 15, "Category" },
1815   { 20, "Supplemental Category" },
1816   { 22, "Fixture Identifier" },
1817   { 25, "Keyword" },
1818   { 30, "Release Date" },
1819   { 35, "Release Time" },
1820   { 40, "Special Instructions" },
1821   { 45, "Reference Service" },
1822   { 47, "Reference Date" },
1823   { 50, "Reference Number" },
1824   { 55, "Created Date" },
1825   { 60, "Created Time" },
1826   { 65, "Originating Program" },
1827   { 70, "Program Version" },
1828   { 75, "Object Cycle" },
1829   { 80, "Byline" },
1830   { 85, "Byline Title" },
1831   { 90, "City" },
1832   { 95, "Province State" },
1833   { 100, "Country Code" },
1834   { 101, "Country" },
1835   { 103, "Original Transmission Reference" },
1836   { 105, "Headline" },
1837   { 110, "Credit" },
1838   { 115, "Source" },
1839   { 116, "Copyright String" },
1840   { 120, "Caption" },
1841   { 121, "Image Orientation" },
1842   { 122, "Caption Writer" },
1843   { 131, "Local Caption" },
1844   { 200, "Custom Field 1" },
1845   { 201, "Custom Field 2" },
1846   { 202, "Custom Field 3" },
1847   { 203, "Custom Field 4" },
1848   { 204, "Custom Field 5" },
1849   { 205, "Custom Field 6" },
1850   { 206, "Custom Field 7" },
1851   { 207, "Custom Field 8" },
1852   { 208, "Custom Field 9" },
1853   { 209, "Custom Field 10" },
1854   { 210, "Custom Field 11" },
1855   { 211, "Custom Field 12" },
1856   { 212, "Custom Field 13" },
1857   { 213, "Custom Field 14" },
1858   { 214, "Custom Field 15" },
1859   { 215, "Custom Field 16" },
1860   { 216, "Custom Field 17" },
1861   { 217, "Custom Field 18" },
1862   { 218, "Custom Field 19" },
1863   { 219, "Custom Field 20" }
1864 };
1865
1866 static int formatIPTC(Image *ifile, Image *ofile)
1867 {
1868   char
1869     temp[MaxTextExtent];
1870
1871   unsigned int
1872     foundiptc,
1873     tagsfound;
1874
1875   unsigned char
1876     recnum,
1877     dataset;
1878
1879   unsigned char
1880     *readable,
1881     *str;
1882
1883   ssize_t
1884     tagindx,
1885     taglen;
1886
1887   int
1888     i,
1889     tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
1890
1891   int
1892     c;
1893
1894   foundiptc = 0; /* found the IPTC-Header */
1895   tagsfound = 0; /* number of tags found */
1896
1897   c = ReadBlobByte(ifile);
1898   while (c != EOF)
1899   {
1900     if (c == 0x1c)
1901       foundiptc = 1;
1902     else
1903       {
1904         if (foundiptc)
1905           return -1;
1906         else
1907           continue;
1908       }
1909
1910     /* we found the 0x1c tag and now grab the dataset and record number tags */
1911     c = ReadBlobByte(ifile);
1912     if (c == EOF) return -1;
1913     dataset = (unsigned char) c;
1914     c = ReadBlobByte(ifile);
1915     if (c == EOF) return -1;
1916     recnum = (unsigned char) c;
1917     /* try to match this record to one of the ones in our named table */
1918     for (i=0; i< tagcount; i++)
1919     {
1920       if (tags[i].id == (short) recnum)
1921           break;
1922     }
1923     if (i < tagcount)
1924       readable = (unsigned char *) tags[i].name;
1925     else
1926       readable = (unsigned char *) "";
1927     /*
1928       We decode the length of the block that follows - ssize_t or short fmt.
1929     */
1930     c=ReadBlobByte(ifile);
1931     if (c == EOF) return -1;
1932     if (c & (unsigned char) 0x80)
1933       return 0;
1934     else
1935       {
1936         int
1937           c0;
1938
1939         c0=ReadBlobByte(ifile);
1940         if (c0 == EOF) return -1;
1941         taglen = (c << 8) | c0;
1942       }
1943     if (taglen < 0) return -1;
1944     /* make a buffer to hold the tag datand snag it from the input stream */
1945     str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MaxTextExtent),
1946       sizeof(*str));
1947     if (str == (unsigned char *) NULL)
1948       {
1949         printf("MemoryAllocationFailed");
1950         return 0;
1951       }
1952     for (tagindx=0; tagindx<taglen; tagindx++)
1953     {
1954       c=ReadBlobByte(ifile);
1955       if (c == EOF) return -1;
1956       str[tagindx] = (unsigned char) c;
1957     }
1958     str[taglen] = 0;
1959
1960     /* now finish up by formatting this binary data into ASCII equivalent */
1961     if (strlen((char *)readable) > 0)
1962       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d#%s=",
1963         (unsigned int) dataset, (unsigned int) recnum, readable);
1964     else
1965       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d=",
1966         (unsigned int) dataset,(unsigned int) recnum);
1967     (void) WriteBlobString(ofile,temp);
1968     formatString( ofile, (char *)str, taglen );
1969     str=(unsigned char *) RelinquishMagickMemory(str);
1970
1971     tagsfound++;
1972
1973     c=ReadBlobByte(ifile);
1974   }
1975   return((int) tagsfound);
1976 }
1977
1978 static int readWordFromBuffer(char **s, ssize_t *len)
1979 {
1980   unsigned char
1981     buffer[2];
1982
1983   int
1984     i,
1985     c;
1986
1987   for (i=0; i<2; i++)
1988   {
1989     c = *(*s)++; (*len)--;
1990     if (*len < 0) return -1;
1991     buffer[i] = (unsigned char) c;
1992   }
1993   return (((int) buffer[ 0 ]) <<  8) |
1994          (((int) buffer[ 1 ]));
1995 }
1996
1997 static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len)
1998 {
1999   char
2000     temp[MaxTextExtent];
2001
2002   unsigned int
2003     foundiptc,
2004     tagsfound;
2005
2006   unsigned char
2007     recnum,
2008     dataset;
2009
2010   unsigned char
2011     *readable,
2012     *str;
2013
2014   ssize_t
2015     tagindx,
2016     taglen;
2017
2018   int
2019     i,
2020     tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
2021
2022   int
2023     c;
2024
2025   foundiptc = 0; /* found the IPTC-Header */
2026   tagsfound = 0; /* number of tags found */
2027
2028   while (len > 0)
2029   {
2030     c = *s++; len--;
2031     if (c == 0x1c)
2032       foundiptc = 1;
2033     else
2034       {
2035         if (foundiptc)
2036           return -1;
2037         else
2038           continue;
2039       }
2040     /*
2041       We found the 0x1c tag and now grab the dataset and record number tags.
2042     */
2043     c = *s++; len--;
2044     if (len < 0) return -1;
2045     dataset = (unsigned char) c;
2046     c = *s++; len--;
2047     if (len < 0) return -1;
2048     recnum = (unsigned char) c;
2049     /* try to match this record to one of the ones in our named table */
2050     for (i=0; i< tagcount; i++)
2051       if (tags[i].id == (short) recnum)
2052         break;
2053     if (i < tagcount)
2054       readable=(unsigned char *) tags[i].name;
2055     else
2056       readable=(unsigned char *) "";
2057     /*
2058       We decode the length of the block that follows - ssize_t or short fmt.
2059     */
2060     c=(*s++);
2061     len--;
2062     if (len < 0)
2063       return(-1);
2064     if (c & (unsigned char) 0x80)
2065       return(0);
2066     else
2067       {
2068         s--;
2069         len++;
2070         taglen=readWordFromBuffer(&s, &len);
2071       }
2072     if (taglen < 0)
2073       return(-1);
2074     /* make a buffer to hold the tag datand snag it from the input stream */
2075     str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MaxTextExtent),
2076       sizeof(*str));
2077     if (str == (unsigned char *) NULL)
2078       {
2079         printf("MemoryAllocationFailed");
2080         return 0;
2081       }
2082     for (tagindx=0; tagindx<taglen; tagindx++)
2083     {
2084       c = *s++; len--;
2085       if (len < 0)
2086         return(-1);
2087       str[tagindx]=(unsigned char) c;
2088     }
2089     str[taglen]=0;
2090
2091     /* now finish up by formatting this binary data into ASCII equivalent */
2092     if (strlen((char *)readable) > 0)
2093       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d#%s=",
2094         (unsigned int) dataset,(unsigned int) recnum, readable);
2095     else
2096       (void) FormatLocaleString(temp,MaxTextExtent,"%d#%d=",
2097         (unsigned int) dataset,(unsigned int) recnum);
2098     (void) WriteBlobString(ofile,temp);
2099     formatString( ofile, (char *)str, taglen );
2100     str=(unsigned char *) RelinquishMagickMemory(str);
2101
2102     tagsfound++;
2103   }
2104   return ((int) tagsfound);
2105 }
2106
2107 static int format8BIM(Image *ifile, Image *ofile)
2108 {
2109   char
2110     temp[MaxTextExtent];
2111
2112   unsigned int
2113     foundOSType;
2114
2115   int
2116     ID,
2117     resCount,
2118     i,
2119     c;
2120
2121   ssize_t
2122     count;
2123
2124   unsigned char
2125     *PString,
2126     *str;
2127
2128   resCount=0;
2129   foundOSType=0; /* found the OSType */
2130   (void) foundOSType;
2131   c=ReadBlobByte(ifile);
2132   while (c != EOF)
2133   {
2134     if (c == '8')
2135       {
2136         unsigned char
2137           buffer[5];
2138
2139         buffer[0]=(unsigned char) c;
2140         for (i=1; i<4; i++)
2141         {
2142           c=ReadBlobByte(ifile);
2143           if (c == EOF)
2144             return(-1);
2145           buffer[i] = (unsigned char) c;
2146         }
2147         buffer[4]=0;
2148         if (strcmp((const char *)buffer, "8BIM") == 0)
2149           foundOSType=1;
2150         else
2151           continue;
2152       }
2153     else
2154       {
2155         c=ReadBlobByte(ifile);
2156         continue;
2157       }
2158     /*
2159       We found the OSType (8BIM) and now grab the ID, PString, and Size fields.
2160     */
2161     ID=(int) ReadBlobMSBShort(ifile);
2162     if (ID < 0)
2163       return(-1);
2164     {
2165       unsigned char
2166         plen;
2167
2168       c=ReadBlobByte(ifile);
2169       if (c == EOF)
2170         return(-1);
2171       plen = (unsigned char) c;
2172       PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+
2173         MaxTextExtent),sizeof(*PString));
2174       if (PString == (unsigned char *) NULL)
2175         {
2176           printf("MemoryAllocationFailed");
2177           return 0;
2178         }
2179       for (i=0; i<plen; i++)
2180       {
2181         c=ReadBlobByte(ifile);
2182         if (c == EOF) return -1;
2183         PString[i] = (unsigned char) c;
2184       }
2185       PString[ plen ] = 0;
2186       if ((plen & 0x01) == 0)
2187       {
2188         c=ReadBlobByte(ifile);
2189         if (c == EOF)
2190           return(-1);
2191       }
2192     }
2193     count = (int) ReadBlobMSBLong(ifile);
2194     if (count < 0) return -1;
2195     /* make a buffer to hold the datand snag it from the input stream */
2196     str=(unsigned char *) AcquireQuantumMemory((size_t) count,sizeof(*str));
2197     if (str == (unsigned char *) NULL)
2198       {
2199         printf("MemoryAllocationFailed");
2200         return 0;
2201       }
2202     for (i=0; i < (ssize_t) count; i++)
2203     {
2204       c=ReadBlobByte(ifile);
2205       if (c == EOF)
2206         return(-1);
2207       str[i]=(unsigned char) c;
2208     }
2209
2210     /* we currently skip thumbnails, since it does not make
2211      * any sense preserving them in a real world application
2212      */
2213     if (ID != THUMBNAIL_ID)
2214       {
2215         /* now finish up by formatting this binary data into
2216          * ASCII equivalent
2217          */
2218         if (strlen((const char *)PString) > 0)
2219           (void) FormatLocaleString(temp,MaxTextExtent,"8BIM#%d#%s=",ID,
2220             PString);
2221         else
2222           (void) FormatLocaleString(temp,MaxTextExtent,"8BIM#%d=",ID);
2223         (void) WriteBlobString(ofile,temp);
2224         if (ID == IPTC_ID)
2225           {
2226             formatString(ofile, "IPTC", 4);
2227             formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count);
2228           }
2229         else
2230           formatString(ofile, (char *)str, (ssize_t) count);
2231       }
2232     str=(unsigned char *) RelinquishMagickMemory(str);
2233     PString=(unsigned char *) RelinquishMagickMemory(PString);
2234     resCount++;
2235     c=ReadBlobByte(ifile);
2236   }
2237   return resCount;
2238 }
2239
2240 static MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
2241   Image *image,ExceptionInfo *exception)
2242 {
2243   const StringInfo
2244     *profile;
2245
2246   MagickBooleanType
2247     status;
2248
2249   size_t
2250     length;
2251
2252   /*
2253     Open image file.
2254   */
2255   assert(image_info != (const ImageInfo *) NULL);
2256   assert(image_info->signature == MagickSignature);
2257   assert(image != (Image *) NULL);
2258   assert(image->signature == MagickSignature);
2259   if (image->debug != MagickFalse)
2260     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2261   length=0;
2262   if (LocaleCompare(image_info->magick,"8BIM") == 0)
2263     {
2264       /*
2265         Write 8BIM image.
2266       */
2267       profile=GetImageProfile(image,"8bim");
2268       if (profile == (StringInfo *) NULL)
2269         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2270       assert(exception != (ExceptionInfo *) NULL);
2271   assert(exception->signature == MagickSignature);
2272   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2273       if (status == MagickFalse)
2274         return(status);
2275       (void) WriteBlob(image,GetStringInfoLength(profile),
2276         GetStringInfoDatum(profile));
2277       (void) CloseBlob(image);
2278       return(MagickTrue);
2279     }
2280   if (LocaleCompare(image_info->magick,"iptc") == 0)
2281     {
2282       size_t
2283         length;
2284
2285       unsigned char
2286         *info;
2287
2288       profile=GetImageProfile(image,"iptc");
2289       if (profile == (StringInfo *) NULL)
2290         profile=GetImageProfile(image,"8bim");
2291       if (profile == (StringInfo *) NULL)
2292         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2293       assert(exception != (ExceptionInfo *) NULL);
2294   assert(exception->signature == MagickSignature);
2295   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2296       info=GetStringInfoDatum(profile);
2297       length=GetStringInfoLength(profile);
2298       length=GetIPTCStream(&info,length);
2299       if (length == 0)
2300         ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2301       (void) WriteBlob(image,length,info);
2302       (void) CloseBlob(image);
2303       return(MagickTrue);
2304     }
2305   if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
2306     {
2307       Image
2308         *buff;
2309
2310       profile=GetImageProfile(image,"8bim");
2311       if (profile == (StringInfo *) NULL)
2312         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2313       assert(exception != (ExceptionInfo *) NULL);
2314   assert(exception->signature == MagickSignature);
2315   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2316       if (status == MagickFalse)
2317         return(status);
2318       buff=AcquireImage((ImageInfo *) NULL);
2319       if (buff == (Image *) NULL)
2320         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2321       AttachBlob(buff->blob,GetStringInfoDatum(profile),
2322         GetStringInfoLength(profile));
2323       format8BIM(buff,image);
2324       (void) DetachBlob(buff->blob);
2325       buff=DestroyImage(buff);
2326       (void) CloseBlob(image);
2327       return(MagickTrue);
2328     }
2329   if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
2330     return(MagickFalse);
2331   if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
2332     {
2333       Image
2334         *buff;
2335
2336       unsigned char
2337         *info;
2338
2339       profile=GetImageProfile(image,"8bim");
2340       if (profile == (StringInfo *) NULL)
2341         ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2342       info=GetStringInfoDatum(profile);
2343       length=GetStringInfoLength(profile);
2344       length=GetIPTCStream(&info,length);
2345       if (length == 0)
2346         ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2347       assert(exception != (ExceptionInfo *) NULL);
2348   assert(exception->signature == MagickSignature);
2349   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2350       if (status == MagickFalse)
2351         return(status);
2352       buff=AcquireImage((ImageInfo *) NULL);
2353       if (buff == (Image *) NULL)
2354         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2355       AttachBlob(buff->blob,info,length);
2356       formatIPTC(buff,image);
2357       (void) DetachBlob(buff->blob);
2358       buff=DestroyImage(buff);
2359       (void) CloseBlob(image);
2360       return(MagickTrue);
2361     }
2362   if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
2363     return(MagickFalse);
2364   if ((LocaleCompare(image_info->magick,"APP1") == 0) ||
2365       (LocaleCompare(image_info->magick,"EXIF") == 0) ||
2366       (LocaleCompare(image_info->magick,"XMP") == 0))
2367     {
2368       /*
2369         (void) Write APP1 image.
2370       */
2371       profile=GetImageProfile(image,image_info->magick);
2372       if (profile == (StringInfo *) NULL)
2373         ThrowWriterException(CoderError,"NoAPP1DataIsAvailable");
2374       assert(exception != (ExceptionInfo *) NULL);
2375   assert(exception->signature == MagickSignature);
2376   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2377       if (status == MagickFalse)
2378         return(status);
2379       (void) WriteBlob(image,GetStringInfoLength(profile),
2380         GetStringInfoDatum(profile));
2381       (void) CloseBlob(image);
2382       return(MagickTrue);
2383     }
2384   if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
2385       (LocaleCompare(image_info->magick,"ICM") == 0))
2386     {
2387       /*
2388         Write ICM image.
2389       */
2390       profile=GetImageProfile(image,"icc");
2391       if (profile == (StringInfo *) NULL)
2392         ThrowWriterException(CoderError,"NoColorProfileIsAvailable");
2393       assert(exception != (ExceptionInfo *) NULL);
2394   assert(exception->signature == MagickSignature);
2395   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2396       if (status == MagickFalse)
2397         return(status);
2398       (void) WriteBlob(image,GetStringInfoLength(profile),
2399         GetStringInfoDatum(profile));
2400       (void) CloseBlob(image);
2401       return(MagickTrue);
2402     }
2403   return(MagickFalse);
2404 }