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