root / ImageMagick / trunk / coders / meta.c

Revision 11831, 64.5 kB (checked in by cristy, 2 weeks ago)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                        M   M  EEEEE  TTTTT   AAA                            %
7%                        MM MM  E        T    A   A                           %
8%                        M M M  EEE      T    AAAAA                           %
9%                        M   M  E        T    A   A                           %
10%                        M   M  EEEEE    T    A   A                           %
11%                                                                             %
12%                                                                             %
13%                    Read/Write Embedded Image Profiles.                      %
14%                                                                             %
15%                              Software Design                                %
16%                             William Radcliffe                               %
17%                                 July 2001                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    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*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/exception.h"
46#include "magick/exception-private.h"
47#include "magick/image.h"
48#include "magick/image-private.h"
49#include "magick/list.h"
50#include "magick/magick.h"
51#include "magick/memory_.h"
52#include "magick/profile.h"
53#include "magick/splay-tree.h"
54#include "magick/quantum-private.h"
55#include "magick/static.h"
56#include "magick/string_.h"
57#include "magick/module.h"
58#include "magick/token.h"
59#include "magick/utility.h"
60
61/*
62  Forward declarations.
63*/
64static MagickBooleanType
65  WriteMETAImage(const ImageInfo *,Image *);
66
67/*
68%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69%                                                                             %
70%                                                                             %
71%                                                                             %
72%   I s M E T A                                                               %
73%                                                                             %
74%                                                                             %
75%                                                                             %
76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77%
78%  IsMETA() returns MagickTrue if the image format type, identified by the
79%  magick string, is META.
80%
81%  The format of the IsMETA method is:
82%
83%      MagickBooleanType IsMETA(const unsigned char *magick,const size_t length)
84%
85%  A description of each parameter follows:
86%
87%    o magick: This string is generally the first few bytes of an image file
88%      or blob.
89%
90%    o length: Specifies the length of the magick string.
91%
92%
93*/
94#ifdef IMPLEMENT_IS_FUNCTION
95static MagickBooleanType IsMETA(const unsigned char *magick,const size_t length)
96{
97  if (length < 4)
98    return(MagickFalse);
99  if (LocaleNCompare((char *) magick,"8BIM",4) == 0)
100    return(MagickTrue);
101  if (LocaleNCompare((char *) magick,"APP1",4) == 0)
102    return(MagickTrue);
103  if (LocaleNCompare((char *) magick,"\034\002",2) == 0)
104    return(MagickTrue);
105  return(MagickFalse);
106}
107#endif
108
109/*
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%                                                                             %
112%                                                                             %
113%                                                                             %
114%   R e a d M E T A I m a g e                                                 %
115%                                                                             %
116%                                                                             %
117%                                                                             %
118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119%
120%  ReadMETAImage() reads a META image file and returns it.  It
121%  allocates the memory necessary for the new Image structure and returns a
122%  pointer to the new image.
123%
124%  The format of the ReadMETAImage method is:
125%
126%      Image *ReadMETAImage(const ImageInfo *image_info,
127%        ExceptionInfo *exception)
128%
129%  Decompression code contributed by Kyle Shorter.
130%
131%  A description of each parameter follows:
132%
133%    o image: Method ReadMETAImage returns a pointer to the image after
134%      reading.  A null image is returned if there is a memory shortage or
135%      if the image cannot be read.
136%
137%    o image_info: Specifies a pointer to an ImageInfo structure.
138%
139%    o exception: return any errors or warnings in this structure.
140%
141*/
142#define BUFFER_SZ 4096
143
144typedef struct _html_code
145{
146  short
147    len;
148  const char
149    *code,
150    val;
151} html_code;
152
153static html_code html_codes[] = {
154#ifdef HANDLE_GT_LT
155  { 4,"&lt;",'<' },
156  { 4,"&gt;",'>' },
157#endif
158  { 5,"&amp;",'&' },
159  { 6,"&quot;",'"' },
160  { 6,"&apos;",'\''}
161};
162
163static int stringnicmp(const char *p,const char *q,size_t n)
164{
165  register long
166    i,
167    j;
168
169  if (p == q)
170    return(0);
171  if (p == (char *) NULL)
172    return(-1);
173  if (q == (char *) NULL)
174    return(1);
175  while ((*p != '\0') && (*q != '\0'))
176  {
177    if ((*p == '\0') || (*q == '\0'))
178      break;
179    i=(*p);
180    if (islower(i))
181      i=toupper(i);
182    j=(*q);
183    if (islower(j))
184      j=toupper(j);
185    if (i != j)
186      break;
187    n--;
188    if (n == 0)
189      break;
190    p++;
191    q++;
192  }
193  return(toupper((int) *p)-toupper((int) *q));
194}
195
196static int convertHTMLcodes(char *s, int len)
197{
198  if (len <=0 || s==(char*)NULL || *s=='\0')
199    return 0;
200
201  if (s[1] == '#')
202    {
203      int val, o;
204
205      if (sscanf(s,"&#%d;",&val) == 1)
206      {
207        o = 3;
208        while (s[o] != ';')
209        {
210          o++;
211          if (o > 5)
212            break;
213        }
214        if (o < 6)
215          (void) strcpy(s+1,s+1+o);
216        *s = val;
217        return o;
218      }
219    }
220  else
221    {
222      int
223        i,
224        codes = (int) (sizeof(html_codes) / sizeof(html_code));
225
226      for (i=0; i < codes; i++)
227      {
228        if (html_codes[i].len <= len)
229          if (stringnicmp(s,html_codes[i].code,(size_t) html_codes[i].len) == 0)
230            {
231              (void) strcpy(s+1,s+html_codes[i].len);
232              *s = html_codes[i].val;
233              return html_codes[i].len-1;
234            }
235      }
236    }
237  return 0;
238}
239
240static char *super_fgets(char **b, int *blen, Image *file)
241{
242  int
243    c,
244    len;
245
246  unsigned char
247    *p,
248    *q;
249
250  len=*blen;
251  p=(unsigned char *) (*b);
252  for (q=p; ; q++)
253  {
254    c=ReadBlobByte(file);
255    if (c == EOF || c == '\n')
256      break;
257    if ((q-p+1) >= (int) len)
258      {
259        int
260          tlen;
261
262        tlen=q-p;
263        len<<=1;
264        p=(unsigned char *) ResizeQuantumMemory(p,(size_t) len+2UL,sizeof(*p));
265        *b=(char *) p;
266        if (p == (unsigned char *) NULL)
267          break;
268        q=p+tlen;
269      }
270    *q=(unsigned char) c;
271  }
272  *blen=0;
273  if (p != (unsigned char *) NULL)
274    {
275      int
276        tlen;
277
278      tlen=q-p;
279      if (tlen == 0)
280        return (char *) NULL;
281      p[tlen] = '\0';
282      *blen=++tlen;
283    }
284  return((char *) p);
285}
286
287#define BUFFER_SZ 4096
288#define IPTC_ID 1028
289#define THUMBNAIL_ID 1033
290
291static long parse8BIM(Image *ifile, Image *ofile)
292{
293  char
294    brkused,
295    quoted,
296    *line,
297    *token,
298    *newstr,
299    *name;
300
301  int
302    state,
303    next;
304
305  unsigned char
306    dataset;
307
308  unsigned int
309    recnum;
310
311  int
312    inputlen = BUFFER_SZ;
313
314  long
315    savedolen = 0L,
316    outputlen = 0L;
317
318  MagickOffsetType
319    savedpos,
320    currentpos;
321
322  TokenInfo
323    *token_info;
324
325  dataset = 0;
326  recnum = 0;
327  line = (char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line));
328  name = token = (char *)NULL;
329  savedpos = 0;
330  token_info=AcquireTokenInfo();
331  while (super_fgets(&line,&inputlen,ifile)!=NULL)
332  {
333    state=0;
334    next=0;
335
336    token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token));
337    newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr));
338    while (Tokenizer(token_info,0,token,(size_t) inputlen,line,(char *) "",
339      (char *) "=",(char *) "\"",0,&brkused,&next,&quoted)==0)
340    {
341      if (state == 0)
342        {
343          int
344            state,
345            next;
346
347          char
348            brkused,
349            quoted;
350
351          state=0;
352          next=0;
353          while (Tokenizer(token_info, 0, newstr, (size_t) inputlen, token,
354            (char *) "", (char *) "#", (char *) "", 0, &brkused, &next,
355            &quoted)==0)
356          {
357            switch (state)
358            {
359              case 0:
360                if (strcmp(newstr,"8BIM")==0)
361                  dataset = 255;
362                else
363                  dataset = (unsigned char) atoi(newstr);
364                break;
365              case 1:
366                recnum = (unsigned int) atoi(newstr);
367                break;
368              case 2:
369                name=(char *) AcquireQuantumMemory(strlen(newstr)+MaxTextExtent,
370                  sizeof(*name));
371                if (name)
372                  (void) strcpy(name,newstr);
373                break;
374            }
375            state++;
376          }
377        }
378      else
379        if (state == 1)
380          {
381            int
382              next;
383
384            long
385              len;
386
387            char
388              brkused,
389              quoted;
390
391            next=0;
392            len = (long) strlen(token);
393            while (Tokenizer(token_info,0, newstr, (size_t) inputlen, token,
394              (char *) "", (char *) "&", (char *) "", 0, &brkused, &next,
395              &quoted)==0)
396            {
397              if (brkused && next > 0)
398                {
399                  char
400                    *s = &token[next-1];
401
402                  len -= (long) convertHTMLcodes(s,(int) strlen(s));
403                }
404            }
405
406            if (dataset == 255)
407              {
408                unsigned char
409                  nlen = 0;
410
411                int
412                  i;
413
414                if (savedolen > 0)
415                  {
416                    MagickOffsetType
417                      offset;
418
419                    long diff = outputlen - savedolen;
420                    currentpos = TellBlob(ofile);
421                    offset=SeekBlob(ofile,savedpos,SEEK_SET);
422                    if (offset < 0)
423                      return(-1);
424                    (void) WriteBlobMSBLong(ofile,(unsigned long) diff);
425                    offset=SeekBlob(ofile,currentpos,SEEK_SET);
426                    if (offset < 0)
427                      return(-1);
428                    savedolen = 0L;
429                  }
430                if (outputlen & 1)
431                  {
432                    (void) WriteBlobByte(ofile,0x00);
433                    outputlen++;
434                  }
435                (void) WriteBlobString(ofile,"8BIM");
436                (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
437                outputlen += 6;
438                if (name)
439                  nlen = (unsigned char) strlen(name);
440                (void) WriteBlobByte(ofile,nlen);
441                outputlen++;
442                for (i=0; i<nlen; i++)
443                  (void) WriteBlobByte(ofile,(unsigned char) name[i]);
444                outputlen += nlen;
445                if ((nlen & 0x01) == 0)
446                  {
447                    (void) WriteBlobByte(ofile,0x00);
448                    outputlen++;
449                  }
450                if (recnum != IPTC_ID)
451                  {
452                    (void) WriteBlobMSBLong(ofile, (unsigned long) len);
453                    outputlen += 4;
454
455                    next=0;
456                    outputlen += len;
457                    while (len--)
458                      (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
459
460                    if (outputlen & 1)
461                      {
462                        (void) WriteBlobByte(ofile,0x00);
463                        outputlen++;
464                      }
465                  }
466                else
467                  {
468                    /* patch in a fake length for now and fix it later */
469                    savedpos = TellBlob(ofile);
470                    (void) WriteBlobMSBLong(ofile,0xFFFFFFFFUL);
471                    outputlen += 4;
472                    savedolen = outputlen;
473                  }
474              }
475            else
476              {
477                if (len <= 0x7FFF)
478                  {
479                    (void) WriteBlobByte(ofile,0x1c);
480                    (void) WriteBlobByte(ofile,(unsigned char) dataset);
481                    (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
482                    (void) WriteBlobMSBShort(ofile,(unsigned short) len);
483                    outputlen += 5;
484                    next=0;
485                    outputlen += len;
486                    while (len--)
487                      (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
488                  }
489              }
490          }
491      state++;
492    }
493    token=DestroyString(token);
494    newstr=DestroyString(newstr);
495    if (name != (char *) NULL)
496      name=DestroyString(name);
497  }
498  token_info=DestroyTokenInfo(token_info);
499  line=DestroyString(line);
500  if (savedolen > 0)
501    {
502      MagickOffsetType
503        offset;
504
505      long diff = outputlen - savedolen;
506
507      currentpos = TellBlob(ofile);
508      offset=SeekBlob(ofile,savedpos,SEEK_SET);
509      if (offset < 0)
510        return(-1);
511      (void) WriteBlobMSBLong(ofile,(unsigned long) diff);
512      offset=SeekBlob(ofile,currentpos,SEEK_SET);
513      if (offset < 0)
514        return(-1);
515      savedolen = 0L;
516    }
517  return outputlen;
518}
519
520static char *super_fgets_w(char **b, int *blen, Image *file)
521{
522  int
523    c,
524    len;
525
526  unsigned char
527    *p,
528    *q;
529
530  len=*blen;
531  p=(unsigned char *) (*b);
532  for (q=p; ; q++)
533  {
534    c=(int) ReadBlobLSBShort(file);
535    if ((c == -1) || (c == '\n'))
536      break;
537   if (EOFBlob(file))
538      break;
539   if ((q-p+1) >= (int) len)
540      {
541        int
542          tlen;
543
544        tlen=q-p;
545        len<<=1;
546        p=(unsigned char *) ResizeQuantumMemory(p,(size_t) (len+2),sizeof(*p));
547        *b=(char *) p;
548        if (p == (unsigned char *) NULL)
549          break;
550        q=p+tlen;
551      }
552    *q=(unsigned char) c;
553  }
554  *blen=0;
555  if ((*b) != (char *) NULL)
556    {
557      int
558        tlen;
559
560      tlen=q-p;
561      if (tlen == 0)
562        return (char *) NULL;
563      p[tlen] = '\0';
564      *blen=++tlen;
565    }
566  return((char *) p);
567}
568
569static long parse8BIMW(Image *ifile, Image *ofile)
570{
571  char
572    brkused,
573    quoted,
574    *line,
575    *token,
576    *newstr,
577    *name;
578
579  int
580    state,
581    next;
582
583  unsigned char
584    dataset;
585
586  unsigned int
587    recnum;
588
589  int
590    inputlen = BUFFER_SZ;
591
592  long
593    savedolen = 0L,
594    outputlen = 0L;
595
596  MagickOffsetType
597    savedpos,
598    currentpos;
599
600  TokenInfo
601    *token_info;
602
603  dataset = 0;
604  recnum = 0;
605  line=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line));
606  name = token = (char *)NULL;
607  savedpos = 0;
608  token_info=AcquireTokenInfo();
609  while (super_fgets_w(&line,&inputlen,ifile) != NULL)
610  {
611    state=0;
612    next=0;
613
614    token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token));
615    newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr));
616    while (Tokenizer(token_info, 0, token, (size_t) inputlen, line,
617          (char *) "", (char *) "=",
618      (char *) "\"", 0, &brkused,&next,&quoted)==0)
619    {
620      if (state == 0)
621        {
622          int
623            state,
624            next;
625
626          char
627            brkused,
628            quoted;
629
630          state=0;
631          next=0;
632          while (Tokenizer(token_info, 0, newstr, (size_t) inputlen, token,
633            (char *) "", (char *) "#", (char *) "", 0, &brkused, &next,
634            &quoted)==0)
635          {
636            switch (state)
637            {
638              case 0:
639                if (strcmp(newstr,"8BIM")==0)
640                  dataset = 255;
641                else
642                  dataset = (unsigned char) atoi(newstr);
643                break;
644              case 1:
645                recnum=(unsigned int) atoi(newstr);
646                break;
647              case 2:
648                name=(char *) AcquireQuantumMemory(strlen(newstr)+MaxTextExtent,
649                  sizeof(*name));
650                if (name)
651                  (void) CopyMagickString(name,newstr,strlen(newstr)+MaxTextExtent);
652                break;
653            }
654            state++;
655          }
656        }
657      else
658        if (state == 1)
659          {
660            int
661              next;
662
663            long
664              len;
665
666            char
667              brkused,
668              quoted;
669
670            next=0;
671            len = (long) strlen(token);
672            while (Tokenizer(token_info,0, newstr, (size_t) inputlen, token,
673              (char *) "", (char *) "&", (char *) "", 0, &brkused, &next,
674              &quoted)==0)
675            {
676              if (brkused && next > 0)
677                {
678                  char
679                    *s = &token[next-1];
680
681                  len -= (long) convertHTMLcodes(s,(int) strlen(s));
682                }
683            }
684
685            if (dataset == 255)
686              {
687                unsigned char
688                  nlen = 0;
689
690                int
691                  i;
692
693                if (savedolen > 0)
694                  {
695                    MagickOffsetType
696                      offset;
697
698                    long diff = outputlen - savedolen;
699                    currentpos = TellBlob(ofile);
700                    offset=SeekBlob(ofile,savedpos,SEEK_SET);
701                    if (offset < 0)
702                      return(-1);
703                    (void) WriteBlobMSBLong(ofile,(unsigned long) diff);
704                    offset=SeekBlob(ofile,currentpos,SEEK_SET);
705                    if (offset < 0)
706                      return(-1);
707                    savedolen = 0L;
708                  }
709                if (outputlen & 1)
710                  {
711                    (void) WriteBlobByte(ofile,0x00);
712                    outputlen++;
713                  }
714                (void) WriteBlobString(ofile,"8BIM");
715                (void) WriteBlobMSBShort(ofile,(unsigned short) recnum);
716                outputlen += 6;
717                if (name)
718                  nlen = (unsigned char) strlen(name);
719                (void) WriteBlobByte(ofile,(unsigned char) nlen);
720                outputlen++;
721                for (i=0; i<nlen; i++)
722                  (void) WriteBlobByte(ofile,(unsigned char) name[i]);
723                outputlen += nlen;
724                if ((nlen & 0x01) == 0)
725                  {
726                    (void) WriteBlobByte(ofile,0x00);
727                    outputlen++;
728                  }
729                if (recnum != IPTC_ID)
730                  {
731                    (void) WriteBlobMSBLong(ofile,(unsigned long) len);
732                    outputlen += 4;
733
734                    next=0;
735                    outputlen += len;
736                    while (len--)
737                      (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
738
739                    if (outputlen & 1)
740                      {
741                        (void) WriteBlobByte(ofile,0x00);
742                        outputlen++;
743                      }
744                  }
745                else
746                  {
747                    /* patch in a fake length for now and fix it later */
748                    savedpos = TellBlob(ofile);
749                    (void) WriteBlobMSBLong(ofile,0xFFFFFFFFUL);
750                    outputlen += 4;
751                    savedolen = outputlen;
752                  }
753              }
754            else
755              {
756                if (len <= 0x7FFF)
757                  {
758                    (void) WriteBlobByte(ofile,0x1c);
759                    (void) WriteBlobByte(ofile,dataset);
760                    (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff));
761                    (void) WriteBlobMSBShort(ofile,(unsigned short) len);
762                    outputlen += 5;
763                    next=0;
764                    outputlen += len;
765                    while (len--)
766                      (void) WriteBlobByte(ofile,(unsigned char) token[next++]);
767                  }
768              }
769          }
770      state++;
771    }
772    token=DestroyString(token);
773    newstr=DestroyString(newstr);
774    name=DestroyString(name);
775  }
776  token_info=DestroyTokenInfo(token_info);
777  line=DestroyString(line);
778  if (savedolen > 0)
779    {
780      MagickOffsetType
781        offset;
782
783      long diff = outputlen - savedolen;
784
785      currentpos = TellBlob(ofile);
786      offset=SeekBlob(ofile,savedpos,SEEK_SET);
787      if (offset < 0)
788        return(-1);
789      (void) WriteBlobMSBLong(ofile,(unsigned long) diff);
790      offset=SeekBlob(ofile,currentpos,SEEK_SET);
791      if (offset < 0)
792        return(-1);
793      savedolen = 0L;
794    }
795  return outputlen;
796}
797
798/* some defines for the different JPEG block types */
799#define M_SOF0  0xC0            /* Start Of Frame N */
800#define M_SOF1  0xC1            /* N indicates which compression process */
801#define M_SOF2  0xC2            /* Only SOF0-SOF2 are now in common use */
802#define M_SOF3  0xC3
803#define M_SOF5  0xC5            /* NB: codes C4 and CC are NOT SOF markers */
804#define M_SOF6  0xC6
805#define M_SOF7  0xC7
806#define M_SOF9  0xC9
807#define M_SOF10 0xCA
808#define M_SOF11 0xCB
809#define M_SOF13 0xCD
810#define M_SOF14 0xCE
811#define M_SOF15 0xCF
812#define M_SOI   0xD8
813#define M_EOI   0xD9            /* End Of Image (end of datastream) */
814#define M_SOS   0xDA            /* Start Of Scan (begins compressed data) */
815#define M_APP0  0xe0
816#define M_APP1  0xe1
817#define M_APP2  0xe2
818#define M_APP3  0xe3
819#define M_APP4  0xe4
820#define M_APP5  0xe5
821#define M_APP6  0xe6
822#define M_APP7  0xe7
823#define M_APP8  0xe8
824#define M_APP9  0xe9
825#define M_APP10 0xea
826#define M_APP11 0xeb
827#define M_APP12 0xec
828#define M_APP13 0xed
829#define M_APP14 0xee
830#define M_APP15 0xef
831
832static int jpeg_transfer_1(Image *ifile, Image *ofile)
833{
834  int c;
835
836  c = ReadBlobByte(ifile);
837  if (c == EOF)
838    return EOF;
839  (void) WriteBlobByte(ofile,(unsigned char) c);
840  return c;
841}
842
843#if defined(future)
844static int jpeg_skip_1(Image *ifile)
845{
846  int c;
847
848  c = ReadBlobByte(ifile);
849  if (c == EOF)
850    return EOF;
851  return c;
852}
853#endif
854
855static int jpeg_read_remaining(Image *ifile, Image *ofile)
856{
857   int c;
858
859  while ((c = jpeg_transfer_1(ifile, ofile)) != EOF)
860    continue;
861  return M_EOI;
862}
863
864static int jpeg_skip_variable(Image *ifile, Image *ofile)
865{
866  unsigned int  length;
867  int c1,c2;
868
869  if ((c1 = jpeg_transfer_1(ifile, ofile)) == EOF)
870    return M_EOI;
871  if ((c2 = jpeg_transfer_1(ifile, ofile)) == EOF)
872    return M_EOI;
873
874  length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
875  length -= 2;
876
877  while (length--)
878    if (jpeg_transfer_1(ifile, ofile) == EOF)
879      return M_EOI;
880
881  return 0;
882}
883
884static int jpeg_skip_variable2(Image *ifile, Image *ofile)
885{
886  unsigned int  length;
887  int c1,c2;
888
889  (void) ofile;
890  if ((c1 = ReadBlobByte(ifile)) == EOF) return M_EOI;
891  if ((c2 = ReadBlobByte(ifile)) == EOF) return M_EOI;
892
893  length = (((unsigned char) c1) << 8) + ((unsigned char) c2);
894  length -= 2;
895
896  while (length--)
897    if (ReadBlobByte(ifile) == EOF)
898      return M_EOI;
899
900  return 0;
901}
902
903static int jpeg_nextmarker(Image *ifile, Image *ofile)
904{
905  int c;
906
907  /* transfer anything until we hit 0xff */
908  do
909  {
910    c = ReadBlobByte(ifile);
911    if (c == EOF)
912      return M_EOI; /* we hit EOF */
913    else
914      if (c != 0xff)
915        (void) WriteBlobByte(ofile,(unsigned char) c);
916  } while (c != 0xff);
917
918  /* get marker byte, swallowing possible padding */
919  do
920  {
921    c = ReadBlobByte(ifile);
922    if (c == EOF)
923      return M_EOI; /* we hit EOF */
924  } while (c == 0xff);
925
926  return c;
927}
928
929#if defined(future)
930static int jpeg_skip_till_marker(Image *ifile, int marker)
931{
932  int c, i;
933
934  do
935  {
936    /* skip anything until we hit 0xff */
937    i = 0;
938    do
939    {
940      c = ReadBlobByte(ifile);
941      i++;
942      if (c == EOF)
943        return M_EOI; /* we hit EOF */
944    } while (c != 0xff);
945
946    /* get marker byte, swallowing possible padding */
947    do
948    {
949      c = ReadBlobByte(ifile);
950      if (c == EOF)
951        return M_EOI; /* we hit EOF */
952    } while (c == 0xff);
953  } while (c != marker);
954  return c;
955}
956#endif
957
958static char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0\0";
959
960/* Embed binary IPTC data into a JPEG image. */
961static int jpeg_embed(Image *ifile, Image *ofile, Image *iptc)
962{
963  unsigned int marker;
964  unsigned int done = 0;
965  unsigned int len;
966  int inx;
967
968  if (jpeg_transfer_1(ifile, ofile) != 0xFF)
969    return 0;
970  if (jpeg_transfer_1(ifile, ofile) != M_SOI)
971    return 0;
972
973  while (done == MagickFalse)
974  {
975    marker=(unsigned int) jpeg_nextmarker(ifile, ofile);
976    if (marker == M_EOI)
977      { /* EOF */
978        break;
979      }
980    else
981      {
982        if (marker != M_APP13)
983          {
984            (void) WriteBlobByte(ofile,0xff);
985            (void) WriteBlobByte(ofile,(unsigned char) marker);
986          }
987      }
988
989    switch (marker)
990    {
991      case M_APP13:
992        /* we are going to write a new APP13 marker, so don't output the old one */
993        jpeg_skip_variable2(ifile, ofile);
994        break;
995
996      case M_APP0:
997        /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
998        jpeg_skip_variable(ifile, ofile);
999
1000        if (iptc != (Image *)NULL)
1001          {
1002            len=(unsigned int) GetBlobSize(iptc);
1003            if (len & 1)
1004              len++; /* make the length even */
1005            psheader[2]=(char) ((len+16)>>8);
1006            psheader[3]=(char) ((len+16)&0xff);
1007            for (inx = 0; inx < 18; inx++)
1008              (void) WriteBlobByte(ofile,(unsigned char) psheader[inx]);
1009            jpeg_read_remaining(iptc, ofile);
1010            len=(unsigned int) GetBlobSize(iptc);
1011            if (len & 1)
1012              (void) WriteBlobByte(ofile,0);
1013          }
1014        break;
1015
1016