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