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