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