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