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