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