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