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