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,"<",'<' },
155 { 4,">",'>' },
156 #endif
157 { 5,"&",'&' },
158 { 6,""",'"' },
159 { 6,"'",'\''}
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,"ed)==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,"ed)==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,"ed)==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,"ed)==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,"ed)==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,"ed)==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,"&");
1823 break;
1824 #ifdef HANDLE_GT_LT
1825 case '<':
1826 (void) WriteBlobString(ofile,"<");
1827 break;
1828 case '>':
1829 (void) WriteBlobString(ofile,">");
1830 break;
1831 #endif
1832 case '"':
1833 (void) WriteBlobString(ofile,""");
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