blob: 7b3e936c3acf801ddf456cae9221095cfd35d671 [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% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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"
cristy6a2180c2013-05-27 10:28:36 +000045#include "MagickCore/channel.h"
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/exception.h"
47#include "MagickCore/exception-private.h"
48#include "MagickCore/image.h"
49#include "MagickCore/image-private.h"
50#include "MagickCore/list.h"
51#include "MagickCore/magick.h"
52#include "MagickCore/memory_.h"
53#include "MagickCore/module.h"
54#include "MagickCore/profile.h"
55#include "MagickCore/splay-tree.h"
56#include "MagickCore/quantum-private.h"
57#include "MagickCore/static.h"
58#include "MagickCore/string_.h"
59#include "MagickCore/string-private.h"
60#include "MagickCore/token.h"
61#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000062
63/*
64 Forward declarations.
65*/
66static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000067 WriteMETAImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000068
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
96static 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*/
cristy3ed852e2009-09-05 21:47:34 +0000143
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{
cristyf432c632014-12-07 15:11:28 +0000198 if (len <=0 || s==(char*) NULL || *s=='\0')
cristy3ed852e2009-09-05 21:47:34 +0000199 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)
cristy29d40332014-12-30 23:48:22 +0000215 (void) memmove(s+1,s+1+o,strlen(s+1+o)+1);
cristy3ed852e2009-09-05 21:47:34 +0000216 *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 {
cristy29d40332014-12-30 23:48:22 +0000231 (void) memmove(s+1,s+html_codes[i].len,
232 strlen(s+html_codes[i].len)+1);
233 *s=html_codes[i].val;
cristy3ed852e2009-09-05 21:47:34 +0000234 return html_codes[i].len-1;
235 }
236 }
237 }
238 return 0;
239}
240
241static char *super_fgets(char **b, int *blen, Image *file)
242{
243 int
244 c,
245 len;
246
247 unsigned char
248 *p,
249 *q;
250
251 len=*blen;
252 p=(unsigned char *) (*b);
253 for (q=p; ; q++)
254 {
255 c=ReadBlobByte(file);
256 if (c == EOF || c == '\n')
257 break;
258 if ((q-p+1) >= (int) len)
259 {
260 int
261 tlen;
262
263 tlen=q-p;
264 len<<=1;
265 p=(unsigned char *) ResizeQuantumMemory(p,(size_t) len+2UL,sizeof(*p));
266 *b=(char *) p;
267 if (p == (unsigned char *) NULL)
268 break;
269 q=p+tlen;
270 }
271 *q=(unsigned char) c;
272 }
273 *blen=0;
274 if (p != (unsigned char *) NULL)
275 {
276 int
277 tlen;
278
279 tlen=q-p;
280 if (tlen == 0)
281 return (char *) NULL;
282 p[tlen] = '\0';
283 *blen=++tlen;
284 }
285 return((char *) p);
286}
287
cristy3ed852e2009-09-05 21:47:34 +0000288#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
cristy151b66d2015-04-15 10:50:31 +0000312 inputlen = MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000313
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));
cristyf67023c2015-03-23 14:28:39 +0000328 if (line == (char *) NULL)
cristyd4f11052015-03-23 14:35:47 +0000329 return(-1);
330 newstr = name = token = (char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000331 savedpos = 0;
332 token_info=AcquireTokenInfo();
333 while (super_fgets(&line,&inputlen,ifile)!=NULL)
334 {
335 state=0;
336 next=0;
337
338 token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token));
cristyf67023c2015-03-23 14:28:39 +0000339 if (token == (char *) NULL)
340 break;
cristyd4f11052015-03-23 14:35:47 +0000341 newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr));
cristyf67023c2015-03-23 14:28:39 +0000342 if (newstr == (char *) NULL)
343 break;
cristy3ed852e2009-09-05 21:47:34 +0000344 while (Tokenizer(token_info,0,token,(size_t) inputlen,line,"","=","\"",0,
345 &brkused,&next,&quoted)==0)
346 {
347 if (state == 0)
348 {
349 int
350 state,
351 next;
352
353 char
354 brkused,
355 quoted;
356
357 state=0;
358 next=0;
359 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","#",
360 "", 0,&brkused,&next,&quoted)==0)
361 {
362 switch (state)
363 {
364 case 0:
365 if (strcmp(newstr,"8BIM")==0)
366 dataset = 255;
367 else
cristyf2f27272009-12-17 14:48:46 +0000368 dataset = (unsigned char) StringToLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000369 break;
370 case 1:
cristye27293e2009-12-18 02:53:20 +0000371 recnum = (unsigned int) StringToUnsignedLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000372 break;
373 case 2:
cristy151b66d2015-04-15 10:50:31 +0000374 name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000375 sizeof(*name));
376 if (name)
377 (void) strcpy(name,newstr);
378 break;
379 }
380 state++;
381 }
382 }
383 else
384 if (state == 1)
385 {
386 int
387 next;
388
cristybb503372010-05-27 20:51:26 +0000389 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000390 len;
391
392 char
393 brkused,
394 quoted;
395
396 next=0;
cristybb503372010-05-27 20:51:26 +0000397 len = (ssize_t) strlen(token);
cristy3ed852e2009-09-05 21:47:34 +0000398 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","&",
399 "",0,&brkused,&next,&quoted)==0)
400 {
401 if (brkused && next > 0)
402 {
403 char
404 *s = &token[next-1];
405
cristybb503372010-05-27 20:51:26 +0000406 len -= (ssize_t) convertHTMLcodes(s,(int) strlen(s));
cristy3ed852e2009-09-05 21:47:34 +0000407 }
408 }
409
410 if (dataset == 255)
411 {
412 unsigned char
413 nlen = 0;
414
415 int
416 i;
417
418 if (savedolen > 0)
419 {
420 MagickOffsetType
421 offset;
422
cristybb503372010-05-27 20:51:26 +0000423 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000424 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000425 if (currentpos < 0)
426 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000427 offset=SeekBlob(ofile,savedpos,SEEK_SET);
428 if (offset < 0)
429 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000430 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000431 offset=SeekBlob(ofile,currentpos,SEEK_SET);
432 if (offset < 0)
433 return(-1);
434 savedolen = 0L;
435 }
436 if (outputlen & 1)
437 {
438 (void) WriteBlobByte(ofile,0x00);
439 outputlen++;
440 }
441 (void) WriteBlobString(ofile,"8BIM");
442 (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
443 outputlen += 6;
444 if (name)
445 nlen = (unsigned char) strlen(name);
446 (void) WriteBlobByte(ofile,nlen);
447 outputlen++;
448 for (i=0; i<nlen; i++)
449 (void) WriteBlobByte(ofile,(unsigned char) name[i]);
450 outputlen += nlen;
451 if ((nlen & 0x01) == 0)
452 {
453 (void) WriteBlobByte(ofile,0x00);
454 outputlen++;
455 }
456 if (recnum != IPTC_ID)
457 {
cristyf9cca6a2010-06-04 23:49:28 +0000458 (void) WriteBlobMSBLong(ofile, (unsigned int) len);
cristy3ed852e2009-09-05 21:47:34 +0000459 outputlen += 4;
460
461 next=0;
462 outputlen += len;
463 while (len--)
464 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
465
466 if (outputlen & 1)
467 {
468 (void) WriteBlobByte(ofile,0x00);
469 outputlen++;
470 }
471 }
472 else
473 {
474 /* patch in a fake length for now and fix it later */
475 savedpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000476 if (savedpos < 0)
477 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000478 (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU);
cristy3ed852e2009-09-05 21:47:34 +0000479 outputlen += 4;
480 savedolen = outputlen;
481 }
482 }
483 else
484 {
485 if (len <= 0x7FFF)
486 {
487 (void) WriteBlobByte(ofile,0x1c);
488 (void) WriteBlobByte(ofile,(unsigned char) dataset);
489 (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
490 (void) WriteBlobMSBShort(ofile,(unsigned short) len);
491 outputlen += 5;
492 next=0;
493 outputlen += len;
494 while (len--)
495 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
496 }
497 }
498 }
499 state++;
500 }
cristyf67023c2015-03-23 14:28:39 +0000501 if (token != (char *) NULL)
502 token=DestroyString(token);
503 if (newstr != (char *) NULL)
504 newstr=DestroyString(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000505 if (name != (char *) NULL)
506 name=DestroyString(name);
507 }
508 token_info=DestroyTokenInfo(token_info);
cristyd4f11052015-03-23 14:35:47 +0000509 if (token != (char *) NULL)
510 token=DestroyString(token);
511 if (newstr != (char *) NULL)
512 newstr=DestroyString(newstr);
513 if (name != (char *) NULL)
514 name=DestroyString(name);
cristy3ed852e2009-09-05 21:47:34 +0000515 line=DestroyString(line);
516 if (savedolen > 0)
517 {
518 MagickOffsetType
519 offset;
520
cristybb503372010-05-27 20:51:26 +0000521 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000522
523 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000524 if (currentpos < 0)
525 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000526 offset=SeekBlob(ofile,savedpos,SEEK_SET);
527 if (offset < 0)
528 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000529 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000530 offset=SeekBlob(ofile,currentpos,SEEK_SET);
531 if (offset < 0)
532 return(-1);
533 savedolen = 0L;
534 }
535 return outputlen;
536}
537
538static char *super_fgets_w(char **b, int *blen, Image *file)
539{
540 int
541 c,
542 len;
543
544 unsigned char
545 *p,
546 *q;
547
548 len=*blen;
549 p=(unsigned char *) (*b);
550 for (q=p; ; q++)
551 {
552 c=(int) ReadBlobLSBShort(file);
553 if ((c == -1) || (c == '\n'))
554 break;
555 if (EOFBlob(file))
556 break;
557 if ((q-p+1) >= (int) len)
558 {
559 int
560 tlen;
561
562 tlen=q-p;
563 len<<=1;
564 p=(unsigned char *) ResizeQuantumMemory(p,(size_t) (len+2),sizeof(*p));
565 *b=(char *) p;
566 if (p == (unsigned char *) NULL)
567 break;
568 q=p+tlen;
569 }
570 *q=(unsigned char) c;
571 }
572 *blen=0;
573 if ((*b) != (char *) NULL)
574 {
575 int
576 tlen;
577
578 tlen=q-p;
579 if (tlen == 0)
580 return (char *) NULL;
581 p[tlen] = '\0';
582 *blen=++tlen;
583 }
584 return((char *) p);
585}
586
cristybb503372010-05-27 20:51:26 +0000587static ssize_t parse8BIMW(Image *ifile, Image *ofile)
cristy3ed852e2009-09-05 21:47:34 +0000588{
589 char
590 brkused,
591 quoted,
592 *line,
593 *token,
594 *newstr,
595 *name;
596
597 int
598 state,
599 next;
600
601 unsigned char
602 dataset;
603
604 unsigned int
605 recnum;
606
607 int
cristy151b66d2015-04-15 10:50:31 +0000608 inputlen = MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000609
cristybb503372010-05-27 20:51:26 +0000610 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000611 savedolen = 0L,
612 outputlen = 0L;
613
614 MagickOffsetType
615 savedpos,
616 currentpos;
617
618 TokenInfo
619 *token_info;
620
621 dataset = 0;
622 recnum = 0;
623 line=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line));
cristyf67023c2015-03-23 14:28:39 +0000624 if (line == (char *) NULL)
625 return(-1);
cristyd4f11052015-03-23 14:35:47 +0000626 newstr = name = token = (char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000627 savedpos = 0;
628 token_info=AcquireTokenInfo();
629 while (super_fgets_w(&line,&inputlen,ifile) != NULL)
630 {
631 state=0;
632 next=0;
633
634 token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token));
cristyf67023c2015-03-23 14:28:39 +0000635 if (token == (char *) NULL)
636 break;
cristy3ed852e2009-09-05 21:47:34 +0000637 newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr));
cristyf67023c2015-03-23 14:28:39 +0000638 if (newstr == (char *) NULL)
639 break;
cristy3ed852e2009-09-05 21:47:34 +0000640 while (Tokenizer(token_info,0,token,(size_t) inputlen,line,"","=","\"",0,
641 &brkused,&next,&quoted)==0)
642 {
643 if (state == 0)
644 {
645 int
646 state,
647 next;
648
649 char
650 brkused,
651 quoted;
652
653 state=0;
654 next=0;
655 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","#",
656 "",0,&brkused,&next,&quoted)==0)
657 {
658 switch (state)
659 {
660 case 0:
661 if (strcmp(newstr,"8BIM")==0)
662 dataset = 255;
663 else
cristyf2f27272009-12-17 14:48:46 +0000664 dataset = (unsigned char) StringToLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000665 break;
666 case 1:
cristye27293e2009-12-18 02:53:20 +0000667 recnum=(unsigned int) StringToUnsignedLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000668 break;
669 case 2:
cristy151b66d2015-04-15 10:50:31 +0000670 name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000671 sizeof(*name));
672 if (name)
cristy151b66d2015-04-15 10:50:31 +0000673 (void) CopyMagickString(name,newstr,strlen(newstr)+MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000674 break;
675 }
676 state++;
677 }
678 }
679 else
680 if (state == 1)
681 {
682 int
683 next;
684
cristybb503372010-05-27 20:51:26 +0000685 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000686 len;
687
688 char
689 brkused,
690 quoted;
691
692 next=0;
cristybb503372010-05-27 20:51:26 +0000693 len = (ssize_t) strlen(token);
cristy3ed852e2009-09-05 21:47:34 +0000694 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","&",
695 "",0,&brkused,&next,&quoted)==0)
696 {
697 if (brkused && next > 0)
698 {
699 char
700 *s = &token[next-1];
701
cristybb503372010-05-27 20:51:26 +0000702 len -= (ssize_t) convertHTMLcodes(s,(int) strlen(s));
cristy3ed852e2009-09-05 21:47:34 +0000703 }
704 }
705
706 if (dataset == 255)
707 {
708 unsigned char
709 nlen = 0;
710
711 int
712 i;
713
714 if (savedolen > 0)
715 {
716 MagickOffsetType
717 offset;
718
cristybb503372010-05-27 20:51:26 +0000719 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000720 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000721 if (currentpos < 0)
722 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000723 offset=SeekBlob(ofile,savedpos,SEEK_SET);
724 if (offset < 0)
725 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000726 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000727 offset=SeekBlob(ofile,currentpos,SEEK_SET);
728 if (offset < 0)
729 return(-1);
730 savedolen = 0L;
731 }
732 if (outputlen & 1)
733 {
734 (void) WriteBlobByte(ofile,0x00);
735 outputlen++;
736 }
737 (void) WriteBlobString(ofile,"8BIM");
738 (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
739 outputlen += 6;
740 if (name)
741 nlen = (unsigned char) strlen(name);
742 (void) WriteBlobByte(ofile,(unsigned char) nlen);
743 outputlen++;
744 for (i=0; i<nlen; i++)
745 (void) WriteBlobByte(ofile,(unsigned char) name[i]);
746 outputlen += nlen;
747 if ((nlen & 0x01) == 0)
748 {
749 (void) WriteBlobByte(ofile,0x00);
750 outputlen++;
751 }
752 if (recnum != IPTC_ID)
753 {
cristyf9cca6a2010-06-04 23:49:28 +0000754 (void) WriteBlobMSBLong(ofile,(unsigned int) len);
cristy3ed852e2009-09-05 21:47:34 +0000755 outputlen += 4;
756
757 next=0;
758 outputlen += len;
759 while (len--)
760 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
761
762 if (outputlen & 1)
763 {
764 (void) WriteBlobByte(ofile,0x00);
765 outputlen++;
766 }
767 }
768 else
769 {
770 /* patch in a fake length for now and fix it later */
771 savedpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000772 if (savedpos < 0)
773 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000774 (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU);
cristy3ed852e2009-09-05 21:47:34 +0000775 outputlen += 4;
776 savedolen = outputlen;
777 }
778 }
779 else
780 {
781 if (len <= 0x7FFF)
782 {
783 (void) WriteBlobByte(ofile,0x1c);
784 (void) WriteBlobByte(ofile,dataset);
785 (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
786 (void) WriteBlobMSBShort(ofile,(unsigned short) len);
787 outputlen += 5;
788 next=0;
789 outputlen += len;
790 while (len--)
791 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
792 }
793 }
794 }
795 state++;
796 }
cristyf67023c2015-03-23 14:28:39 +0000797 if (token != (char *) NULL)
798 token=DestroyString(token);
799 if (newstr != (char *) NULL)
800 newstr=DestroyString(newstr);
801 if (name != (char *) NULL)
802 name=DestroyString(name);
cristy3ed852e2009-09-05 21:47:34 +0000803 }
804 token_info=DestroyTokenInfo(token_info);
cristyd4f11052015-03-23 14:35:47 +0000805 if (token != (char *) NULL)
806 token=DestroyString(token);
807 if (newstr != (char *) NULL)
808 newstr=DestroyString(newstr);
809 if (name != (char *) NULL)
810 name=DestroyString(name);
cristy3ed852e2009-09-05 21:47:34 +0000811 line=DestroyString(line);
812 if (savedolen > 0)
813 {
814 MagickOffsetType
815 offset;
816
cristybb503372010-05-27 20:51:26 +0000817 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000818
819 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000820 if (currentpos < 0)
821 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000822 offset=SeekBlob(ofile,savedpos,SEEK_SET);
823 if (offset < 0)
824 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000825 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000826 offset=SeekBlob(ofile,currentpos,SEEK_SET);
827 if (offset < 0)
828 return(-1);
829 savedolen = 0L;
830 }
cristyf67023c2015-03-23 14:28:39 +0000831 return(outputlen);
cristy3ed852e2009-09-05 21:47:34 +0000832}
833
834/* some defines for the different JPEG block types */
835#define M_SOF0 0xC0 /* Start Of Frame N */
836#define M_SOF1 0xC1 /* N indicates which compression process */
837#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */
838#define M_SOF3 0xC3
839#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */
840#define M_SOF6 0xC6
841#define M_SOF7 0xC7
842#define M_SOF9 0xC9
843#define M_SOF10 0xCA
844#define M_SOF11 0xCB
845#define M_SOF13 0xCD
846#define M_SOF14 0xCE
847#define M_SOF15 0xCF
848#define M_SOI 0xD8
849#define M_EOI 0xD9 /* End Of Image (end of datastream) */
850#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
851#define M_APP0 0xe0
852#define M_APP1 0xe1
853#define M_APP2 0xe2
854#define M_APP3 0xe3
855#define M_APP4 0xe4
856#define M_APP5 0xe5
857#define M_APP6 0xe6
858#define M_APP7 0xe7
859#define M_APP8 0xe8
860#define M_APP9 0xe9
861#define M_APP10 0xea
862#define M_APP11 0xeb
863#define M_APP12 0xec
864#define M_APP13 0xed
865#define M_APP14 0xee
866#define M_APP15 0xef
867
868static int jpeg_transfer_1(Image *ifile, Image *ofile)
869{
870 int c;
871
872 c = ReadBlobByte(ifile);
873 if (c == EOF)
874 return EOF;
875 (void) WriteBlobByte(ofile,(unsigned char) c);
876 return c;
877}
878
879#if defined(future)
880static int jpeg_skip_1(Image *ifile)
881{
882 int c;
883
884 c = ReadBlobByte(ifile);
885 if (c == EOF)
886 return EOF;
887 return c;
888}
889#endif
890
891static int jpeg_read_remaining(Image *ifile, Image *ofile)
892{
893 int c;
894
895 while ((c = jpeg_transfer_1(ifile, ofile)) != EOF)
896 continue;
897 return M_EOI;
898}
899
900static int jpeg_skip_variable(Image *ifile, Image *ofile)
901{
902 unsigned int length;
903 int c1,c2;
904
905 if ((c1 = jpeg_transfer_1(ifile, ofile)) == EOF)
906 return M_EOI;
907 if ((c2 = jpeg_transfer_1(ifile, ofile)) == EOF)
908 return M_EOI;
909
910 length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
911 length -= 2;
912
913 while (length--)
914 if (jpeg_transfer_1(ifile, ofile) == EOF)
915 return M_EOI;
916
917 return 0;
918}
919
920static int jpeg_skip_variable2(Image *ifile, Image *ofile)
921{
922 unsigned int length;
923 int c1,c2;
924
925 (void) ofile;
926 if ((c1 = ReadBlobByte(ifile)) == EOF) return M_EOI;
927 if ((c2 = ReadBlobByte(ifile)) == EOF) return M_EOI;
928
929 length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
930 length -= 2;
931
932 while (length--)
933 if (ReadBlobByte(ifile) == EOF)
934 return M_EOI;
935
936 return 0;
937}
938
939static int jpeg_nextmarker(Image *ifile, Image *ofile)
940{
941 int c;
942
943 /* transfer anything until we hit 0xff */
944 do
945 {
946 c = ReadBlobByte(ifile);
947 if (c == EOF)
948 return M_EOI; /* we hit EOF */
949 else
950 if (c != 0xff)
951 (void) WriteBlobByte(ofile,(unsigned char) c);
952 } while (c != 0xff);
953
954 /* get marker byte, swallowing possible padding */
955 do
956 {
957 c = ReadBlobByte(ifile);
958 if (c == EOF)
959 return M_EOI; /* we hit EOF */
960 } while (c == 0xff);
961
962 return c;
963}
964
965#if defined(future)
966static int jpeg_skip_till_marker(Image *ifile, int marker)
967{
968 int c, i;
969
970 do
971 {
972 /* skip anything until we hit 0xff */
973 i = 0;
974 do
975 {
976 c = ReadBlobByte(ifile);
977 i++;
978 if (c == EOF)
979 return M_EOI; /* we hit EOF */
980 } while (c != 0xff);
981
982 /* get marker byte, swallowing possible padding */
983 do
984 {
985 c = ReadBlobByte(ifile);
986 if (c == EOF)
987 return M_EOI; /* we hit EOF */
988 } while (c == 0xff);
989 } while (c != marker);
990 return c;
991}
992#endif
993
cristy3ed852e2009-09-05 21:47:34 +0000994/* Embed binary IPTC data into a JPEG image. */
995static int jpeg_embed(Image *ifile, Image *ofile, Image *iptc)
996{
997 unsigned int marker;
998 unsigned int done = 0;
999 unsigned int len;
1000 int inx;
1001
1002 if (jpeg_transfer_1(ifile, ofile) != 0xFF)
1003 return 0;
1004 if (jpeg_transfer_1(ifile, ofile) != M_SOI)
1005 return 0;
1006
1007 while (done == MagickFalse)
1008 {
1009 marker=(unsigned int) jpeg_nextmarker(ifile, ofile);
1010 if (marker == M_EOI)
1011 { /* EOF */
1012 break;
1013 }
1014 else
1015 {
1016 if (marker != M_APP13)
1017 {
1018 (void) WriteBlobByte(ofile,0xff);
1019 (void) WriteBlobByte(ofile,(unsigned char) marker);
1020 }
1021 }
1022
1023 switch (marker)
1024 {
1025 case M_APP13:
1026 /* we are going to write a new APP13 marker, so don't output the old one */
1027 jpeg_skip_variable2(ifile, ofile);
1028 break;
1029
1030 case M_APP0:
1031 /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
1032 jpeg_skip_variable(ifile, ofile);
1033
cristyf432c632014-12-07 15:11:28 +00001034 if (iptc != (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001035 {
cristy317c03e2013-11-03 22:01:17 +00001036 char
dirk93b02b72013-11-16 16:03:36 +00001037 psheader[] = "\xFF\xED\0\0Photoshop 3.0\0" "8BIM\x04\x04\0\0\0\0";
cristy317c03e2013-11-03 22:01:17 +00001038
cristy3ed852e2009-09-05 21:47:34 +00001039 len=(unsigned int) GetBlobSize(iptc);
1040 if (len & 1)
1041 len++; /* make the length even */
1042 psheader[2]=(char) ((len+16)>>8);
1043 psheader[3]=(char) ((len+16)&0xff);
1044 for (inx = 0; inx < 18; inx++)
1045 (void) WriteBlobByte(ofile,(unsigned char) psheader[inx]);
1046 jpeg_read_remaining(iptc, ofile);
1047 len=(unsigned int) GetBlobSize(iptc);
1048 if (len & 1)
1049 (void) WriteBlobByte(ofile,0);
1050 }
1051 break;
1052
1053 case M_SOS:
1054 /* we hit data, no more marker-inserting can be done! */
1055 jpeg_read_remaining(ifile, ofile);
1056 done = 1;
1057 break;
1058
1059 default:
1060 jpeg_skip_variable(ifile, ofile);
1061 break;
1062 }
1063 }
1064 return 1;
1065}
1066
1067/* handle stripping the APP13 data out of a JPEG */
1068#if defined(future)
1069static void jpeg_strip(Image *ifile, Image *ofile)
1070{
1071 unsigned int marker;
1072
1073 marker = jpeg_skip_till_marker(ifile, M_SOI);
1074 if (marker == M_SOI)
1075 {
1076 (void) WriteBlobByte(ofile,0xff);
1077 (void) WriteBlobByte(ofile,M_SOI);
1078 jpeg_read_remaining(ifile, ofile);
1079 }
1080}
1081
1082/* Extract any APP13 binary data into a file. */
1083static int jpeg_extract(Image *ifile, Image *ofile)
1084{
1085 unsigned int marker;
1086 unsigned int done = 0;
1087
1088 if (jpeg_skip_1(ifile) != 0xff)
1089 return 0;
1090 if (jpeg_skip_1(ifile) != M_SOI)
1091 return 0;
1092
1093 while (done == MagickFalse)
1094 {
1095 marker = jpeg_skip_till_marker(ifile, M_APP13);
1096 if (marker == M_APP13)
1097 {
1098 marker = jpeg_nextmarker(ifile, ofile);
1099 break;
1100 }
1101 }
1102 return 1;
1103}
1104#endif
1105
dirk9f4f3542014-04-21 17:19:03 +00001106static inline void CopyBlob(Image *source,Image *destination)
1107{
1108 ssize_t
1109 i;
1110
1111 unsigned char
1112 *buffer;
1113
1114 ssize_t
1115 count,
1116 length;
1117
1118 buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
1119 sizeof(*buffer));
1120 if (buffer != (unsigned char *) NULL)
1121 {
1122 i=0;
1123 while ((length=ReadBlob(source,MagickMaxBufferExtent,buffer)) != 0)
1124 {
1125 count=0;
1126 for (i=0; i < (ssize_t) length; i+=count)
1127 {
1128 count=WriteBlob(destination,(size_t) (length-i),buffer+i);
1129 if (count <= 0)
1130 break;
1131 }
1132 if (i < (ssize_t) length)
1133 break;
1134 }
1135 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1136 }
1137}
1138
cristy3ed852e2009-09-05 21:47:34 +00001139static Image *ReadMETAImage(const ImageInfo *image_info,
1140 ExceptionInfo *exception)
1141{
1142 Image
1143 *buff,
1144 *image;
1145
cristy3ed852e2009-09-05 21:47:34 +00001146 MagickBooleanType
1147 status;
1148
1149 StringInfo
1150 *profile;
1151
1152 size_t
1153 length;
1154
1155 void
1156 *blob;
1157
1158 /*
1159 Open file containing binary metadata
1160 */
1161 assert(image_info != (const ImageInfo *) NULL);
1162 assert(image_info->signature == MagickSignature);
1163 if (image_info->debug != MagickFalse)
1164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1165 image_info->filename);
1166 assert(exception != (ExceptionInfo *) NULL);
1167 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +00001168 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001169 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1170 if (status == MagickFalse)
1171 {
1172 image=DestroyImageList(image);
1173 return((Image *) NULL);
1174 }
1175 image->columns=1;
1176 image->rows=1;
cristyea1a8aa2011-10-20 13:24:06 +00001177 if (SetImageBackgroundColor(image,exception) == MagickFalse)
cristy95524f92010-02-16 18:44:34 +00001178 {
cristy95524f92010-02-16 18:44:34 +00001179 image=DestroyImageList(image);
1180 return((Image *) NULL);
1181 }
cristy3ed852e2009-09-05 21:47:34 +00001182 length=1;
1183 if (LocaleNCompare(image_info->magick,"8BIM",4) == 0)
1184 {
1185 /*
1186 Read 8BIM binary metadata.
1187 */
cristy9950d572011-10-01 18:22:35 +00001188 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001189 if (buff == (Image *) NULL)
1190 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1191 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1192 if (blob == (unsigned char *) NULL)
1193 {
1194 buff=DestroyImage(buff);
1195 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1196 }
1197 AttachBlob(buff->blob,blob,length);
1198 if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
1199 {
1200 length=(size_t) parse8BIM(image, buff);
1201 if (length & 1)
1202 (void) WriteBlobByte(buff,0x0);
1203 }
1204 else if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
1205 {
1206 length=(size_t) parse8BIMW(image, buff);
1207 if (length & 1)
1208 (void) WriteBlobByte(buff,0x0);
1209 }
1210 else
dirk9f4f3542014-04-21 17:19:03 +00001211 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001212 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1213 GetBlobSize(buff));
1214 if (profile == (StringInfo *) NULL)
1215 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001216 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001217 profile=DestroyStringInfo(profile);
1218 if (status == MagickFalse)
1219 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1220 blob=DetachBlob(buff->blob);
1221 blob=(unsigned char *) RelinquishMagickMemory(blob);
1222 buff=DestroyImage(buff);
1223 }
1224 if (LocaleNCompare(image_info->magick,"APP1",4) == 0)
1225 {
1226 char
cristy151b66d2015-04-15 10:50:31 +00001227 name[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001228
cristy151b66d2015-04-15 10:50:31 +00001229 (void) FormatLocaleString(name,MagickPathExtent,"APP%d",1);
cristy9950d572011-10-01 18:22:35 +00001230 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001231 if (buff == (Image *) NULL)
1232 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1233 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1234 if (blob == (unsigned char *) NULL)
1235 {
1236 buff=DestroyImage(buff);
1237 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1238 }
1239 AttachBlob(buff->blob,blob,length);
1240 if (LocaleCompare(image_info->magick,"APP1JPEG") == 0)
1241 {
1242 Image
1243 *iptc;
1244
1245 int
1246 result;
1247
1248 if (image_info->profile == (void *) NULL)
1249 {
1250 blob=DetachBlob(buff->blob);
1251 blob=RelinquishMagickMemory(blob);
1252 buff=DestroyImage(buff);
1253 ThrowReaderException(CoderError,"NoIPTCProfileAvailable");
1254 }
1255 profile=CloneStringInfo((StringInfo *) image_info->profile);
cristy9950d572011-10-01 18:22:35 +00001256 iptc=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001257 if (iptc == (Image *) NULL)
1258 {
1259 blob=DetachBlob(buff->blob);
1260 blob=RelinquishMagickMemory(blob);
1261 buff=DestroyImage(buff);
1262 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1263 }
1264 AttachBlob(iptc->blob,GetStringInfoDatum(profile),
1265 GetStringInfoLength(profile));
1266 result=jpeg_embed(image,buff,iptc);
1267 blob=DetachBlob(iptc->blob);
1268 blob=RelinquishMagickMemory(blob);
1269 iptc=DestroyImage(iptc);
1270 if (result == 0)
1271 {
1272 blob=DetachBlob(buff->blob);
1273 blob=RelinquishMagickMemory(blob);
1274 buff=DestroyImage(buff);
1275 ThrowReaderException(CoderError,"JPEGEmbeddingFailed");
1276 }
1277 }
1278 else
dirk9f4f3542014-04-21 17:19:03 +00001279 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001280 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1281 GetBlobSize(buff));
1282 if (profile == (StringInfo *) NULL)
1283 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001284 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001285 profile=DestroyStringInfo(profile);
1286 if (status == MagickFalse)
1287 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1288 blob=DetachBlob(buff->blob);
1289 blob=RelinquishMagickMemory(blob);
1290 buff=DestroyImage(buff);
1291 }
1292 if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
1293 (LocaleCompare(image_info->magick,"ICM") == 0))
1294 {
cristy9950d572011-10-01 18:22:35 +00001295 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001296 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);
dirk9f4f3542014-04-21 17:19:03 +00001305 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001306 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1307 GetBlobSize(buff));
1308 if (profile == (StringInfo *) NULL)
1309 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001310 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001311 profile=DestroyStringInfo(profile);
1312 blob=DetachBlob(buff->blob);
1313 blob=(unsigned char *) RelinquishMagickMemory(blob);
1314 buff=DestroyImage(buff);
1315 }
1316 if (LocaleCompare(image_info->magick,"IPTC") == 0)
1317 {
cristy9950d572011-10-01 18:22:35 +00001318 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001319 if (buff == (Image *) NULL)
1320 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1321 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1322 if (blob == (unsigned char *) NULL)
1323 {
1324 buff=DestroyImage(buff);
1325 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1326 }
1327 AttachBlob(buff->blob,blob,length);
dirk9f4f3542014-04-21 17:19:03 +00001328 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001329 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1330 GetBlobSize(buff));
1331 if (profile == (StringInfo *) NULL)
1332 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001333 (void) SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001334 profile=DestroyStringInfo(profile);
1335 blob=DetachBlob(buff->blob);
1336 blob=(unsigned char *) RelinquishMagickMemory(blob);
1337 buff=DestroyImage(buff);
1338 }
1339 if (LocaleCompare(image_info->magick,"XMP") == 0)
1340 {
cristy9950d572011-10-01 18:22:35 +00001341 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001342 if (buff == (Image *) NULL)
1343 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1344 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1345 if (blob == (unsigned char *) NULL)
1346 {
1347 buff=DestroyImage(buff);
1348 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1349 }
1350 AttachBlob(buff->blob,blob,length);
dirk9f4f3542014-04-21 17:19:03 +00001351 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001352 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1353 GetBlobSize(buff));
1354 if (profile == (StringInfo *) NULL)
1355 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001356 (void) SetImageProfile(image,"xmp",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001357 profile=DestroyStringInfo(profile);
1358 blob=DetachBlob(buff->blob);
1359 blob=(unsigned char *) RelinquishMagickMemory(blob);
1360 buff=DestroyImage(buff);
1361 }
1362 (void) CloseBlob(image);
1363 return(GetFirstImageInList(image));
1364}
1365
1366/*
1367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1368% %
1369% %
1370% %
1371% R e g i s t e r M E T A I m a g e %
1372% %
1373% %
1374% %
1375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1376%
1377% RegisterMETAImage() adds attributes for the META image format to
1378% the list of supported formats. The attributes include the image format
1379% tag, a method to read and/or write the format, whether the format
1380% supports the saving of more than one frame to the same file or blob,
1381% whether the format supports native in-memory I/O, and a brief
1382% description of the format.
1383%
1384% The format of the RegisterMETAImage method is:
1385%
cristybb503372010-05-27 20:51:26 +00001386% size_t RegisterMETAImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001387%
1388*/
cristybb503372010-05-27 20:51:26 +00001389ModuleExport size_t RegisterMETAImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001390{
1391 MagickInfo
1392 *entry;
1393
dirk06b627a2015-04-06 18:59:17 +00001394 entry=AcquireMagickInfo("META","8BIM","Photoshop resource format");
cristy3ed852e2009-09-05 21:47:34 +00001395 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1396 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001397 entry->flags^=CoderAdjoinFlag;
1398 entry->flags|=CoderStealthFlag;
1399 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001400 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001401 entry=AcquireMagickInfo("META","8BIMTEXT","Photoshop resource text format");
cristy3ed852e2009-09-05 21:47:34 +00001402 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1403 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001404 entry->flags^=CoderAdjoinFlag;
1405 entry->flags|=CoderStealthFlag;
1406 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001407 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001408 entry=AcquireMagickInfo("META","8BIMWTEXT",
1409 "Photoshop resource wide text format");
cristy3ed852e2009-09-05 21:47:34 +00001410 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1411 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001412 entry->flags^=CoderAdjoinFlag;
1413 entry->flags|=CoderStealthFlag;
1414 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001415 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001416 entry=AcquireMagickInfo("META","APP1","Raw application information");
cristy3ed852e2009-09-05 21:47:34 +00001417 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1418 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001419 entry->flags^=CoderAdjoinFlag;
1420 entry->flags|=CoderStealthFlag;
1421 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001422 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001423 entry=AcquireMagickInfo("META","APP1JPEG","Raw JPEG binary data");
cristy3ed852e2009-09-05 21:47:34 +00001424 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1425 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001426 entry->flags^=CoderAdjoinFlag;
1427 entry->flags|=CoderStealthFlag;
1428 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001429 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001430 entry=AcquireMagickInfo("META","EXIF","Exif digital camera binary data");
cristy3ed852e2009-09-05 21:47:34 +00001431 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1432 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001433 entry->flags^=CoderAdjoinFlag;
1434 entry->flags|=CoderStealthFlag;
1435 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001436 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001437 entry=AcquireMagickInfo("META","XMP","Adobe XML metadata");
cristy3ed852e2009-09-05 21:47:34 +00001438 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1439 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001440 entry->flags^=CoderAdjoinFlag;
1441 entry->flags|=CoderStealthFlag;
1442 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001443 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001444 entry=AcquireMagickInfo("META","ICM","ICC Color Profile");
cristy3ed852e2009-09-05 21:47:34 +00001445 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1446 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001447 entry->flags^=CoderAdjoinFlag;
1448 entry->flags|=CoderStealthFlag;
1449 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001450 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001451 entry=AcquireMagickInfo("META","ICC","ICC Color Profile");
cristy3ed852e2009-09-05 21:47:34 +00001452 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1453 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001454 entry->flags^=CoderAdjoinFlag;
1455 entry->flags|=CoderStealthFlag;
1456 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001457 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001458 entry=AcquireMagickInfo("META","IPTC","IPTC Newsphoto");
cristy3ed852e2009-09-05 21:47:34 +00001459 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1460 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001461 entry->flags^=CoderAdjoinFlag;
1462 entry->flags|=CoderStealthFlag;
1463 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001464 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001465 entry=AcquireMagickInfo("META","IPTCTEXT","IPTC Newsphoto text format");
cristy3ed852e2009-09-05 21:47:34 +00001466 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1467 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001468 entry->flags^=CoderAdjoinFlag;
1469 entry->flags|=CoderStealthFlag;
1470 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001471 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001472 entry=AcquireMagickInfo("META","IPTCWTEXT","IPTC Newsphoto text format");
cristy3ed852e2009-09-05 21:47:34 +00001473 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1474 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001475 entry->flags^=CoderAdjoinFlag;
1476 entry->flags|=CoderStealthFlag;
1477 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001478 (void) RegisterMagickInfo(entry);
1479 return(MagickImageCoderSignature);
1480}
1481
1482/*
1483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484% %
1485% %
1486% %
1487% U n r e g i s t e r M E T A I m a g e %
1488% %
1489% %
1490% %
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492%
1493% UnregisterMETAImage() removes format registrations made by the
1494% META module from the list of supported formats.
1495%
1496% The format of the UnregisterMETAImage method is:
1497%
1498% UnregisterMETAImage(void)
1499%
1500*/
1501ModuleExport void UnregisterMETAImage(void)
1502{
1503 (void) UnregisterMagickInfo("8BIM");
1504 (void) UnregisterMagickInfo("8BIMTEXT");
1505 (void) UnregisterMagickInfo("8BIMWTEXT");
1506 (void) UnregisterMagickInfo("EXIF");
1507 (void) UnregisterMagickInfo("APP1");
1508 (void) UnregisterMagickInfo("APP1JPEG");
1509 (void) UnregisterMagickInfo("ICCTEXT");
1510 (void) UnregisterMagickInfo("ICM");
1511 (void) UnregisterMagickInfo("ICC");
1512 (void) UnregisterMagickInfo("IPTC");
1513 (void) UnregisterMagickInfo("IPTCTEXT");
1514 (void) UnregisterMagickInfo("IPTCWTEXT");
1515 (void) UnregisterMagickInfo("XMP");
1516}
1517
1518/*
1519%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1520% %
1521% %
1522% %
1523% W r i t e M E T A I m a g e %
1524% %
1525% %
1526% %
1527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1528%
1529% WriteMETAImage() writes a META image to a file.
1530%
1531% The format of the WriteMETAImage method is:
1532%
1533% MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001534% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001535%
1536% Compression code contributed by Kyle Shorter.
1537%
1538% A description of each parameter follows:
1539%
1540% o image_info: Specifies a pointer to an ImageInfo structure.
1541%
1542% o image: A pointer to a Image structure.
1543%
cristy1e178e72011-08-28 19:44:34 +00001544% o exception: return any errors or warnings in this structure.
1545%
cristy3ed852e2009-09-05 21:47:34 +00001546*/
1547
cristy84960752009-09-12 23:56:01 +00001548static size_t GetIPTCStream(unsigned char **info,size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001549{
1550 int
1551 c;
1552
cristybb503372010-05-27 20:51:26 +00001553 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001554 i;
1555
1556 register unsigned char
1557 *p;
1558
1559 size_t
cristy84960752009-09-12 23:56:01 +00001560 extent,
cristy3ed852e2009-09-05 21:47:34 +00001561 info_length;
1562
cristy3ed852e2009-09-05 21:47:34 +00001563 unsigned int
1564 marker;
1565
cristybb503372010-05-27 20:51:26 +00001566 size_t
cristy3ed852e2009-09-05 21:47:34 +00001567 tag_length;
1568
cristy84960752009-09-12 23:56:01 +00001569 p=(*info);
1570 extent=length;
1571 if ((*p == 0x1c) && (*(p+1) == 0x02))
1572 return(length);
1573 /*
1574 Extract IPTC from 8BIM resource block.
1575 */
1576 while (extent >= 12)
1577 {
1578 if (strncmp((const char *) p,"8BIM",4))
1579 break;
1580 p+=4;
1581 extent-=4;
1582 marker=(unsigned int) (*p) << 8 | *(p+1);
1583 p+=2;
1584 extent-=2;
1585 c=*p++;
1586 extent--;
1587 c|=0x01;
1588 if ((size_t) c >= extent)
1589 break;
1590 p+=c;
1591 extent-=c;
1592 if (extent < 4)
1593 break;
cristybb503372010-05-27 20:51:26 +00001594 tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) |
1595 (((size_t) *(p+2)) << 8) | ((size_t) *(p+3));
cristy84960752009-09-12 23:56:01 +00001596 p+=4;
1597 extent-=4;
1598 if (tag_length > extent)
1599 break;
1600 if (marker == IPTC_ID)
1601 {
cristye1220a42009-09-25 14:37:03 +00001602 *info=p;
1603 return(tag_length);
cristy84960752009-09-12 23:56:01 +00001604 }
cristye1220a42009-09-25 14:37:03 +00001605 if ((tag_length & 0x01) != 0)
cristydb1856a2009-09-25 03:21:42 +00001606 tag_length++;
cristy84960752009-09-12 23:56:01 +00001607 p+=tag_length;
1608 extent-=tag_length;
1609 }
cristy3ed852e2009-09-05 21:47:34 +00001610 /*
1611 Find the beginning of the IPTC info.
1612 */
1613 p=(*info);
1614 tag_length=0;
1615iptc_find:
1616 info_length=0;
1617 marker=MagickFalse;
1618 while (length != 0)
1619 {
1620 c=(*p++);
1621 length--;
1622 if (length == 0)
1623 break;
1624 if (c == 0x1c)
1625 {
1626 p--;
1627 *info=p; /* let the caller know were it is */
1628 break;
1629 }
1630 }
1631 /*
1632 Determine the length of the IPTC info.
1633 */
1634 while (length != 0)
1635 {
1636 c=(*p++);
1637 length--;
1638 if (length == 0)
1639 break;
1640 if (c == 0x1c)
1641 marker=MagickTrue;
1642 else
1643 if (marker)
1644 break;
1645 else
1646 continue;
1647 info_length++;
1648 /*
1649 Found the 0x1c tag; skip the dataset and record number tags.
1650 */
1651 c=(*p++); /* should be 2 */
1652 length--;
1653 if (length == 0)
1654 break;
1655 if ((info_length == 1) && (c != 2))
1656 goto iptc_find;
1657 info_length++;
1658 c=(*p++); /* should be 0 */
1659 length--;
1660 if (length == 0)
1661 break;
1662 if ((info_length == 2) && (c != 0))
1663 goto iptc_find;
1664 info_length++;
1665 /*
cristybb503372010-05-27 20:51:26 +00001666 Decode the length of the block that follows - ssize_t or short format.
cristy3ed852e2009-09-05 21:47:34 +00001667 */
1668 c=(*p++);
1669 length--;
1670 if (length == 0)
1671 break;
1672 info_length++;
1673 if ((c & 0x80) != 0)
1674 {
cristyc7178b42011-10-30 20:33:29 +00001675 /*
1676 Long format.
1677 */
1678 tag_length=0;
cristy3ed852e2009-09-05 21:47:34 +00001679 for (i=0; i < 4; i++)
1680 {
cristyc7178b42011-10-30 20:33:29 +00001681 tag_length<<=8;
1682 tag_length|=(*p++);
cristy3ed852e2009-09-05 21:47:34 +00001683 length--;
1684 if (length == 0)
1685 break;
1686 info_length++;
1687 }
cristy3ed852e2009-09-05 21:47:34 +00001688 }
1689 else
1690 {
cristyc7178b42011-10-30 20:33:29 +00001691 /*
1692 Short format.
1693 */
1694 tag_length=((long) c) << 8;
cristy3ed852e2009-09-05 21:47:34 +00001695 c=(*p++);
1696 length--;
1697 if (length == 0)
1698 break;
1699 info_length++;
cristyc7178b42011-10-30 20:33:29 +00001700 tag_length|=(long) c;
cristy3ed852e2009-09-05 21:47:34 +00001701 }
1702 if (tag_length > (length+1))
1703 break;
1704 p+=tag_length;
1705 length-=tag_length;
1706 if (length == 0)
1707 break;
1708 info_length+=tag_length;
1709 }
1710 return(info_length);
1711}
1712
1713static void formatString(Image *ofile, const char *s, int len)
1714{
1715 char
cristy151b66d2015-04-15 10:50:31 +00001716 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001717
1718 (void) WriteBlobByte(ofile,'"');
1719 for (; len > 0; len--, s++) {
1720 int c = (*s) & 255;
1721 switch (c) {
1722 case '&':
1723 (void) WriteBlobString(ofile,"&amp;");
1724 break;
1725#ifdef HANDLE_GT_LT
1726 case '<':
1727 (void) WriteBlobString(ofile,"&lt;");
1728 break;
1729 case '>':
1730 (void) WriteBlobString(ofile,"&gt;");
1731 break;
1732#endif
1733 case '"':
1734 (void) WriteBlobString(ofile,"&quot;");
1735 break;
1736 default:
1737 if (isprint(c))
1738 (void) WriteBlobByte(ofile,(unsigned char) *s);
1739 else
1740 {
cristy151b66d2015-04-15 10:50:31 +00001741 (void) FormatLocaleString(temp,MagickPathExtent,"&#%d;", c & 255);
cristy3ed852e2009-09-05 21:47:34 +00001742 (void) WriteBlobString(ofile,temp);
1743 }
1744 break;
1745 }
1746 }
cristy0157aea2010-04-24 21:12:18 +00001747#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001748 (void) WriteBlobString(ofile,"\"\r\n");
1749#else
1750#if defined(macintosh)
1751 (void) WriteBlobString(ofile,"\"\r");
1752#else
1753 (void) WriteBlobString(ofile,"\"\n");
1754#endif
1755#endif
1756}
1757
1758typedef struct _tag_spec
1759{
1760 short
1761 id;
1762
1763 const char
1764 *name;
1765} tag_spec;
1766
1767static const tag_spec tags[] = {
1768 { 5, "Image Name" },
1769 { 7, "Edit Status" },
1770 { 10, "Priority" },
1771 { 15, "Category" },
1772 { 20, "Supplemental Category" },
1773 { 22, "Fixture Identifier" },
1774 { 25, "Keyword" },
1775 { 30, "Release Date" },
1776 { 35, "Release Time" },
1777 { 40, "Special Instructions" },
1778 { 45, "Reference Service" },
1779 { 47, "Reference Date" },
1780 { 50, "Reference Number" },
1781 { 55, "Created Date" },
1782 { 60, "Created Time" },
1783 { 65, "Originating Program" },
1784 { 70, "Program Version" },
1785 { 75, "Object Cycle" },
1786 { 80, "Byline" },
1787 { 85, "Byline Title" },
1788 { 90, "City" },
1789 { 95, "Province State" },
1790 { 100, "Country Code" },
1791 { 101, "Country" },
1792 { 103, "Original Transmission Reference" },
1793 { 105, "Headline" },
1794 { 110, "Credit" },
1795 { 115, "Source" },
1796 { 116, "Copyright String" },
1797 { 120, "Caption" },
1798 { 121, "Image Orientation" },
1799 { 122, "Caption Writer" },
1800 { 131, "Local Caption" },
1801 { 200, "Custom Field 1" },
1802 { 201, "Custom Field 2" },
1803 { 202, "Custom Field 3" },
1804 { 203, "Custom Field 4" },
1805 { 204, "Custom Field 5" },
1806 { 205, "Custom Field 6" },
1807 { 206, "Custom Field 7" },
1808 { 207, "Custom Field 8" },
1809 { 208, "Custom Field 9" },
1810 { 209, "Custom Field 10" },
1811 { 210, "Custom Field 11" },
1812 { 211, "Custom Field 12" },
1813 { 212, "Custom Field 13" },
1814 { 213, "Custom Field 14" },
1815 { 214, "Custom Field 15" },
1816 { 215, "Custom Field 16" },
1817 { 216, "Custom Field 17" },
1818 { 217, "Custom Field 18" },
1819 { 218, "Custom Field 19" },
1820 { 219, "Custom Field 20" }
1821};
1822
1823static int formatIPTC(Image *ifile, Image *ofile)
1824{
1825 char
cristy151b66d2015-04-15 10:50:31 +00001826 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001827
1828 unsigned int
1829 foundiptc,
1830 tagsfound;
1831
1832 unsigned char
1833 recnum,
1834 dataset;
1835
1836 unsigned char
1837 *readable,
1838 *str;
1839
cristybb503372010-05-27 20:51:26 +00001840 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001841 tagindx,
1842 taglen;
1843
1844 int
1845 i,
1846 tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
1847
1848 int
1849 c;
1850
1851 foundiptc = 0; /* found the IPTC-Header */
1852 tagsfound = 0; /* number of tags found */
1853
1854 c = ReadBlobByte(ifile);
1855 while (c != EOF)
1856 {
1857 if (c == 0x1c)
1858 foundiptc = 1;
1859 else
1860 {
1861 if (foundiptc)
cristy326182d2014-05-18 21:48:30 +00001862 return(-1);
cristy3ed852e2009-09-05 21:47:34 +00001863 else
cristy326182d2014-05-18 21:48:30 +00001864 {
1865 c=0;
1866 continue;
1867 }
cristy3ed852e2009-09-05 21:47:34 +00001868 }
1869
1870 /* we found the 0x1c tag and now grab the dataset and record number tags */
1871 c = ReadBlobByte(ifile);
1872 if (c == EOF) return -1;
1873 dataset = (unsigned char) c;
1874 c = ReadBlobByte(ifile);
1875 if (c == EOF) return -1;
1876 recnum = (unsigned char) c;
1877 /* try to match this record to one of the ones in our named table */
1878 for (i=0; i< tagcount; i++)
1879 {
1880 if (tags[i].id == (short) recnum)
1881 break;
1882 }
1883 if (i < tagcount)
1884 readable = (unsigned char *) tags[i].name;
1885 else
1886 readable = (unsigned char *) "";
1887 /*
cristybb503372010-05-27 20:51:26 +00001888 We decode the length of the block that follows - ssize_t or short fmt.
cristy3ed852e2009-09-05 21:47:34 +00001889 */
1890 c=ReadBlobByte(ifile);
1891 if (c == EOF) return -1;
1892 if (c & (unsigned char) 0x80)
1893 return 0;
1894 else
1895 {
1896 int
1897 c0;
1898
1899 c0=ReadBlobByte(ifile);
1900 if (c0 == EOF) return -1;
1901 taglen = (c << 8) | c0;
1902 }
1903 if (taglen < 0) return -1;
1904 /* make a buffer to hold the tag datand snag it from the input stream */
cristy151b66d2015-04-15 10:50:31 +00001905 str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent),
cristy3ed852e2009-09-05 21:47:34 +00001906 sizeof(*str));
1907 if (str == (unsigned char *) NULL)
1908 {
1909 printf("MemoryAllocationFailed");
1910 return 0;
1911 }
1912 for (tagindx=0; tagindx<taglen; tagindx++)
1913 {
1914 c=ReadBlobByte(ifile);
1915 if (c == EOF) return -1;
1916 str[tagindx] = (unsigned char) c;
1917 }
1918 str[taglen] = 0;
1919
1920 /* now finish up by formatting this binary data into ASCII equivalent */
1921 if (strlen((char *)readable) > 0)
cristy151b66d2015-04-15 10:50:31 +00001922 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
cristy3ed852e2009-09-05 21:47:34 +00001923 (unsigned int) dataset, (unsigned int) recnum, readable);
1924 else
cristy151b66d2015-04-15 10:50:31 +00001925 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
cristy3ed852e2009-09-05 21:47:34 +00001926 (unsigned int) dataset,(unsigned int) recnum);
1927 (void) WriteBlobString(ofile,temp);
1928 formatString( ofile, (char *)str, taglen );
1929 str=(unsigned char *) RelinquishMagickMemory(str);
1930
1931 tagsfound++;
1932
1933 c=ReadBlobByte(ifile);
1934 }
1935 return((int) tagsfound);
1936}
1937
cristybb503372010-05-27 20:51:26 +00001938static int readWordFromBuffer(char **s, ssize_t *len)
cristy3ed852e2009-09-05 21:47:34 +00001939{
1940 unsigned char
1941 buffer[2];
1942
1943 int
1944 i,
1945 c;
1946
1947 for (i=0; i<2; i++)
1948 {
1949 c = *(*s)++; (*len)--;
1950 if (*len < 0) return -1;
1951 buffer[i] = (unsigned char) c;
1952 }
1953 return (((int) buffer[ 0 ]) << 8) |
1954 (((int) buffer[ 1 ]));
1955}
1956
cristybb503372010-05-27 20:51:26 +00001957static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len)
cristy3ed852e2009-09-05 21:47:34 +00001958{
1959 char
cristy151b66d2015-04-15 10:50:31 +00001960 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001961
1962 unsigned int
1963 foundiptc,
1964 tagsfound;
1965
1966 unsigned char
1967 recnum,
1968 dataset;
1969
1970 unsigned char
1971 *readable,
1972 *str;
1973
cristybb503372010-05-27 20:51:26 +00001974 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001975 tagindx,
1976 taglen;
1977
1978 int
1979 i,
1980 tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
1981
1982 int
1983 c;
1984
1985 foundiptc = 0; /* found the IPTC-Header */
1986 tagsfound = 0; /* number of tags found */
1987
1988 while (len > 0)
1989 {
1990 c = *s++; len--;
1991 if (c == 0x1c)
1992 foundiptc = 1;
1993 else
1994 {
1995 if (foundiptc)
1996 return -1;
1997 else
1998 continue;
1999 }
2000 /*
2001 We found the 0x1c tag and now grab the dataset and record number tags.
2002 */
2003 c = *s++; len--;
2004 if (len < 0) return -1;
2005 dataset = (unsigned char) c;
2006 c = *s++; len--;
2007 if (len < 0) return -1;
2008 recnum = (unsigned char) c;
2009 /* try to match this record to one of the ones in our named table */
2010 for (i=0; i< tagcount; i++)
2011 if (tags[i].id == (short) recnum)
2012 break;
2013 if (i < tagcount)
2014 readable=(unsigned char *) tags[i].name;
2015 else
2016 readable=(unsigned char *) "";
2017 /*
cristybb503372010-05-27 20:51:26 +00002018 We decode the length of the block that follows - ssize_t or short fmt.
cristy3ed852e2009-09-05 21:47:34 +00002019 */
2020 c=(*s++);
2021 len--;
2022 if (len < 0)
2023 return(-1);
2024 if (c & (unsigned char) 0x80)
2025 return(0);
2026 else
2027 {
2028 s--;
2029 len++;
2030 taglen=readWordFromBuffer(&s, &len);
2031 }
2032 if (taglen < 0)
2033 return(-1);
cristy2867b872014-05-17 11:53:50 +00002034 if (taglen > 65535)
2035 return(-1);
cristy3ed852e2009-09-05 21:47:34 +00002036 /* make a buffer to hold the tag datand snag it from the input stream */
cristy151b66d2015-04-15 10:50:31 +00002037 str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent),
cristy3ed852e2009-09-05 21:47:34 +00002038 sizeof(*str));
2039 if (str == (unsigned char *) NULL)
2040 {
2041 printf("MemoryAllocationFailed");
2042 return 0;
2043 }
2044 for (tagindx=0; tagindx<taglen; tagindx++)
2045 {
2046 c = *s++; len--;
2047 if (len < 0)
2048 return(-1);
2049 str[tagindx]=(unsigned char) c;
2050 }
2051 str[taglen]=0;
2052
2053 /* now finish up by formatting this binary data into ASCII equivalent */
2054 if (strlen((char *)readable) > 0)
cristy151b66d2015-04-15 10:50:31 +00002055 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
cristy3ed852e2009-09-05 21:47:34 +00002056 (unsigned int) dataset,(unsigned int) recnum, readable);
2057 else
cristy151b66d2015-04-15 10:50:31 +00002058 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
cristy3ed852e2009-09-05 21:47:34 +00002059 (unsigned int) dataset,(unsigned int) recnum);
2060 (void) WriteBlobString(ofile,temp);
2061 formatString( ofile, (char *)str, taglen );
2062 str=(unsigned char *) RelinquishMagickMemory(str);
2063
2064 tagsfound++;
2065 }
2066 return ((int) tagsfound);
2067}
2068
2069static int format8BIM(Image *ifile, Image *ofile)
2070{
2071 char
cristy151b66d2015-04-15 10:50:31 +00002072 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002073
2074 unsigned int
2075 foundOSType;
2076
2077 int
2078 ID,
2079 resCount,
2080 i,
2081 c;
2082
2083 ssize_t
2084 count;
2085
2086 unsigned char
2087 *PString,
2088 *str;
2089
2090 resCount=0;
2091 foundOSType=0; /* found the OSType */
cristyda16f162011-02-19 23:52:17 +00002092 (void) foundOSType;
cristy3ed852e2009-09-05 21:47:34 +00002093 c=ReadBlobByte(ifile);
2094 while (c != EOF)
2095 {
2096 if (c == '8')
2097 {
2098 unsigned char
2099 buffer[5];
2100
2101 buffer[0]=(unsigned char) c;
2102 for (i=1; i<4; i++)
2103 {
2104 c=ReadBlobByte(ifile);
2105 if (c == EOF)
2106 return(-1);
2107 buffer[i] = (unsigned char) c;
2108 }
2109 buffer[4]=0;
2110 if (strcmp((const char *)buffer, "8BIM") == 0)
2111 foundOSType=1;
2112 else
2113 continue;
2114 }
2115 else
2116 {
2117 c=ReadBlobByte(ifile);
2118 continue;
2119 }
2120 /*
2121 We found the OSType (8BIM) and now grab the ID, PString, and Size fields.
2122 */
2123 ID=(int) ReadBlobMSBShort(ifile);
2124 if (ID < 0)
2125 return(-1);
2126 {
2127 unsigned char
2128 plen;
2129
2130 c=ReadBlobByte(ifile);
2131 if (c == EOF)
2132 return(-1);
2133 plen = (unsigned char) c;
2134 PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+
cristy151b66d2015-04-15 10:50:31 +00002135 MagickPathExtent),sizeof(*PString));
cristy3ed852e2009-09-05 21:47:34 +00002136 if (PString == (unsigned char *) NULL)
2137 {
2138 printf("MemoryAllocationFailed");
2139 return 0;
2140 }
2141 for (i=0; i<plen; i++)
2142 {
2143 c=ReadBlobByte(ifile);
2144 if (c == EOF) return -1;
2145 PString[i] = (unsigned char) c;
2146 }
2147 PString[ plen ] = 0;
2148 if ((plen & 0x01) == 0)
2149 {
2150 c=ReadBlobByte(ifile);
2151 if (c == EOF)
2152 return(-1);
2153 }
2154 }
cristy6cff05d2010-09-02 11:22:46 +00002155 count = (int) ReadBlobMSBLong(ifile);
cristy3ed852e2009-09-05 21:47:34 +00002156 if (count < 0) return -1;
2157 /* make a buffer to hold the datand snag it from the input stream */
2158 str=(unsigned char *) AcquireQuantumMemory((size_t) count,sizeof(*str));
2159 if (str == (unsigned char *) NULL)
2160 {
2161 printf("MemoryAllocationFailed");
2162 return 0;
2163 }
cristybb503372010-05-27 20:51:26 +00002164 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +00002165 {
2166 c=ReadBlobByte(ifile);
2167 if (c == EOF)
2168 return(-1);
2169 str[i]=(unsigned char) c;
2170 }
2171
2172 /* we currently skip thumbnails, since it does not make
2173 * any sense preserving them in a real world application
2174 */
2175 if (ID != THUMBNAIL_ID)
2176 {
2177 /* now finish up by formatting this binary data into
2178 * ASCII equivalent
2179 */
2180 if (strlen((const char *)PString) > 0)
cristy151b66d2015-04-15 10:50:31 +00002181 (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d#%s=",ID,
cristy3ed852e2009-09-05 21:47:34 +00002182 PString);
2183 else
cristy151b66d2015-04-15 10:50:31 +00002184 (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d=",ID);
cristy3ed852e2009-09-05 21:47:34 +00002185 (void) WriteBlobString(ofile,temp);
2186 if (ID == IPTC_ID)
2187 {
2188 formatString(ofile, "IPTC", 4);
cristybb503372010-05-27 20:51:26 +00002189 formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count);
cristy3ed852e2009-09-05 21:47:34 +00002190 }
2191 else
cristybb503372010-05-27 20:51:26 +00002192 formatString(ofile, (char *)str, (ssize_t) count);
cristy3ed852e2009-09-05 21:47:34 +00002193 }
2194 str=(unsigned char *) RelinquishMagickMemory(str);
2195 PString=(unsigned char *) RelinquishMagickMemory(PString);
2196 resCount++;
2197 c=ReadBlobByte(ifile);
2198 }
2199 return resCount;
2200}
2201
2202static MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002203 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002204{
2205 const StringInfo
2206 *profile;
2207
2208 MagickBooleanType
2209 status;
2210
2211 size_t
2212 length;
2213
2214 /*
2215 Open image file.
2216 */
2217 assert(image_info != (const ImageInfo *) NULL);
2218 assert(image_info->signature == MagickSignature);
2219 assert(image != (Image *) NULL);
2220 assert(image->signature == MagickSignature);
2221 if (image->debug != MagickFalse)
2222 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2223 length=0;
2224 if (LocaleCompare(image_info->magick,"8BIM") == 0)
2225 {
2226 /*
2227 Write 8BIM image.
2228 */
2229 profile=GetImageProfile(image,"8bim");
2230 if (profile == (StringInfo *) NULL)
2231 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002232 assert(exception != (ExceptionInfo *) NULL);
2233 assert(exception->signature == MagickSignature);
2234 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002235 if (status == MagickFalse)
2236 return(status);
2237 (void) WriteBlob(image,GetStringInfoLength(profile),
2238 GetStringInfoDatum(profile));
2239 (void) CloseBlob(image);
2240 return(MagickTrue);
2241 }
2242 if (LocaleCompare(image_info->magick,"iptc") == 0)
2243 {
2244 size_t
2245 length;
2246
2247 unsigned char
2248 *info;
2249
cristy0ba01102010-11-06 18:09:58 +00002250 profile=GetImageProfile(image,"iptc");
2251 if (profile == (StringInfo *) NULL)
2252 profile=GetImageProfile(image,"8bim");
cristy3ed852e2009-09-05 21:47:34 +00002253 if (profile == (StringInfo *) NULL)
2254 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002255 assert(exception != (ExceptionInfo *) NULL);
2256 assert(exception->signature == MagickSignature);
2257 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002258 info=GetStringInfoDatum(profile);
2259 length=GetStringInfoLength(profile);
cristy84960752009-09-12 23:56:01 +00002260 length=GetIPTCStream(&info,length);
cristy3ed852e2009-09-05 21:47:34 +00002261 if (length == 0)
2262 ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2263 (void) WriteBlob(image,length,info);
2264 (void) CloseBlob(image);
2265 return(MagickTrue);
2266 }
2267 if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
2268 {
2269 Image
2270 *buff;
2271
2272 profile=GetImageProfile(image,"8bim");
2273 if (profile == (StringInfo *) NULL)
2274 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002275 assert(exception != (ExceptionInfo *) NULL);
2276 assert(exception->signature == MagickSignature);
2277 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002278 if (status == MagickFalse)
2279 return(status);
cristy9950d572011-10-01 18:22:35 +00002280 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002281 if (buff == (Image *) NULL)
2282 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2283 AttachBlob(buff->blob,GetStringInfoDatum(profile),
2284 GetStringInfoLength(profile));
2285 format8BIM(buff,image);
2286 (void) DetachBlob(buff->blob);
2287 buff=DestroyImage(buff);
2288 (void) CloseBlob(image);
2289 return(MagickTrue);
2290 }
2291 if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
2292 return(MagickFalse);
2293 if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
2294 {
2295 Image
2296 *buff;
2297
2298 unsigned char
2299 *info;
2300
2301 profile=GetImageProfile(image,"8bim");
2302 if (profile == (StringInfo *) NULL)
2303 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2304 info=GetStringInfoDatum(profile);
2305 length=GetStringInfoLength(profile);
cristy84960752009-09-12 23:56:01 +00002306 length=GetIPTCStream(&info,length);
cristy3ed852e2009-09-05 21:47:34 +00002307 if (length == 0)
2308 ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002309 assert(exception != (ExceptionInfo *) NULL);
2310 assert(exception->signature == MagickSignature);
2311 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002312 if (status == MagickFalse)
2313 return(status);
cristy9950d572011-10-01 18:22:35 +00002314 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002315 if (buff == (Image *) NULL)
2316 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2317 AttachBlob(buff->blob,info,length);
2318 formatIPTC(buff,image);
2319 (void) DetachBlob(buff->blob);
2320 buff=DestroyImage(buff);
2321 (void) CloseBlob(image);
2322 return(MagickTrue);
2323 }
2324 if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
2325 return(MagickFalse);
2326 if ((LocaleCompare(image_info->magick,"APP1") == 0) ||
2327 (LocaleCompare(image_info->magick,"EXIF") == 0) ||
2328 (LocaleCompare(image_info->magick,"XMP") == 0))
2329 {
2330 /*
2331 (void) Write APP1 image.
2332 */
2333 profile=GetImageProfile(image,image_info->magick);
2334 if (profile == (StringInfo *) NULL)
2335 ThrowWriterException(CoderError,"NoAPP1DataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002336 assert(exception != (ExceptionInfo *) NULL);
2337 assert(exception->signature == MagickSignature);
2338 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002339 if (status == MagickFalse)
2340 return(status);
2341 (void) WriteBlob(image,GetStringInfoLength(profile),
2342 GetStringInfoDatum(profile));
2343 (void) CloseBlob(image);
2344 return(MagickTrue);
2345 }
2346 if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
2347 (LocaleCompare(image_info->magick,"ICM") == 0))
2348 {
2349 /*
2350 Write ICM image.
2351 */
2352 profile=GetImageProfile(image,"icc");
2353 if (profile == (StringInfo *) NULL)
2354 ThrowWriterException(CoderError,"NoColorProfileIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002355 assert(exception != (ExceptionInfo *) NULL);
2356 assert(exception->signature == MagickSignature);
2357 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002358 if (status == MagickFalse)
2359 return(status);
2360 (void) WriteBlob(image,GetStringInfoLength(profile),
2361 GetStringInfoDatum(profile));
2362 (void) CloseBlob(image);
2363 return(MagickTrue);
2364 }
2365 return(MagickFalse);
2366}