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