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