blob: 96ad6c8f19d53cd89ea8bec5c4c481d78ae72efc [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% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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{
Cristy629f8052015-09-14 07:48:08 -0400146 const short
cristy3ed852e2009-09-05 21:47:34 +0000147 len;
Cristy629f8052015-09-14 07:48:08 -0400148
cristy3ed852e2009-09-05 21:47:34 +0000149 const char
150 *code,
151 val;
152} html_code;
153
Cristy629f8052015-09-14 07:48:08 -0400154static const html_code html_codes[] = {
cristy3ed852e2009-09-05 21:47:34 +0000155#ifdef HANDLE_GT_LT
156 { 4,"&lt;",'<' },
157 { 4,"&gt;",'>' },
158#endif
159 { 5,"&amp;",'&' },
160 { 6,"&quot;",'"' },
161 { 6,"&apos;",'\''}
162};
163
164static int stringnicmp(const char *p,const char *q,size_t n)
165{
cristybb503372010-05-27 20:51:26 +0000166 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000167 i,
168 j;
169
170 if (p == q)
171 return(0);
172 if (p == (char *) NULL)
173 return(-1);
174 if (q == (char *) NULL)
175 return(1);
176 while ((*p != '\0') && (*q != '\0'))
177 {
178 if ((*p == '\0') || (*q == '\0'))
179 break;
180 i=(*p);
181 if (islower(i))
182 i=toupper(i);
183 j=(*q);
184 if (islower(j))
185 j=toupper(j);
186 if (i != j)
187 break;
188 n--;
189 if (n == 0)
190 break;
191 p++;
192 q++;
193 }
194 return(toupper((int) *p)-toupper((int) *q));
195}
196
197static int convertHTMLcodes(char *s, int len)
198{
cristyf432c632014-12-07 15:11:28 +0000199 if (len <=0 || s==(char*) NULL || *s=='\0')
cristy3ed852e2009-09-05 21:47:34 +0000200 return 0;
201
202 if (s[1] == '#')
203 {
204 int val, o;
205
206 if (sscanf(s,"&#%d;",&val) == 1)
207 {
208 o = 3;
209 while (s[o] != ';')
210 {
211 o++;
212 if (o > 5)
213 break;
214 }
215 if (o < 6)
cristy29d40332014-12-30 23:48:22 +0000216 (void) memmove(s+1,s+1+o,strlen(s+1+o)+1);
cristy3ed852e2009-09-05 21:47:34 +0000217 *s = val;
218 return o;
219 }
220 }
221 else
222 {
223 int
224 i,
225 codes = (int) (sizeof(html_codes) / sizeof(html_code));
226
227 for (i=0; i < codes; i++)
228 {
229 if (html_codes[i].len <= len)
230 if (stringnicmp(s,html_codes[i].code,(size_t) html_codes[i].len) == 0)
231 {
cristy29d40332014-12-30 23:48:22 +0000232 (void) memmove(s+1,s+html_codes[i].len,
233 strlen(s+html_codes[i].len)+1);
234 *s=html_codes[i].val;
cristy3ed852e2009-09-05 21:47:34 +0000235 return html_codes[i].len-1;
236 }
237 }
238 }
239 return 0;
240}
241
242static char *super_fgets(char **b, int *blen, Image *file)
243{
244 int
245 c,
246 len;
247
248 unsigned char
249 *p,
250 *q;
251
252 len=*blen;
253 p=(unsigned char *) (*b);
254 for (q=p; ; q++)
255 {
256 c=ReadBlobByte(file);
257 if (c == EOF || c == '\n')
258 break;
259 if ((q-p+1) >= (int) len)
260 {
261 int
262 tlen;
263
264 tlen=q-p;
265 len<<=1;
266 p=(unsigned char *) ResizeQuantumMemory(p,(size_t) len+2UL,sizeof(*p));
267 *b=(char *) p;
268 if (p == (unsigned char *) NULL)
269 break;
270 q=p+tlen;
271 }
272 *q=(unsigned char) c;
273 }
274 *blen=0;
275 if (p != (unsigned char *) NULL)
276 {
277 int
278 tlen;
279
280 tlen=q-p;
281 if (tlen == 0)
282 return (char *) NULL;
283 p[tlen] = '\0';
284 *blen=++tlen;
285 }
286 return((char *) p);
287}
288
cristy3ed852e2009-09-05 21:47:34 +0000289#define IPTC_ID 1028
290#define THUMBNAIL_ID 1033
291
cristybb503372010-05-27 20:51:26 +0000292static ssize_t parse8BIM(Image *ifile, Image *ofile)
cristy3ed852e2009-09-05 21:47:34 +0000293{
294 char
295 brkused,
296 quoted,
297 *line,
298 *token,
299 *newstr,
300 *name;
301
302 int
303 state,
304 next;
305
306 unsigned char
307 dataset;
308
309 unsigned int
310 recnum;
311
312 int
cristy151b66d2015-04-15 10:50:31 +0000313 inputlen = MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000314
cristy3ed852e2009-09-05 21:47:34 +0000315 MagickOffsetType
316 savedpos,
317 currentpos;
318
cristy524222d2011-04-25 00:37:06 +0000319 ssize_t
320 savedolen = 0L,
321 outputlen = 0L;
322
cristy3ed852e2009-09-05 21:47:34 +0000323 TokenInfo
324 *token_info;
325
326 dataset = 0;
327 recnum = 0;
328 line = (char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line));
cristyf67023c2015-03-23 14:28:39 +0000329 if (line == (char *) NULL)
cristyd4f11052015-03-23 14:35:47 +0000330 return(-1);
331 newstr = name = token = (char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000332 savedpos = 0;
333 token_info=AcquireTokenInfo();
334 while (super_fgets(&line,&inputlen,ifile)!=NULL)
335 {
336 state=0;
337 next=0;
338
339 token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token));
cristyf67023c2015-03-23 14:28:39 +0000340 if (token == (char *) NULL)
341 break;
cristyd4f11052015-03-23 14:35:47 +0000342 newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr));
cristyf67023c2015-03-23 14:28:39 +0000343 if (newstr == (char *) NULL)
344 break;
cristy3ed852e2009-09-05 21:47:34 +0000345 while (Tokenizer(token_info,0,token,(size_t) inputlen,line,"","=","\"",0,
346 &brkused,&next,&quoted)==0)
347 {
348 if (state == 0)
349 {
350 int
351 state,
352 next;
353
354 char
355 brkused,
356 quoted;
357
358 state=0;
359 next=0;
360 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","#",
361 "", 0,&brkused,&next,&quoted)==0)
362 {
363 switch (state)
364 {
365 case 0:
366 if (strcmp(newstr,"8BIM")==0)
367 dataset = 255;
368 else
cristyf2f27272009-12-17 14:48:46 +0000369 dataset = (unsigned char) StringToLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000370 break;
371 case 1:
cristye27293e2009-12-18 02:53:20 +0000372 recnum = (unsigned int) StringToUnsignedLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000373 break;
374 case 2:
cristy151b66d2015-04-15 10:50:31 +0000375 name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000376 sizeof(*name));
377 if (name)
378 (void) strcpy(name,newstr);
379 break;
380 }
381 state++;
382 }
383 }
384 else
385 if (state == 1)
386 {
387 int
388 next;
389
cristybb503372010-05-27 20:51:26 +0000390 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000391 len;
392
393 char
394 brkused,
395 quoted;
396
397 next=0;
cristybb503372010-05-27 20:51:26 +0000398 len = (ssize_t) strlen(token);
cristy3ed852e2009-09-05 21:47:34 +0000399 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","&",
400 "",0,&brkused,&next,&quoted)==0)
401 {
402 if (brkused && next > 0)
403 {
404 char
405 *s = &token[next-1];
406
cristybb503372010-05-27 20:51:26 +0000407 len -= (ssize_t) convertHTMLcodes(s,(int) strlen(s));
cristy3ed852e2009-09-05 21:47:34 +0000408 }
409 }
410
411 if (dataset == 255)
412 {
413 unsigned char
414 nlen = 0;
415
416 int
417 i;
418
419 if (savedolen > 0)
420 {
421 MagickOffsetType
422 offset;
423
cristybb503372010-05-27 20:51:26 +0000424 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000425 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000426 if (currentpos < 0)
427 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000428 offset=SeekBlob(ofile,savedpos,SEEK_SET);
429 if (offset < 0)
430 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000431 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000432 offset=SeekBlob(ofile,currentpos,SEEK_SET);
433 if (offset < 0)
434 return(-1);
435 savedolen = 0L;
436 }
437 if (outputlen & 1)
438 {
439 (void) WriteBlobByte(ofile,0x00);
440 outputlen++;
441 }
442 (void) WriteBlobString(ofile,"8BIM");
443 (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
444 outputlen += 6;
445 if (name)
446 nlen = (unsigned char) strlen(name);
447 (void) WriteBlobByte(ofile,nlen);
448 outputlen++;
449 for (i=0; i<nlen; i++)
450 (void) WriteBlobByte(ofile,(unsigned char) name[i]);
451 outputlen += nlen;
452 if ((nlen & 0x01) == 0)
453 {
454 (void) WriteBlobByte(ofile,0x00);
455 outputlen++;
456 }
457 if (recnum != IPTC_ID)
458 {
cristyf9cca6a2010-06-04 23:49:28 +0000459 (void) WriteBlobMSBLong(ofile, (unsigned int) len);
cristy3ed852e2009-09-05 21:47:34 +0000460 outputlen += 4;
461
462 next=0;
463 outputlen += len;
Cristyf8c318d2016-01-24 19:10:07 -0500464 while (len-- > 0)
cristy3ed852e2009-09-05 21:47:34 +0000465 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
466
467 if (outputlen & 1)
468 {
469 (void) WriteBlobByte(ofile,0x00);
470 outputlen++;
471 }
472 }
473 else
474 {
475 /* patch in a fake length for now and fix it later */
476 savedpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000477 if (savedpos < 0)
478 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000479 (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU);
cristy3ed852e2009-09-05 21:47:34 +0000480 outputlen += 4;
481 savedolen = outputlen;
482 }
483 }
484 else
485 {
486 if (len <= 0x7FFF)
487 {
488 (void) WriteBlobByte(ofile,0x1c);
489 (void) WriteBlobByte(ofile,(unsigned char) dataset);
490 (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
491 (void) WriteBlobMSBShort(ofile,(unsigned short) len);
492 outputlen += 5;
493 next=0;
494 outputlen += len;
Cristyf8c318d2016-01-24 19:10:07 -0500495 while (len-- > 0)
cristy3ed852e2009-09-05 21:47:34 +0000496 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
497 }
498 }
499 }
500 state++;
501 }
cristyf67023c2015-03-23 14:28:39 +0000502 if (token != (char *) NULL)
503 token=DestroyString(token);
504 if (newstr != (char *) NULL)
505 newstr=DestroyString(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000506 if (name != (char *) NULL)
507 name=DestroyString(name);
508 }
509 token_info=DestroyTokenInfo(token_info);
cristyd4f11052015-03-23 14:35:47 +0000510 if (token != (char *) NULL)
511 token=DestroyString(token);
512 if (newstr != (char *) NULL)
513 newstr=DestroyString(newstr);
514 if (name != (char *) NULL)
515 name=DestroyString(name);
cristy3ed852e2009-09-05 21:47:34 +0000516 line=DestroyString(line);
517 if (savedolen > 0)
518 {
519 MagickOffsetType
520 offset;
521
cristybb503372010-05-27 20:51:26 +0000522 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000523
524 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000525 if (currentpos < 0)
526 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000527 offset=SeekBlob(ofile,savedpos,SEEK_SET);
528 if (offset < 0)
529 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000530 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000531 offset=SeekBlob(ofile,currentpos,SEEK_SET);
532 if (offset < 0)
533 return(-1);
534 savedolen = 0L;
535 }
536 return outputlen;
537}
538
539static char *super_fgets_w(char **b, int *blen, Image *file)
540{
541 int
542 c,
543 len;
544
545 unsigned char
546 *p,
547 *q;
548
549 len=*blen;
550 p=(unsigned char *) (*b);
551 for (q=p; ; q++)
552 {
553 c=(int) ReadBlobLSBShort(file);
554 if ((c == -1) || (c == '\n'))
555 break;
556 if (EOFBlob(file))
557 break;
558 if ((q-p+1) >= (int) len)
559 {
560 int
561 tlen;
562
563 tlen=q-p;
564 len<<=1;
565 p=(unsigned char *) ResizeQuantumMemory(p,(size_t) (len+2),sizeof(*p));
566 *b=(char *) p;
567 if (p == (unsigned char *) NULL)
568 break;
569 q=p+tlen;
570 }
571 *q=(unsigned char) c;
572 }
573 *blen=0;
574 if ((*b) != (char *) NULL)
575 {
576 int
577 tlen;
578
579 tlen=q-p;
580 if (tlen == 0)
581 return (char *) NULL;
582 p[tlen] = '\0';
583 *blen=++tlen;
584 }
585 return((char *) p);
586}
587
cristybb503372010-05-27 20:51:26 +0000588static ssize_t parse8BIMW(Image *ifile, Image *ofile)
cristy3ed852e2009-09-05 21:47:34 +0000589{
590 char
591 brkused,
592 quoted,
593 *line,
594 *token,
595 *newstr,
596 *name;
597
598 int
599 state,
600 next;
601
602 unsigned char
603 dataset;
604
605 unsigned int
606 recnum;
607
608 int
cristy151b66d2015-04-15 10:50:31 +0000609 inputlen = MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000610
cristybb503372010-05-27 20:51:26 +0000611 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000612 savedolen = 0L,
613 outputlen = 0L;
614
615 MagickOffsetType
616 savedpos,
617 currentpos;
618
619 TokenInfo
620 *token_info;
621
622 dataset = 0;
623 recnum = 0;
624 line=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line));
cristyf67023c2015-03-23 14:28:39 +0000625 if (line == (char *) NULL)
626 return(-1);
cristyd4f11052015-03-23 14:35:47 +0000627 newstr = name = token = (char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000628 savedpos = 0;
629 token_info=AcquireTokenInfo();
630 while (super_fgets_w(&line,&inputlen,ifile) != NULL)
631 {
632 state=0;
633 next=0;
634
635 token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token));
cristyf67023c2015-03-23 14:28:39 +0000636 if (token == (char *) NULL)
637 break;
cristy3ed852e2009-09-05 21:47:34 +0000638 newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr));
cristyf67023c2015-03-23 14:28:39 +0000639 if (newstr == (char *) NULL)
640 break;
cristy3ed852e2009-09-05 21:47:34 +0000641 while (Tokenizer(token_info,0,token,(size_t) inputlen,line,"","=","\"",0,
642 &brkused,&next,&quoted)==0)
643 {
644 if (state == 0)
645 {
646 int
647 state,
648 next;
649
650 char
651 brkused,
652 quoted;
653
654 state=0;
655 next=0;
656 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","#",
657 "",0,&brkused,&next,&quoted)==0)
658 {
659 switch (state)
660 {
661 case 0:
662 if (strcmp(newstr,"8BIM")==0)
663 dataset = 255;
664 else
cristyf2f27272009-12-17 14:48:46 +0000665 dataset = (unsigned char) StringToLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000666 break;
667 case 1:
cristye27293e2009-12-18 02:53:20 +0000668 recnum=(unsigned int) StringToUnsignedLong(newstr);
cristy3ed852e2009-09-05 21:47:34 +0000669 break;
670 case 2:
cristy151b66d2015-04-15 10:50:31 +0000671 name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000672 sizeof(*name));
673 if (name)
cristy151b66d2015-04-15 10:50:31 +0000674 (void) CopyMagickString(name,newstr,strlen(newstr)+MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000675 break;
676 }
677 state++;
678 }
679 }
680 else
681 if (state == 1)
682 {
683 int
684 next;
685
cristybb503372010-05-27 20:51:26 +0000686 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000687 len;
688
689 char
690 brkused,
691 quoted;
692
693 next=0;
cristybb503372010-05-27 20:51:26 +0000694 len = (ssize_t) strlen(token);
cristy3ed852e2009-09-05 21:47:34 +0000695 while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","&",
696 "",0,&brkused,&next,&quoted)==0)
697 {
698 if (brkused && next > 0)
699 {
700 char
701 *s = &token[next-1];
702
cristybb503372010-05-27 20:51:26 +0000703 len -= (ssize_t) convertHTMLcodes(s,(int) strlen(s));
cristy3ed852e2009-09-05 21:47:34 +0000704 }
705 }
706
707 if (dataset == 255)
708 {
709 unsigned char
710 nlen = 0;
711
712 int
713 i;
714
715 if (savedolen > 0)
716 {
717 MagickOffsetType
718 offset;
719
cristybb503372010-05-27 20:51:26 +0000720 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000721 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000722 if (currentpos < 0)
723 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000724 offset=SeekBlob(ofile,savedpos,SEEK_SET);
725 if (offset < 0)
726 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000727 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000728 offset=SeekBlob(ofile,currentpos,SEEK_SET);
729 if (offset < 0)
730 return(-1);
731 savedolen = 0L;
732 }
733 if (outputlen & 1)
734 {
735 (void) WriteBlobByte(ofile,0x00);
736 outputlen++;
737 }
738 (void) WriteBlobString(ofile,"8BIM");
739 (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
740 outputlen += 6;
741 if (name)
742 nlen = (unsigned char) strlen(name);
743 (void) WriteBlobByte(ofile,(unsigned char) nlen);
744 outputlen++;
745 for (i=0; i<nlen; i++)
746 (void) WriteBlobByte(ofile,(unsigned char) name[i]);
747 outputlen += nlen;
748 if ((nlen & 0x01) == 0)
749 {
750 (void) WriteBlobByte(ofile,0x00);
751 outputlen++;
752 }
753 if (recnum != IPTC_ID)
754 {
cristyf9cca6a2010-06-04 23:49:28 +0000755 (void) WriteBlobMSBLong(ofile,(unsigned int) len);
cristy3ed852e2009-09-05 21:47:34 +0000756 outputlen += 4;
757
758 next=0;
759 outputlen += len;
760 while (len--)
761 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
762
763 if (outputlen & 1)
764 {
765 (void) WriteBlobByte(ofile,0x00);
766 outputlen++;
767 }
768 }
769 else
770 {
771 /* patch in a fake length for now and fix it later */
772 savedpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000773 if (savedpos < 0)
774 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000775 (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU);
cristy3ed852e2009-09-05 21:47:34 +0000776 outputlen += 4;
777 savedolen = outputlen;
778 }
779 }
780 else
781 {
782 if (len <= 0x7FFF)
783 {
784 (void) WriteBlobByte(ofile,0x1c);
785 (void) WriteBlobByte(ofile,dataset);
786 (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
787 (void) WriteBlobMSBShort(ofile,(unsigned short) len);
788 outputlen += 5;
789 next=0;
790 outputlen += len;
791 while (len--)
792 (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
793 }
794 }
795 }
796 state++;
797 }
cristyf67023c2015-03-23 14:28:39 +0000798 if (token != (char *) NULL)
799 token=DestroyString(token);
800 if (newstr != (char *) NULL)
801 newstr=DestroyString(newstr);
802 if (name != (char *) NULL)
803 name=DestroyString(name);
cristy3ed852e2009-09-05 21:47:34 +0000804 }
805 token_info=DestroyTokenInfo(token_info);
cristyd4f11052015-03-23 14:35:47 +0000806 if (token != (char *) NULL)
807 token=DestroyString(token);
808 if (newstr != (char *) NULL)
809 newstr=DestroyString(newstr);
810 if (name != (char *) NULL)
811 name=DestroyString(name);
cristy3ed852e2009-09-05 21:47:34 +0000812 line=DestroyString(line);
813 if (savedolen > 0)
814 {
815 MagickOffsetType
816 offset;
817
cristybb503372010-05-27 20:51:26 +0000818 ssize_t diff = outputlen - savedolen;
cristy3ed852e2009-09-05 21:47:34 +0000819
820 currentpos = TellBlob(ofile);
cristy793c1d32015-04-04 23:53:29 +0000821 if (currentpos < 0)
822 return(-1);
cristy3ed852e2009-09-05 21:47:34 +0000823 offset=SeekBlob(ofile,savedpos,SEEK_SET);
824 if (offset < 0)
825 return(-1);
cristyf9cca6a2010-06-04 23:49:28 +0000826 (void) WriteBlobMSBLong(ofile,(unsigned int) diff);
cristy3ed852e2009-09-05 21:47:34 +0000827 offset=SeekBlob(ofile,currentpos,SEEK_SET);
828 if (offset < 0)
829 return(-1);
830 savedolen = 0L;
831 }
cristyf67023c2015-03-23 14:28:39 +0000832 return(outputlen);
cristy3ed852e2009-09-05 21:47:34 +0000833}
834
835/* some defines for the different JPEG block types */
836#define M_SOF0 0xC0 /* Start Of Frame N */
837#define M_SOF1 0xC1 /* N indicates which compression process */
838#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */
839#define M_SOF3 0xC3
840#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */
841#define M_SOF6 0xC6
842#define M_SOF7 0xC7
843#define M_SOF9 0xC9
844#define M_SOF10 0xCA
845#define M_SOF11 0xCB
846#define M_SOF13 0xCD
847#define M_SOF14 0xCE
848#define M_SOF15 0xCF
849#define M_SOI 0xD8
850#define M_EOI 0xD9 /* End Of Image (end of datastream) */
851#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
852#define M_APP0 0xe0
853#define M_APP1 0xe1
854#define M_APP2 0xe2
855#define M_APP3 0xe3
856#define M_APP4 0xe4
857#define M_APP5 0xe5
858#define M_APP6 0xe6
859#define M_APP7 0xe7
860#define M_APP8 0xe8
861#define M_APP9 0xe9
862#define M_APP10 0xea
863#define M_APP11 0xeb
864#define M_APP12 0xec
865#define M_APP13 0xed
866#define M_APP14 0xee
867#define M_APP15 0xef
868
869static int jpeg_transfer_1(Image *ifile, Image *ofile)
870{
871 int c;
872
873 c = ReadBlobByte(ifile);
874 if (c == EOF)
875 return EOF;
876 (void) WriteBlobByte(ofile,(unsigned char) c);
877 return c;
878}
879
880#if defined(future)
881static int jpeg_skip_1(Image *ifile)
882{
883 int c;
884
885 c = ReadBlobByte(ifile);
886 if (c == EOF)
887 return EOF;
888 return c;
889}
890#endif
891
892static int jpeg_read_remaining(Image *ifile, Image *ofile)
893{
894 int c;
895
896 while ((c = jpeg_transfer_1(ifile, ofile)) != EOF)
897 continue;
898 return M_EOI;
899}
900
901static int jpeg_skip_variable(Image *ifile, Image *ofile)
902{
903 unsigned int length;
904 int c1,c2;
905
906 if ((c1 = jpeg_transfer_1(ifile, ofile)) == EOF)
907 return M_EOI;
908 if ((c2 = jpeg_transfer_1(ifile, ofile)) == EOF)
909 return M_EOI;
910
911 length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
912 length -= 2;
913
914 while (length--)
915 if (jpeg_transfer_1(ifile, ofile) == EOF)
916 return M_EOI;
917
918 return 0;
919}
920
921static int jpeg_skip_variable2(Image *ifile, Image *ofile)
922{
923 unsigned int length;
924 int c1,c2;
925
926 (void) ofile;
927 if ((c1 = ReadBlobByte(ifile)) == EOF) return M_EOI;
928 if ((c2 = ReadBlobByte(ifile)) == EOF) return M_EOI;
929
930 length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
931 length -= 2;
932
933 while (length--)
934 if (ReadBlobByte(ifile) == EOF)
935 return M_EOI;
936
937 return 0;
938}
939
940static int jpeg_nextmarker(Image *ifile, Image *ofile)
941{
942 int c;
943
944 /* transfer anything until we hit 0xff */
945 do
946 {
947 c = ReadBlobByte(ifile);
948 if (c == EOF)
949 return M_EOI; /* we hit EOF */
950 else
951 if (c != 0xff)
952 (void) WriteBlobByte(ofile,(unsigned char) c);
953 } while (c != 0xff);
954
955 /* get marker byte, swallowing possible padding */
956 do
957 {
958 c = ReadBlobByte(ifile);
959 if (c == EOF)
960 return M_EOI; /* we hit EOF */
961 } while (c == 0xff);
962
963 return c;
964}
965
966#if defined(future)
967static int jpeg_skip_till_marker(Image *ifile, int marker)
968{
969 int c, i;
970
971 do
972 {
973 /* skip anything until we hit 0xff */
974 i = 0;
975 do
976 {
977 c = ReadBlobByte(ifile);
978 i++;
979 if (c == EOF)
980 return M_EOI; /* we hit EOF */
981 } while (c != 0xff);
982
983 /* get marker byte, swallowing possible padding */
984 do
985 {
986 c = ReadBlobByte(ifile);
987 if (c == EOF)
988 return M_EOI; /* we hit EOF */
989 } while (c == 0xff);
990 } while (c != marker);
991 return c;
992}
993#endif
994
cristy3ed852e2009-09-05 21:47:34 +0000995/* Embed binary IPTC data into a JPEG image. */
996static int jpeg_embed(Image *ifile, Image *ofile, Image *iptc)
997{
998 unsigned int marker;
999 unsigned int done = 0;
1000 unsigned int len;
1001 int inx;
1002
1003 if (jpeg_transfer_1(ifile, ofile) != 0xFF)
1004 return 0;
1005 if (jpeg_transfer_1(ifile, ofile) != M_SOI)
1006 return 0;
1007
1008 while (done == MagickFalse)
1009 {
1010 marker=(unsigned int) jpeg_nextmarker(ifile, ofile);
1011 if (marker == M_EOI)
1012 { /* EOF */
1013 break;
1014 }
1015 else
1016 {
1017 if (marker != M_APP13)
1018 {
1019 (void) WriteBlobByte(ofile,0xff);
1020 (void) WriteBlobByte(ofile,(unsigned char) marker);
1021 }
1022 }
1023
1024 switch (marker)
1025 {
1026 case M_APP13:
1027 /* we are going to write a new APP13 marker, so don't output the old one */
1028 jpeg_skip_variable2(ifile, ofile);
1029 break;
1030
1031 case M_APP0:
1032 /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
1033 jpeg_skip_variable(ifile, ofile);
1034
cristyf432c632014-12-07 15:11:28 +00001035 if (iptc != (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001036 {
cristy317c03e2013-11-03 22:01:17 +00001037 char
dirk93b02b72013-11-16 16:03:36 +00001038 psheader[] = "\xFF\xED\0\0Photoshop 3.0\0" "8BIM\x04\x04\0\0\0\0";
cristy317c03e2013-11-03 22:01:17 +00001039
cristy3ed852e2009-09-05 21:47:34 +00001040 len=(unsigned int) GetBlobSize(iptc);
1041 if (len & 1)
1042 len++; /* make the length even */
1043 psheader[2]=(char) ((len+16)>>8);
1044 psheader[3]=(char) ((len+16)&0xff);
1045 for (inx = 0; inx < 18; inx++)
1046 (void) WriteBlobByte(ofile,(unsigned char) psheader[inx]);
1047 jpeg_read_remaining(iptc, ofile);
1048 len=(unsigned int) GetBlobSize(iptc);
1049 if (len & 1)
1050 (void) WriteBlobByte(ofile,0);
1051 }
1052 break;
1053
1054 case M_SOS:
1055 /* we hit data, no more marker-inserting can be done! */
1056 jpeg_read_remaining(ifile, ofile);
1057 done = 1;
1058 break;
1059
1060 default:
1061 jpeg_skip_variable(ifile, ofile);
1062 break;
1063 }
1064 }
1065 return 1;
1066}
1067
1068/* handle stripping the APP13 data out of a JPEG */
1069#if defined(future)
1070static void jpeg_strip(Image *ifile, Image *ofile)
1071{
1072 unsigned int marker;
1073
1074 marker = jpeg_skip_till_marker(ifile, M_SOI);
1075 if (marker == M_SOI)
1076 {
1077 (void) WriteBlobByte(ofile,0xff);
1078 (void) WriteBlobByte(ofile,M_SOI);
1079 jpeg_read_remaining(ifile, ofile);
1080 }
1081}
1082
1083/* Extract any APP13 binary data into a file. */
1084static int jpeg_extract(Image *ifile, Image *ofile)
1085{
1086 unsigned int marker;
1087 unsigned int done = 0;
1088
1089 if (jpeg_skip_1(ifile) != 0xff)
1090 return 0;
1091 if (jpeg_skip_1(ifile) != M_SOI)
1092 return 0;
1093
1094 while (done == MagickFalse)
1095 {
1096 marker = jpeg_skip_till_marker(ifile, M_APP13);
1097 if (marker == M_APP13)
1098 {
1099 marker = jpeg_nextmarker(ifile, ofile);
1100 break;
1101 }
1102 }
1103 return 1;
1104}
1105#endif
1106
dirk9f4f3542014-04-21 17:19:03 +00001107static inline void CopyBlob(Image *source,Image *destination)
1108{
1109 ssize_t
1110 i;
1111
1112 unsigned char
1113 *buffer;
1114
1115 ssize_t
1116 count,
1117 length;
1118
1119 buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent,
1120 sizeof(*buffer));
1121 if (buffer != (unsigned char *) NULL)
1122 {
1123 i=0;
1124 while ((length=ReadBlob(source,MagickMaxBufferExtent,buffer)) != 0)
1125 {
1126 count=0;
1127 for (i=0; i < (ssize_t) length; i+=count)
1128 {
1129 count=WriteBlob(destination,(size_t) (length-i),buffer+i);
1130 if (count <= 0)
1131 break;
1132 }
1133 if (i < (ssize_t) length)
1134 break;
1135 }
1136 buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1137 }
1138}
1139
cristy3ed852e2009-09-05 21:47:34 +00001140static Image *ReadMETAImage(const ImageInfo *image_info,
1141 ExceptionInfo *exception)
1142{
1143 Image
1144 *buff,
1145 *image;
1146
cristy3ed852e2009-09-05 21:47:34 +00001147 MagickBooleanType
1148 status;
1149
1150 StringInfo
1151 *profile;
1152
1153 size_t
1154 length;
1155
1156 void
1157 *blob;
1158
1159 /*
1160 Open file containing binary metadata
1161 */
1162 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001163 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001164 if (image_info->debug != MagickFalse)
1165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1166 image_info->filename);
1167 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001168 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +00001169 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001170 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1171 if (status == MagickFalse)
1172 {
1173 image=DestroyImageList(image);
1174 return((Image *) NULL);
1175 }
1176 image->columns=1;
1177 image->rows=1;
cristyea1a8aa2011-10-20 13:24:06 +00001178 if (SetImageBackgroundColor(image,exception) == MagickFalse)
cristy95524f92010-02-16 18:44:34 +00001179 {
cristy95524f92010-02-16 18:44:34 +00001180 image=DestroyImageList(image);
1181 return((Image *) NULL);
1182 }
cristy3ed852e2009-09-05 21:47:34 +00001183 length=1;
1184 if (LocaleNCompare(image_info->magick,"8BIM",4) == 0)
1185 {
1186 /*
1187 Read 8BIM binary metadata.
1188 */
cristy9950d572011-10-01 18:22:35 +00001189 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001190 if (buff == (Image *) NULL)
1191 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1192 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1193 if (blob == (unsigned char *) NULL)
1194 {
1195 buff=DestroyImage(buff);
1196 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1197 }
1198 AttachBlob(buff->blob,blob,length);
1199 if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
1200 {
1201 length=(size_t) parse8BIM(image, buff);
1202 if (length & 1)
1203 (void) WriteBlobByte(buff,0x0);
1204 }
1205 else if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
1206 {
1207 length=(size_t) parse8BIMW(image, buff);
1208 if (length & 1)
1209 (void) WriteBlobByte(buff,0x0);
1210 }
1211 else
dirk9f4f3542014-04-21 17:19:03 +00001212 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001213 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1214 GetBlobSize(buff));
1215 if (profile == (StringInfo *) NULL)
1216 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001217 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001218 profile=DestroyStringInfo(profile);
1219 if (status == MagickFalse)
1220 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1221 blob=DetachBlob(buff->blob);
1222 blob=(unsigned char *) RelinquishMagickMemory(blob);
1223 buff=DestroyImage(buff);
1224 }
1225 if (LocaleNCompare(image_info->magick,"APP1",4) == 0)
1226 {
1227 char
cristy151b66d2015-04-15 10:50:31 +00001228 name[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001229
cristy151b66d2015-04-15 10:50:31 +00001230 (void) FormatLocaleString(name,MagickPathExtent,"APP%d",1);
cristy9950d572011-10-01 18:22:35 +00001231 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001232 if (buff == (Image *) NULL)
1233 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1234 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1235 if (blob == (unsigned char *) NULL)
1236 {
1237 buff=DestroyImage(buff);
1238 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1239 }
1240 AttachBlob(buff->blob,blob,length);
1241 if (LocaleCompare(image_info->magick,"APP1JPEG") == 0)
1242 {
1243 Image
1244 *iptc;
1245
1246 int
1247 result;
1248
1249 if (image_info->profile == (void *) NULL)
1250 {
1251 blob=DetachBlob(buff->blob);
1252 blob=RelinquishMagickMemory(blob);
1253 buff=DestroyImage(buff);
1254 ThrowReaderException(CoderError,"NoIPTCProfileAvailable");
1255 }
1256 profile=CloneStringInfo((StringInfo *) image_info->profile);
cristy9950d572011-10-01 18:22:35 +00001257 iptc=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001258 if (iptc == (Image *) NULL)
1259 {
1260 blob=DetachBlob(buff->blob);
1261 blob=RelinquishMagickMemory(blob);
1262 buff=DestroyImage(buff);
1263 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1264 }
1265 AttachBlob(iptc->blob,GetStringInfoDatum(profile),
1266 GetStringInfoLength(profile));
1267 result=jpeg_embed(image,buff,iptc);
1268 blob=DetachBlob(iptc->blob);
1269 blob=RelinquishMagickMemory(blob);
1270 iptc=DestroyImage(iptc);
1271 if (result == 0)
1272 {
1273 blob=DetachBlob(buff->blob);
1274 blob=RelinquishMagickMemory(blob);
1275 buff=DestroyImage(buff);
1276 ThrowReaderException(CoderError,"JPEGEmbeddingFailed");
1277 }
1278 }
1279 else
dirk9f4f3542014-04-21 17:19:03 +00001280 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001281 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1282 GetBlobSize(buff));
1283 if (profile == (StringInfo *) NULL)
1284 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001285 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001286 profile=DestroyStringInfo(profile);
1287 if (status == MagickFalse)
1288 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1289 blob=DetachBlob(buff->blob);
1290 blob=RelinquishMagickMemory(blob);
1291 buff=DestroyImage(buff);
1292 }
1293 if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
1294 (LocaleCompare(image_info->magick,"ICM") == 0))
1295 {
cristy9950d572011-10-01 18:22:35 +00001296 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001297 if (buff == (Image *) NULL)
1298 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1299 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1300 if (blob == (unsigned char *) NULL)
1301 {
1302 buff=DestroyImage(buff);
1303 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1304 }
1305 AttachBlob(buff->blob,blob,length);
dirk9f4f3542014-04-21 17:19:03 +00001306 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001307 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1308 GetBlobSize(buff));
1309 if (profile == (StringInfo *) NULL)
1310 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001311 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001312 profile=DestroyStringInfo(profile);
1313 blob=DetachBlob(buff->blob);
1314 blob=(unsigned char *) RelinquishMagickMemory(blob);
1315 buff=DestroyImage(buff);
1316 }
1317 if (LocaleCompare(image_info->magick,"IPTC") == 0)
1318 {
cristy9950d572011-10-01 18:22:35 +00001319 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001320 if (buff == (Image *) NULL)
1321 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1322 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1323 if (blob == (unsigned char *) NULL)
1324 {
1325 buff=DestroyImage(buff);
1326 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1327 }
1328 AttachBlob(buff->blob,blob,length);
dirk9f4f3542014-04-21 17:19:03 +00001329 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001330 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1331 GetBlobSize(buff));
1332 if (profile == (StringInfo *) NULL)
1333 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001334 (void) SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001335 profile=DestroyStringInfo(profile);
1336 blob=DetachBlob(buff->blob);
1337 blob=(unsigned char *) RelinquishMagickMemory(blob);
1338 buff=DestroyImage(buff);
1339 }
1340 if (LocaleCompare(image_info->magick,"XMP") == 0)
1341 {
cristy9950d572011-10-01 18:22:35 +00001342 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001343 if (buff == (Image *) NULL)
1344 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1345 blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char));
1346 if (blob == (unsigned char *) NULL)
1347 {
1348 buff=DestroyImage(buff);
1349 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1350 }
1351 AttachBlob(buff->blob,blob,length);
dirk9f4f3542014-04-21 17:19:03 +00001352 CopyBlob(image,buff);
cristy6eb7d4b2011-09-01 13:36:26 +00001353 profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t)
1354 GetBlobSize(buff));
1355 if (profile == (StringInfo *) NULL)
1356 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +00001357 (void) SetImageProfile(image,"xmp",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001358 profile=DestroyStringInfo(profile);
1359 blob=DetachBlob(buff->blob);
1360 blob=(unsigned char *) RelinquishMagickMemory(blob);
1361 buff=DestroyImage(buff);
1362 }
1363 (void) CloseBlob(image);
1364 return(GetFirstImageInList(image));
1365}
1366
1367/*
1368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1369% %
1370% %
1371% %
1372% R e g i s t e r M E T A I m a g e %
1373% %
1374% %
1375% %
1376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377%
1378% RegisterMETAImage() adds attributes for the META image format to
1379% the list of supported formats. The attributes include the image format
1380% tag, a method to read and/or write the format, whether the format
1381% supports the saving of more than one frame to the same file or blob,
1382% whether the format supports native in-memory I/O, and a brief
1383% description of the format.
1384%
1385% The format of the RegisterMETAImage method is:
1386%
cristybb503372010-05-27 20:51:26 +00001387% size_t RegisterMETAImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001388%
1389*/
cristybb503372010-05-27 20:51:26 +00001390ModuleExport size_t RegisterMETAImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001391{
1392 MagickInfo
1393 *entry;
1394
dirk06b627a2015-04-06 18:59:17 +00001395 entry=AcquireMagickInfo("META","8BIM","Photoshop resource format");
cristy3ed852e2009-09-05 21:47:34 +00001396 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1397 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001398 entry->flags^=CoderAdjoinFlag;
1399 entry->flags|=CoderStealthFlag;
1400 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001401 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001402 entry=AcquireMagickInfo("META","8BIMTEXT","Photoshop resource text format");
cristy3ed852e2009-09-05 21:47:34 +00001403 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1404 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001405 entry->flags^=CoderAdjoinFlag;
1406 entry->flags|=CoderStealthFlag;
1407 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001408 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001409 entry=AcquireMagickInfo("META","8BIMWTEXT",
1410 "Photoshop resource wide text format");
cristy3ed852e2009-09-05 21:47:34 +00001411 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1412 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001413 entry->flags^=CoderAdjoinFlag;
1414 entry->flags|=CoderStealthFlag;
1415 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001416 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001417 entry=AcquireMagickInfo("META","APP1","Raw application information");
cristy3ed852e2009-09-05 21:47:34 +00001418 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1419 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001420 entry->flags^=CoderAdjoinFlag;
1421 entry->flags|=CoderStealthFlag;
1422 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001423 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001424 entry=AcquireMagickInfo("META","APP1JPEG","Raw JPEG binary data");
cristy3ed852e2009-09-05 21:47:34 +00001425 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1426 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001427 entry->flags^=CoderAdjoinFlag;
1428 entry->flags|=CoderStealthFlag;
1429 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001430 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001431 entry=AcquireMagickInfo("META","EXIF","Exif digital camera binary data");
cristy3ed852e2009-09-05 21:47:34 +00001432 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1433 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001434 entry->flags^=CoderAdjoinFlag;
1435 entry->flags|=CoderStealthFlag;
1436 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001437 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001438 entry=AcquireMagickInfo("META","XMP","Adobe XML metadata");
cristy3ed852e2009-09-05 21:47:34 +00001439 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1440 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001441 entry->flags^=CoderAdjoinFlag;
1442 entry->flags|=CoderStealthFlag;
1443 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001444 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001445 entry=AcquireMagickInfo("META","ICM","ICC Color Profile");
cristy3ed852e2009-09-05 21:47:34 +00001446 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1447 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001448 entry->flags^=CoderAdjoinFlag;
1449 entry->flags|=CoderStealthFlag;
1450 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001451 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001452 entry=AcquireMagickInfo("META","ICC","ICC Color Profile");
cristy3ed852e2009-09-05 21:47:34 +00001453 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1454 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001455 entry->flags^=CoderAdjoinFlag;
1456 entry->flags|=CoderStealthFlag;
1457 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001458 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001459 entry=AcquireMagickInfo("META","IPTC","IPTC Newsphoto");
cristy3ed852e2009-09-05 21:47:34 +00001460 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1461 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001462 entry->flags^=CoderAdjoinFlag;
1463 entry->flags|=CoderStealthFlag;
1464 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001465 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001466 entry=AcquireMagickInfo("META","IPTCTEXT","IPTC Newsphoto text format");
cristy3ed852e2009-09-05 21:47:34 +00001467 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1468 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001469 entry->flags^=CoderAdjoinFlag;
1470 entry->flags|=CoderStealthFlag;
1471 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001472 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001473 entry=AcquireMagickInfo("META","IPTCWTEXT","IPTC Newsphoto text format");
cristy3ed852e2009-09-05 21:47:34 +00001474 entry->decoder=(DecodeImageHandler *) ReadMETAImage;
1475 entry->encoder=(EncodeImageHandler *) WriteMETAImage;
dirk08e9a112015-02-22 01:51:41 +00001476 entry->flags^=CoderAdjoinFlag;
1477 entry->flags|=CoderStealthFlag;
1478 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001479 (void) RegisterMagickInfo(entry);
1480 return(MagickImageCoderSignature);
1481}
1482
1483/*
1484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485% %
1486% %
1487% %
1488% U n r e g i s t e r M E T A I m a g e %
1489% %
1490% %
1491% %
1492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493%
1494% UnregisterMETAImage() removes format registrations made by the
1495% META module from the list of supported formats.
1496%
1497% The format of the UnregisterMETAImage method is:
1498%
1499% UnregisterMETAImage(void)
1500%
1501*/
1502ModuleExport void UnregisterMETAImage(void)
1503{
1504 (void) UnregisterMagickInfo("8BIM");
1505 (void) UnregisterMagickInfo("8BIMTEXT");
1506 (void) UnregisterMagickInfo("8BIMWTEXT");
1507 (void) UnregisterMagickInfo("EXIF");
1508 (void) UnregisterMagickInfo("APP1");
1509 (void) UnregisterMagickInfo("APP1JPEG");
1510 (void) UnregisterMagickInfo("ICCTEXT");
1511 (void) UnregisterMagickInfo("ICM");
1512 (void) UnregisterMagickInfo("ICC");
1513 (void) UnregisterMagickInfo("IPTC");
1514 (void) UnregisterMagickInfo("IPTCTEXT");
1515 (void) UnregisterMagickInfo("IPTCWTEXT");
1516 (void) UnregisterMagickInfo("XMP");
1517}
1518
1519/*
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521% %
1522% %
1523% %
1524% W r i t e M E T A I m a g e %
1525% %
1526% %
1527% %
1528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529%
1530% WriteMETAImage() writes a META image to a file.
1531%
1532% The format of the WriteMETAImage method is:
1533%
1534% MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001535% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001536%
1537% Compression code contributed by Kyle Shorter.
1538%
1539% A description of each parameter follows:
1540%
1541% o image_info: Specifies a pointer to an ImageInfo structure.
1542%
1543% o image: A pointer to a Image structure.
1544%
cristy1e178e72011-08-28 19:44:34 +00001545% o exception: return any errors or warnings in this structure.
1546%
cristy3ed852e2009-09-05 21:47:34 +00001547*/
1548
cristy84960752009-09-12 23:56:01 +00001549static size_t GetIPTCStream(unsigned char **info,size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001550{
1551 int
1552 c;
1553
cristybb503372010-05-27 20:51:26 +00001554 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001555 i;
1556
1557 register unsigned char
1558 *p;
1559
1560 size_t
cristy84960752009-09-12 23:56:01 +00001561 extent,
cristy3ed852e2009-09-05 21:47:34 +00001562 info_length;
1563
cristy3ed852e2009-09-05 21:47:34 +00001564 unsigned int
1565 marker;
1566
cristybb503372010-05-27 20:51:26 +00001567 size_t
cristy3ed852e2009-09-05 21:47:34 +00001568 tag_length;
1569
cristy84960752009-09-12 23:56:01 +00001570 p=(*info);
1571 extent=length;
1572 if ((*p == 0x1c) && (*(p+1) == 0x02))
1573 return(length);
1574 /*
1575 Extract IPTC from 8BIM resource block.
1576 */
1577 while (extent >= 12)
1578 {
1579 if (strncmp((const char *) p,"8BIM",4))
1580 break;
1581 p+=4;
1582 extent-=4;
1583 marker=(unsigned int) (*p) << 8 | *(p+1);
1584 p+=2;
1585 extent-=2;
1586 c=*p++;
1587 extent--;
1588 c|=0x01;
1589 if ((size_t) c >= extent)
1590 break;
1591 p+=c;
1592 extent-=c;
1593 if (extent < 4)
1594 break;
cristybb503372010-05-27 20:51:26 +00001595 tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) |
1596 (((size_t) *(p+2)) << 8) | ((size_t) *(p+3));
cristy84960752009-09-12 23:56:01 +00001597 p+=4;
1598 extent-=4;
1599 if (tag_length > extent)
1600 break;
1601 if (marker == IPTC_ID)
1602 {
cristye1220a42009-09-25 14:37:03 +00001603 *info=p;
1604 return(tag_length);
cristy84960752009-09-12 23:56:01 +00001605 }
cristye1220a42009-09-25 14:37:03 +00001606 if ((tag_length & 0x01) != 0)
cristydb1856a2009-09-25 03:21:42 +00001607 tag_length++;
cristy84960752009-09-12 23:56:01 +00001608 p+=tag_length;
1609 extent-=tag_length;
1610 }
cristy3ed852e2009-09-05 21:47:34 +00001611 /*
1612 Find the beginning of the IPTC info.
1613 */
1614 p=(*info);
1615 tag_length=0;
1616iptc_find:
1617 info_length=0;
1618 marker=MagickFalse;
1619 while (length != 0)
1620 {
1621 c=(*p++);
1622 length--;
1623 if (length == 0)
1624 break;
1625 if (c == 0x1c)
1626 {
1627 p--;
1628 *info=p; /* let the caller know were it is */
1629 break;
1630 }
1631 }
1632 /*
1633 Determine the length of the IPTC info.
1634 */
1635 while (length != 0)
1636 {
1637 c=(*p++);
1638 length--;
1639 if (length == 0)
1640 break;
1641 if (c == 0x1c)
1642 marker=MagickTrue;
1643 else
1644 if (marker)
1645 break;
1646 else
1647 continue;
1648 info_length++;
1649 /*
1650 Found the 0x1c tag; skip the dataset and record number tags.
1651 */
1652 c=(*p++); /* should be 2 */
1653 length--;
1654 if (length == 0)
1655 break;
1656 if ((info_length == 1) && (c != 2))
1657 goto iptc_find;
1658 info_length++;
1659 c=(*p++); /* should be 0 */
1660 length--;
1661 if (length == 0)
1662 break;
1663 if ((info_length == 2) && (c != 0))
1664 goto iptc_find;
1665 info_length++;
1666 /*
cristybb503372010-05-27 20:51:26 +00001667 Decode the length of the block that follows - ssize_t or short format.
cristy3ed852e2009-09-05 21:47:34 +00001668 */
1669 c=(*p++);
1670 length--;
1671 if (length == 0)
1672 break;
1673 info_length++;
1674 if ((c & 0x80) != 0)
1675 {
cristyc7178b42011-10-30 20:33:29 +00001676 /*
1677 Long format.
1678 */
1679 tag_length=0;
cristy3ed852e2009-09-05 21:47:34 +00001680 for (i=0; i < 4; i++)
1681 {
cristyc7178b42011-10-30 20:33:29 +00001682 tag_length<<=8;
1683 tag_length|=(*p++);
cristy3ed852e2009-09-05 21:47:34 +00001684 length--;
1685 if (length == 0)
1686 break;
1687 info_length++;
1688 }
cristy3ed852e2009-09-05 21:47:34 +00001689 }
1690 else
1691 {
cristyc7178b42011-10-30 20:33:29 +00001692 /*
1693 Short format.
1694 */
1695 tag_length=((long) c) << 8;
cristy3ed852e2009-09-05 21:47:34 +00001696 c=(*p++);
1697 length--;
1698 if (length == 0)
1699 break;
1700 info_length++;
cristyc7178b42011-10-30 20:33:29 +00001701 tag_length|=(long) c;
cristy3ed852e2009-09-05 21:47:34 +00001702 }
1703 if (tag_length > (length+1))
1704 break;
1705 p+=tag_length;
1706 length-=tag_length;
1707 if (length == 0)
1708 break;
1709 info_length+=tag_length;
1710 }
1711 return(info_length);
1712}
1713
1714static void formatString(Image *ofile, const char *s, int len)
1715{
1716 char
cristy151b66d2015-04-15 10:50:31 +00001717 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001718
1719 (void) WriteBlobByte(ofile,'"');
1720 for (; len > 0; len--, s++) {
1721 int c = (*s) & 255;
1722 switch (c) {
1723 case '&':
1724 (void) WriteBlobString(ofile,"&amp;");
1725 break;
1726#ifdef HANDLE_GT_LT
1727 case '<':
1728 (void) WriteBlobString(ofile,"&lt;");
1729 break;
1730 case '>':
1731 (void) WriteBlobString(ofile,"&gt;");
1732 break;
1733#endif
1734 case '"':
1735 (void) WriteBlobString(ofile,"&quot;");
1736 break;
1737 default:
1738 if (isprint(c))
1739 (void) WriteBlobByte(ofile,(unsigned char) *s);
1740 else
1741 {
cristy151b66d2015-04-15 10:50:31 +00001742 (void) FormatLocaleString(temp,MagickPathExtent,"&#%d;", c & 255);
cristy3ed852e2009-09-05 21:47:34 +00001743 (void) WriteBlobString(ofile,temp);
1744 }
1745 break;
1746 }
1747 }
cristy0157aea2010-04-24 21:12:18 +00001748#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001749 (void) WriteBlobString(ofile,"\"\r\n");
1750#else
1751#if defined(macintosh)
1752 (void) WriteBlobString(ofile,"\"\r");
1753#else
1754 (void) WriteBlobString(ofile,"\"\n");
1755#endif
1756#endif
1757}
1758
1759typedef struct _tag_spec
1760{
Cristy629f8052015-09-14 07:48:08 -04001761 const short
cristy3ed852e2009-09-05 21:47:34 +00001762 id;
1763
1764 const char
1765 *name;
1766} tag_spec;
1767
1768static const tag_spec tags[] = {
1769 { 5, "Image Name" },
1770 { 7, "Edit Status" },
1771 { 10, "Priority" },
1772 { 15, "Category" },
1773 { 20, "Supplemental Category" },
1774 { 22, "Fixture Identifier" },
1775 { 25, "Keyword" },
1776 { 30, "Release Date" },
1777 { 35, "Release Time" },
1778 { 40, "Special Instructions" },
1779 { 45, "Reference Service" },
1780 { 47, "Reference Date" },
1781 { 50, "Reference Number" },
1782 { 55, "Created Date" },
1783 { 60, "Created Time" },
1784 { 65, "Originating Program" },
1785 { 70, "Program Version" },
1786 { 75, "Object Cycle" },
1787 { 80, "Byline" },
1788 { 85, "Byline Title" },
1789 { 90, "City" },
cristya0b0ad32015-07-02 12:33:54 +00001790 { 92, "Sub-Location" },
cristy3ed852e2009-09-05 21:47:34 +00001791 { 95, "Province State" },
1792 { 100, "Country Code" },
1793 { 101, "Country" },
1794 { 103, "Original Transmission Reference" },
1795 { 105, "Headline" },
1796 { 110, "Credit" },
1797 { 115, "Source" },
1798 { 116, "Copyright String" },
1799 { 120, "Caption" },
1800 { 121, "Image Orientation" },
1801 { 122, "Caption Writer" },
1802 { 131, "Local Caption" },
1803 { 200, "Custom Field 1" },
1804 { 201, "Custom Field 2" },
1805 { 202, "Custom Field 3" },
1806 { 203, "Custom Field 4" },
1807 { 204, "Custom Field 5" },
1808 { 205, "Custom Field 6" },
1809 { 206, "Custom Field 7" },
1810 { 207, "Custom Field 8" },
1811 { 208, "Custom Field 9" },
1812 { 209, "Custom Field 10" },
1813 { 210, "Custom Field 11" },
1814 { 211, "Custom Field 12" },
1815 { 212, "Custom Field 13" },
1816 { 213, "Custom Field 14" },
1817 { 214, "Custom Field 15" },
1818 { 215, "Custom Field 16" },
1819 { 216, "Custom Field 17" },
1820 { 217, "Custom Field 18" },
1821 { 218, "Custom Field 19" },
1822 { 219, "Custom Field 20" }
1823};
1824
1825static int formatIPTC(Image *ifile, Image *ofile)
1826{
1827 char
cristy151b66d2015-04-15 10:50:31 +00001828 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001829
1830 unsigned int
1831 foundiptc,
1832 tagsfound;
1833
1834 unsigned char
1835 recnum,
1836 dataset;
1837
1838 unsigned char
1839 *readable,
1840 *str;
1841
cristybb503372010-05-27 20:51:26 +00001842 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001843 tagindx,
1844 taglen;
1845
1846 int
1847 i,
1848 tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
1849
1850 int
1851 c;
1852
1853 foundiptc = 0; /* found the IPTC-Header */
1854 tagsfound = 0; /* number of tags found */
1855
1856 c = ReadBlobByte(ifile);
1857 while (c != EOF)
1858 {
1859 if (c == 0x1c)
1860 foundiptc = 1;
1861 else
1862 {
1863 if (foundiptc)
cristy326182d2014-05-18 21:48:30 +00001864 return(-1);
cristy3ed852e2009-09-05 21:47:34 +00001865 else
cristy326182d2014-05-18 21:48:30 +00001866 {
1867 c=0;
1868 continue;
1869 }
cristy3ed852e2009-09-05 21:47:34 +00001870 }
1871
1872 /* we found the 0x1c tag and now grab the dataset and record number tags */
1873 c = ReadBlobByte(ifile);
1874 if (c == EOF) return -1;
1875 dataset = (unsigned char) c;
1876 c = ReadBlobByte(ifile);
1877 if (c == EOF) return -1;
1878 recnum = (unsigned char) c;
1879 /* try to match this record to one of the ones in our named table */
1880 for (i=0; i< tagcount; i++)
1881 {
1882 if (tags[i].id == (short) recnum)
1883 break;
1884 }
1885 if (i < tagcount)
1886 readable = (unsigned char *) tags[i].name;
1887 else
1888 readable = (unsigned char *) "";
1889 /*
cristybb503372010-05-27 20:51:26 +00001890 We decode the length of the block that follows - ssize_t or short fmt.
cristy3ed852e2009-09-05 21:47:34 +00001891 */
1892 c=ReadBlobByte(ifile);
1893 if (c == EOF) return -1;
1894 if (c & (unsigned char) 0x80)
1895 return 0;
1896 else
1897 {
1898 int
1899 c0;
1900
1901 c0=ReadBlobByte(ifile);
1902 if (c0 == EOF) return -1;
1903 taglen = (c << 8) | c0;
1904 }
1905 if (taglen < 0) return -1;
1906 /* make a buffer to hold the tag datand snag it from the input stream */
cristy151b66d2015-04-15 10:50:31 +00001907 str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent),
cristy3ed852e2009-09-05 21:47:34 +00001908 sizeof(*str));
1909 if (str == (unsigned char *) NULL)
1910 {
1911 printf("MemoryAllocationFailed");
1912 return 0;
1913 }
1914 for (tagindx=0; tagindx<taglen; tagindx++)
1915 {
1916 c=ReadBlobByte(ifile);
1917 if (c == EOF) return -1;
1918 str[tagindx] = (unsigned char) c;
1919 }
1920 str[taglen] = 0;
1921
1922 /* now finish up by formatting this binary data into ASCII equivalent */
1923 if (strlen((char *)readable) > 0)
cristy151b66d2015-04-15 10:50:31 +00001924 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
cristy3ed852e2009-09-05 21:47:34 +00001925 (unsigned int) dataset, (unsigned int) recnum, readable);
1926 else
cristy151b66d2015-04-15 10:50:31 +00001927 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
cristy3ed852e2009-09-05 21:47:34 +00001928 (unsigned int) dataset,(unsigned int) recnum);
1929 (void) WriteBlobString(ofile,temp);
1930 formatString( ofile, (char *)str, taglen );
1931 str=(unsigned char *) RelinquishMagickMemory(str);
1932
1933 tagsfound++;
1934
1935 c=ReadBlobByte(ifile);
1936 }
1937 return((int) tagsfound);
1938}
1939
cristybb503372010-05-27 20:51:26 +00001940static int readWordFromBuffer(char **s, ssize_t *len)
cristy3ed852e2009-09-05 21:47:34 +00001941{
1942 unsigned char
1943 buffer[2];
1944
1945 int
1946 i,
1947 c;
1948
1949 for (i=0; i<2; i++)
1950 {
1951 c = *(*s)++; (*len)--;
1952 if (*len < 0) return -1;
1953 buffer[i] = (unsigned char) c;
1954 }
1955 return (((int) buffer[ 0 ]) << 8) |
1956 (((int) buffer[ 1 ]));
1957}
1958
cristybb503372010-05-27 20:51:26 +00001959static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len)
cristy3ed852e2009-09-05 21:47:34 +00001960{
1961 char
cristy151b66d2015-04-15 10:50:31 +00001962 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001963
1964 unsigned int
1965 foundiptc,
1966 tagsfound;
1967
1968 unsigned char
1969 recnum,
1970 dataset;
1971
1972 unsigned char
1973 *readable,
1974 *str;
1975
cristybb503372010-05-27 20:51:26 +00001976 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001977 tagindx,
1978 taglen;
1979
1980 int
1981 i,
1982 tagcount = (int) (sizeof(tags) / sizeof(tag_spec));
1983
1984 int
1985 c;
1986
1987 foundiptc = 0; /* found the IPTC-Header */
1988 tagsfound = 0; /* number of tags found */
1989
1990 while (len > 0)
1991 {
1992 c = *s++; len--;
1993 if (c == 0x1c)
1994 foundiptc = 1;
1995 else
1996 {
1997 if (foundiptc)
1998 return -1;
1999 else
2000 continue;
2001 }
2002 /*
2003 We found the 0x1c tag and now grab the dataset and record number tags.
2004 */
2005 c = *s++; len--;
2006 if (len < 0) return -1;
2007 dataset = (unsigned char) c;
2008 c = *s++; len--;
2009 if (len < 0) return -1;
2010 recnum = (unsigned char) c;
2011 /* try to match this record to one of the ones in our named table */
2012 for (i=0; i< tagcount; i++)
2013 if (tags[i].id == (short) recnum)
2014 break;
2015 if (i < tagcount)
2016 readable=(unsigned char *) tags[i].name;
2017 else
2018 readable=(unsigned char *) "";
2019 /*
cristybb503372010-05-27 20:51:26 +00002020 We decode the length of the block that follows - ssize_t or short fmt.
cristy3ed852e2009-09-05 21:47:34 +00002021 */
2022 c=(*s++);
2023 len--;
2024 if (len < 0)
2025 return(-1);
2026 if (c & (unsigned char) 0x80)
2027 return(0);
2028 else
2029 {
2030 s--;
2031 len++;
2032 taglen=readWordFromBuffer(&s, &len);
2033 }
2034 if (taglen < 0)
2035 return(-1);
cristy2867b872014-05-17 11:53:50 +00002036 if (taglen > 65535)
2037 return(-1);
cristy3ed852e2009-09-05 21:47:34 +00002038 /* make a buffer to hold the tag datand snag it from the input stream */
cristy151b66d2015-04-15 10:50:31 +00002039 str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent),
cristy3ed852e2009-09-05 21:47:34 +00002040 sizeof(*str));
2041 if (str == (unsigned char *) NULL)
2042 {
2043 printf("MemoryAllocationFailed");
2044 return 0;
2045 }
2046 for (tagindx=0; tagindx<taglen; tagindx++)
2047 {
2048 c = *s++; len--;
2049 if (len < 0)
2050 return(-1);
2051 str[tagindx]=(unsigned char) c;
2052 }
2053 str[taglen]=0;
2054
2055 /* now finish up by formatting this binary data into ASCII equivalent */
2056 if (strlen((char *)readable) > 0)
cristy151b66d2015-04-15 10:50:31 +00002057 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=",
cristy3ed852e2009-09-05 21:47:34 +00002058 (unsigned int) dataset,(unsigned int) recnum, readable);
2059 else
cristy151b66d2015-04-15 10:50:31 +00002060 (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=",
cristy3ed852e2009-09-05 21:47:34 +00002061 (unsigned int) dataset,(unsigned int) recnum);
2062 (void) WriteBlobString(ofile,temp);
2063 formatString( ofile, (char *)str, taglen );
2064 str=(unsigned char *) RelinquishMagickMemory(str);
2065
2066 tagsfound++;
2067 }
2068 return ((int) tagsfound);
2069}
2070
2071static int format8BIM(Image *ifile, Image *ofile)
2072{
2073 char
cristy151b66d2015-04-15 10:50:31 +00002074 temp[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002075
2076 unsigned int
2077 foundOSType;
2078
2079 int
2080 ID,
2081 resCount,
2082 i,
2083 c;
2084
2085 ssize_t
2086 count;
2087
2088 unsigned char
2089 *PString,
2090 *str;
2091
2092 resCount=0;
2093 foundOSType=0; /* found the OSType */
cristyda16f162011-02-19 23:52:17 +00002094 (void) foundOSType;
cristy3ed852e2009-09-05 21:47:34 +00002095 c=ReadBlobByte(ifile);
2096 while (c != EOF)
2097 {
2098 if (c == '8')
2099 {
2100 unsigned char
2101 buffer[5];
2102
2103 buffer[0]=(unsigned char) c;
2104 for (i=1; i<4; i++)
2105 {
2106 c=ReadBlobByte(ifile);
2107 if (c == EOF)
2108 return(-1);
2109 buffer[i] = (unsigned char) c;
2110 }
2111 buffer[4]=0;
2112 if (strcmp((const char *)buffer, "8BIM") == 0)
2113 foundOSType=1;
2114 else
2115 continue;
2116 }
2117 else
2118 {
2119 c=ReadBlobByte(ifile);
2120 continue;
2121 }
2122 /*
2123 We found the OSType (8BIM) and now grab the ID, PString, and Size fields.
2124 */
2125 ID=(int) ReadBlobMSBShort(ifile);
2126 if (ID < 0)
2127 return(-1);
2128 {
2129 unsigned char
2130 plen;
2131
2132 c=ReadBlobByte(ifile);
2133 if (c == EOF)
2134 return(-1);
2135 plen = (unsigned char) c;
2136 PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+
cristy151b66d2015-04-15 10:50:31 +00002137 MagickPathExtent),sizeof(*PString));
cristy3ed852e2009-09-05 21:47:34 +00002138 if (PString == (unsigned char *) NULL)
2139 {
2140 printf("MemoryAllocationFailed");
2141 return 0;
2142 }
2143 for (i=0; i<plen; i++)
2144 {
2145 c=ReadBlobByte(ifile);
2146 if (c == EOF) return -1;
2147 PString[i] = (unsigned char) c;
2148 }
2149 PString[ plen ] = 0;
2150 if ((plen & 0x01) == 0)
2151 {
2152 c=ReadBlobByte(ifile);
2153 if (c == EOF)
2154 return(-1);
2155 }
2156 }
cristy6cff05d2010-09-02 11:22:46 +00002157 count = (int) ReadBlobMSBLong(ifile);
cristy3ed852e2009-09-05 21:47:34 +00002158 if (count < 0) return -1;
2159 /* make a buffer to hold the datand snag it from the input stream */
2160 str=(unsigned char *) AcquireQuantumMemory((size_t) count,sizeof(*str));
2161 if (str == (unsigned char *) NULL)
2162 {
2163 printf("MemoryAllocationFailed");
2164 return 0;
2165 }
cristybb503372010-05-27 20:51:26 +00002166 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +00002167 {
2168 c=ReadBlobByte(ifile);
2169 if (c == EOF)
2170 return(-1);
2171 str[i]=(unsigned char) c;
2172 }
2173
2174 /* we currently skip thumbnails, since it does not make
2175 * any sense preserving them in a real world application
2176 */
2177 if (ID != THUMBNAIL_ID)
2178 {
2179 /* now finish up by formatting this binary data into
2180 * ASCII equivalent
2181 */
2182 if (strlen((const char *)PString) > 0)
cristy151b66d2015-04-15 10:50:31 +00002183 (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d#%s=",ID,
cristy3ed852e2009-09-05 21:47:34 +00002184 PString);
2185 else
cristy151b66d2015-04-15 10:50:31 +00002186 (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d=",ID);
cristy3ed852e2009-09-05 21:47:34 +00002187 (void) WriteBlobString(ofile,temp);
2188 if (ID == IPTC_ID)
2189 {
2190 formatString(ofile, "IPTC", 4);
cristybb503372010-05-27 20:51:26 +00002191 formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count);
cristy3ed852e2009-09-05 21:47:34 +00002192 }
2193 else
cristybb503372010-05-27 20:51:26 +00002194 formatString(ofile, (char *)str, (ssize_t) count);
cristy3ed852e2009-09-05 21:47:34 +00002195 }
2196 str=(unsigned char *) RelinquishMagickMemory(str);
2197 PString=(unsigned char *) RelinquishMagickMemory(PString);
2198 resCount++;
2199 c=ReadBlobByte(ifile);
2200 }
2201 return resCount;
2202}
2203
2204static MagickBooleanType WriteMETAImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002205 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002206{
2207 const StringInfo
2208 *profile;
2209
2210 MagickBooleanType
2211 status;
2212
2213 size_t
2214 length;
2215
2216 /*
2217 Open image file.
2218 */
2219 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002220 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002221 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002222 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002223 if (image->debug != MagickFalse)
2224 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2225 length=0;
2226 if (LocaleCompare(image_info->magick,"8BIM") == 0)
2227 {
2228 /*
2229 Write 8BIM image.
2230 */
2231 profile=GetImageProfile(image,"8bim");
2232 if (profile == (StringInfo *) NULL)
2233 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002234 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002235 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002236 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002237 if (status == MagickFalse)
2238 return(status);
2239 (void) WriteBlob(image,GetStringInfoLength(profile),
2240 GetStringInfoDatum(profile));
2241 (void) CloseBlob(image);
2242 return(MagickTrue);
2243 }
2244 if (LocaleCompare(image_info->magick,"iptc") == 0)
2245 {
2246 size_t
2247 length;
2248
2249 unsigned char
2250 *info;
2251
cristy0ba01102010-11-06 18:09:58 +00002252 profile=GetImageProfile(image,"iptc");
2253 if (profile == (StringInfo *) NULL)
2254 profile=GetImageProfile(image,"8bim");
cristy3ed852e2009-09-05 21:47:34 +00002255 if (profile == (StringInfo *) NULL)
2256 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002257 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002258 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002259 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002260 info=GetStringInfoDatum(profile);
2261 length=GetStringInfoLength(profile);
cristy84960752009-09-12 23:56:01 +00002262 length=GetIPTCStream(&info,length);
cristy3ed852e2009-09-05 21:47:34 +00002263 if (length == 0)
2264 ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
2265 (void) WriteBlob(image,length,info);
2266 (void) CloseBlob(image);
2267 return(MagickTrue);
2268 }
2269 if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0)
2270 {
2271 Image
2272 *buff;
2273
2274 profile=GetImageProfile(image,"8bim");
2275 if (profile == (StringInfo *) NULL)
2276 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002277 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002278 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002279 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002280 if (status == MagickFalse)
2281 return(status);
cristy9950d572011-10-01 18:22:35 +00002282 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002283 if (buff == (Image *) NULL)
2284 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2285 AttachBlob(buff->blob,GetStringInfoDatum(profile),
2286 GetStringInfoLength(profile));
2287 format8BIM(buff,image);
2288 (void) DetachBlob(buff->blob);
2289 buff=DestroyImage(buff);
2290 (void) CloseBlob(image);
2291 return(MagickTrue);
2292 }
2293 if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0)
2294 return(MagickFalse);
2295 if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0)
2296 {
2297 Image
2298 *buff;
2299
2300 unsigned char
2301 *info;
2302
2303 profile=GetImageProfile(image,"8bim");
2304 if (profile == (StringInfo *) NULL)
2305 ThrowWriterException(CoderError,"No8BIMDataIsAvailable");
2306 info=GetStringInfoDatum(profile);
2307 length=GetStringInfoLength(profile);
cristy84960752009-09-12 23:56:01 +00002308 length=GetIPTCStream(&info,length);
cristy3ed852e2009-09-05 21:47:34 +00002309 if (length == 0)
2310 ThrowWriterException(CoderError,"NoIPTCProfileAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002311 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002312 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002313 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002314 if (status == MagickFalse)
2315 return(status);
cristy9950d572011-10-01 18:22:35 +00002316 buff=AcquireImage((ImageInfo *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00002317 if (buff == (Image *) NULL)
2318 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2319 AttachBlob(buff->blob,info,length);
2320 formatIPTC(buff,image);
2321 (void) DetachBlob(buff->blob);
2322 buff=DestroyImage(buff);
2323 (void) CloseBlob(image);
2324 return(MagickTrue);
2325 }
2326 if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0)
2327 return(MagickFalse);
2328 if ((LocaleCompare(image_info->magick,"APP1") == 0) ||
2329 (LocaleCompare(image_info->magick,"EXIF") == 0) ||
2330 (LocaleCompare(image_info->magick,"XMP") == 0))
2331 {
2332 /*
2333 (void) Write APP1 image.
2334 */
2335 profile=GetImageProfile(image,image_info->magick);
2336 if (profile == (StringInfo *) NULL)
2337 ThrowWriterException(CoderError,"NoAPP1DataIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002338 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002339 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002340 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002341 if (status == MagickFalse)
2342 return(status);
2343 (void) WriteBlob(image,GetStringInfoLength(profile),
2344 GetStringInfoDatum(profile));
2345 (void) CloseBlob(image);
2346 return(MagickTrue);
2347 }
2348 if ((LocaleCompare(image_info->magick,"ICC") == 0) ||
2349 (LocaleCompare(image_info->magick,"ICM") == 0))
2350 {
2351 /*
2352 Write ICM image.
2353 */
2354 profile=GetImageProfile(image,"icc");
2355 if (profile == (StringInfo *) NULL)
2356 ThrowWriterException(CoderError,"NoColorProfileIsAvailable");
cristy3a37efd2011-08-28 20:31:03 +00002357 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002358 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002359 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002360 if (status == MagickFalse)
2361 return(status);
2362 (void) WriteBlob(image,GetStringInfoLength(profile),
2363 GetStringInfoDatum(profile));
2364 (void) CloseBlob(image);
2365 return(MagickTrue);
2366 }
2367 return(MagickFalse);
2368}