/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M M EEEEE TTTTT AAA % % MM MM E T A A % % M M M EEE T AAAAA % % M M E T A A % % M M EEEEE T A A % % % % % % Read/Write Embedded Image Profiles. % % % % Software Design % % William Radcliffe % % July 2001 % % % % % % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % http://www.imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % */ /* Include declarations. */ #include "MagickCore/studio.h" #include "MagickCore/blob.h" #include "MagickCore/blob-private.h" #include "MagickCore/channel.h" #include "MagickCore/exception.h" #include "MagickCore/exception-private.h" #include "MagickCore/image.h" #include "MagickCore/image-private.h" #include "MagickCore/list.h" #include "MagickCore/magick.h" #include "MagickCore/memory_.h" #include "MagickCore/module.h" #include "MagickCore/profile.h" #include "MagickCore/splay-tree.h" #include "MagickCore/quantum-private.h" #include "MagickCore/static.h" #include "MagickCore/string_.h" #include "MagickCore/string-private.h" #include "MagickCore/token.h" #include "MagickCore/utility.h" /* Forward declarations. */ static MagickBooleanType WriteMETAImage(const ImageInfo *,Image *,ExceptionInfo *); /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s M E T A % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsMETA() returns MagickTrue if the image format type, identified by the % magick string, is META. % % The format of the IsMETA method is: % % MagickBooleanType IsMETA(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o magick: compare image format pattern against these bytes. % % o length: Specifies the length of the magick string. % % */ #ifdef IMPLEMENT_IS_FUNCTION static MagickBooleanType IsMETA(const unsigned char *magick,const size_t length) { if (length < 4) return(MagickFalse); if (LocaleNCompare((char *) magick,"8BIM",4) == 0) return(MagickTrue); if (LocaleNCompare((char *) magick,"APP1",4) == 0) return(MagickTrue); if (LocaleNCompare((char *) magick,"\034\002",2) == 0) return(MagickTrue); return(MagickFalse); } #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d M E T A I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ReadMETAImage() reads a META image file and returns it. It % allocates the memory necessary for the new Image structure and returns a % pointer to the new image. % % The format of the ReadMETAImage method is: % % Image *ReadMETAImage(const ImageInfo *image_info, % ExceptionInfo *exception) % % Decompression code contributed by Kyle Shorter. % % A description of each parameter follows: % % o image: Method ReadMETAImage returns a pointer to the image after % reading. A null image is returned if there is a memory shortage or % if the image cannot be read. % % o image_info: Specifies a pointer to an ImageInfo structure. % % o exception: return any errors or warnings in this structure. % */ typedef struct _html_code { const short int len; const char *code, val; } html_code; static const html_code html_codes[] = { #ifdef HANDLE_GT_LT { 4,"<",'<' }, { 4,">",'>' }, #endif { 5,"&",'&' }, { 6,""",'"' }, { 6,"'",'\''} }; static int stringnicmp(const char *p,const char *q,size_t n) { register ssize_t i, j; if (p == q) return(0); if (p == (char *) NULL) return(-1); if (q == (char *) NULL) return(1); while ((*p != '\0') && (*q != '\0')) { if ((*p == '\0') || (*q == '\0')) break; i=(*p); if (islower(i)) i=toupper(i); j=(*q); if (islower(j)) j=toupper(j); if (i != j) break; n--; if (n == 0) break; p++; q++; } return(toupper((int) *p)-toupper((int) *q)); } static size_t convertHTMLcodes(char *s, const size_t len) { int value; if ((len == 0) || (s == (char*) NULL) || (*s=='\0')) return(0); if ((len > 3) && (s[1] == '#') && (strchr(s,';') != (char *) NULL) && (sscanf(s,"&#%d;",&value) == 1)) { size_t o = 3; while (s[o] != ';') { o++; if (o > 5) break; } if (o < 6) (void) memmove(s+1,s+1+o,strlen(s+1+o)+1); *s=value; return(o); } else { int i, codes; codes=sizeof(html_codes)/sizeof(html_code); for (i=0; i < codes; i++) { if (html_codes[i].len <= (ssize_t) len) if (stringnicmp(s, html_codes[i].code,(size_t) (html_codes[i].len)) == 0) { (void) memmove(s+1,s+html_codes[i].len, strlen(s+html_codes[i].len)+1); *s=html_codes[i].val; return(html_codes[i].len-1); } } } return(0); } static char *super_fgets(char **b, int *blen, Image *file) { int c, len; unsigned char *p, *q; len=*blen; p=(unsigned char *) (*b); for (q=p; ; q++) { c=ReadBlobByte(file); if (c == EOF || c == '\n') break; if ((q-p+1) >= (int) len) { int tlen; tlen=q-p; len<<=1; p=(unsigned char *) ResizeQuantumMemory(p,(size_t) len+2UL,sizeof(*p)); *b=(char *) p; if (p == (unsigned char *) NULL) break; q=p+tlen; } *q=(unsigned char) c; } *blen=0; if (p != (unsigned char *) NULL) { int tlen; tlen=q-p; if (tlen == 0) return (char *) NULL; p[tlen] = '\0'; *blen=++tlen; } return((char *) p); } #define IPTC_ID 1028 #define THUMBNAIL_ID 1033 static ssize_t parse8BIM(Image *ifile, Image *ofile) { char brkused, quoted, *line, *token, *newstr, *name; int state, next; unsigned char dataset; unsigned int recnum; int inputlen = MagickPathExtent; MagickOffsetType savedpos, currentpos; ssize_t savedolen = 0L, outputlen = 0L; TokenInfo *token_info; dataset = 0; recnum = 0; line = (char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line)); if (line == (char *) NULL) return(-1); newstr = name = token = (char *) NULL; savedpos = 0; token_info=AcquireTokenInfo(); while (super_fgets(&line,&inputlen,ifile)!=NULL) { state=0; next=0; token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token)); if (token == (char *) NULL) break; newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr)); if (newstr == (char *) NULL) break; while (Tokenizer(token_info,0,token,(size_t) inputlen,line,"","=","\"",0, &brkused,&next,"ed)==0) { if (state == 0) { int state, next; char brkused, quoted; state=0; next=0; while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","#", "", 0,&brkused,&next,"ed)==0) { switch (state) { case 0: if (strcmp(newstr,"8BIM")==0) dataset = 255; else dataset = (unsigned char) StringToLong(newstr); break; case 1: recnum = (unsigned int) StringToUnsignedLong(newstr); break; case 2: name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent, sizeof(*name)); if (name) (void) strcpy(name,newstr); break; } state++; } } else if (state == 1) { int next; ssize_t len; char brkused, quoted; next=0; len = (ssize_t) strlen(token); while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","&", "",0,&brkused,&next,"ed)==0) { if (brkused && next > 0) { char *s = &token[next-1]; len -= (ssize_t) convertHTMLcodes(s,strlen(s)); } } if (dataset == 255) { unsigned char nlen = 0; int i; if (savedolen > 0) { MagickOffsetType offset; ssize_t diff = outputlen - savedolen; currentpos = TellBlob(ofile); if (currentpos < 0) return(-1); offset=SeekBlob(ofile,savedpos,SEEK_SET); if (offset < 0) return(-1); (void) WriteBlobMSBLong(ofile,(unsigned int) diff); offset=SeekBlob(ofile,currentpos,SEEK_SET); if (offset < 0) return(-1); savedolen = 0L; } if (outputlen & 1) { (void) WriteBlobByte(ofile,0x00); outputlen++; } (void) WriteBlobString(ofile,"8BIM"); (void) WriteBlobMSBShort(ofile,(unsigned short) recnum); outputlen += 6; if (name) nlen = (unsigned char) strlen(name); (void) WriteBlobByte(ofile,nlen); outputlen++; for (i=0; i<nlen; i++) (void) WriteBlobByte(ofile,(unsigned char) name[i]); outputlen += nlen; if ((nlen & 0x01) == 0) { (void) WriteBlobByte(ofile,0x00); outputlen++; } if (recnum != IPTC_ID) { (void) WriteBlobMSBLong(ofile, (unsigned int) len); outputlen += 4; next=0; outputlen += len; while (len-- > 0) (void) WriteBlobByte(ofile,(unsigned char) token[next++]); if (outputlen & 1) { (void) WriteBlobByte(ofile,0x00); outputlen++; } } else { /* patch in a fake length for now and fix it later */ savedpos = TellBlob(ofile); if (savedpos < 0) return(-1); (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU); outputlen += 4; savedolen = outputlen; } } else { if (len <= 0x7FFF) { (void) WriteBlobByte(ofile,0x1c); (void) WriteBlobByte(ofile,(unsigned char) dataset); (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff)); (void) WriteBlobMSBShort(ofile,(unsigned short) len); outputlen += 5; next=0; outputlen += len; while (len-- > 0) (void) WriteBlobByte(ofile,(unsigned char) token[next++]); } } } state++; } if (token != (char *) NULL) token=DestroyString(token); if (newstr != (char *) NULL) newstr=DestroyString(newstr); if (name != (char *) NULL) name=DestroyString(name); } token_info=DestroyTokenInfo(token_info); if (token != (char *) NULL) token=DestroyString(token); if (newstr != (char *) NULL) newstr=DestroyString(newstr); if (name != (char *) NULL) name=DestroyString(name); line=DestroyString(line); if (savedolen > 0) { MagickOffsetType offset; ssize_t diff = outputlen - savedolen; currentpos = TellBlob(ofile); if (currentpos < 0) return(-1); offset=SeekBlob(ofile,savedpos,SEEK_SET); if (offset < 0) return(-1); (void) WriteBlobMSBLong(ofile,(unsigned int) diff); offset=SeekBlob(ofile,currentpos,SEEK_SET); if (offset < 0) return(-1); savedolen = 0L; } return outputlen; } static char *super_fgets_w(char **b, int *blen, Image *file) { int c, len; unsigned char *p, *q; len=*blen; p=(unsigned char *) (*b); for (q=p; ; q++) { c=ReadBlobLSBSignedShort(file); if ((c == -1) || (c == '\n')) break; if (EOFBlob(file)) break; if ((q-p+1) >= (int) len) { int tlen; tlen=q-p; len<<=1; p=(unsigned char *) ResizeQuantumMemory(p,(size_t) (len+2),sizeof(*p)); *b=(char *) p; if (p == (unsigned char *) NULL) break; q=p+tlen; } *q=(unsigned char) c; } *blen=0; if ((*b) != (char *) NULL) { int tlen; tlen=q-p; if (tlen == 0) return (char *) NULL; p[tlen] = '\0'; *blen=++tlen; } return((char *) p); } static ssize_t parse8BIMW(Image *ifile, Image *ofile) { char brkused, quoted, *line, *token, *newstr, *name; int state, next; unsigned char dataset; unsigned int recnum; int inputlen = MagickPathExtent; ssize_t savedolen = 0L, outputlen = 0L; MagickOffsetType savedpos, currentpos; TokenInfo *token_info; dataset = 0; recnum = 0; line=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*line)); if (line == (char *) NULL) return(-1); newstr = name = token = (char *) NULL; savedpos = 0; token_info=AcquireTokenInfo(); while (super_fgets_w(&line,&inputlen,ifile) != NULL) { state=0; next=0; token=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*token)); if (token == (char *) NULL) break; newstr=(char *) AcquireQuantumMemory((size_t) inputlen,sizeof(*newstr)); if (newstr == (char *) NULL) break; while (Tokenizer(token_info,0,token,(size_t) inputlen,line,"","=","\"",0, &brkused,&next,"ed)==0) { if (state == 0) { int state, next; char brkused, quoted; state=0; next=0; while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","#", "",0,&brkused,&next,"ed)==0) { switch (state) { case 0: if (strcmp(newstr,"8BIM")==0) dataset = 255; else dataset = (unsigned char) StringToLong(newstr); break; case 1: recnum=(unsigned int) StringToUnsignedLong(newstr); break; case 2: name=(char *) AcquireQuantumMemory(strlen(newstr)+MagickPathExtent, sizeof(*name)); if (name) (void) CopyMagickString(name,newstr,strlen(newstr)+MagickPathExtent); break; } state++; } } else if (state == 1) { int next; ssize_t len; char brkused, quoted; next=0; len = (ssize_t) strlen(token); while (Tokenizer(token_info,0,newstr,(size_t) inputlen,token,"","&", "",0,&brkused,&next,"ed)==0) { if (brkused && next > 0) { char *s = &token[next-1]; len -= (ssize_t) convertHTMLcodes(s,strlen(s)); } } if (dataset == 255) { unsigned char nlen = 0; int i; if (savedolen > 0) { MagickOffsetType offset; ssize_t diff = outputlen - savedolen; currentpos = TellBlob(ofile); if (currentpos < 0) return(-1); offset=SeekBlob(ofile,savedpos,SEEK_SET); if (offset < 0) return(-1); (void) WriteBlobMSBLong(ofile,(unsigned int) diff); offset=SeekBlob(ofile,currentpos,SEEK_SET); if (offset < 0) return(-1); savedolen = 0L; } if (outputlen & 1) { (void) WriteBlobByte(ofile,0x00); outputlen++; } (void) WriteBlobString(ofile,"8BIM"); (void) WriteBlobMSBShort(ofile,(unsigned short) recnum); outputlen += 6; if (name) nlen = (unsigned char) strlen(name); (void) WriteBlobByte(ofile,(unsigned char) nlen); outputlen++; for (i=0; i<nlen; i++) (void) WriteBlobByte(ofile,(unsigned char) name[i]); outputlen += nlen; if ((nlen & 0x01) == 0) { (void) WriteBlobByte(ofile,0x00); outputlen++; } if (recnum != IPTC_ID) { (void) WriteBlobMSBLong(ofile,(unsigned int) len); outputlen += 4; next=0; outputlen += len; while (len--) (void) WriteBlobByte(ofile,(unsigned char) token[next++]); if (outputlen & 1) { (void) WriteBlobByte(ofile,0x00); outputlen++; } } else { /* patch in a fake length for now and fix it later */ savedpos = TellBlob(ofile); if (savedpos < 0) return(-1); (void) WriteBlobMSBLong(ofile,0xFFFFFFFFU); outputlen += 4; savedolen = outputlen; } } else { if (len <= 0x7FFF) { (void) WriteBlobByte(ofile,0x1c); (void) WriteBlobByte(ofile,dataset); (void) WriteBlobByte(ofile,(unsigned char) (recnum & 0xff)); (void) WriteBlobMSBShort(ofile,(unsigned short) len); outputlen += 5; next=0; outputlen += len; while (len--) (void) WriteBlobByte(ofile,(unsigned char) token[next++]); } } } state++; } if (token != (char *) NULL) token=DestroyString(token); if (newstr != (char *) NULL) newstr=DestroyString(newstr); if (name != (char *) NULL) name=DestroyString(name); } token_info=DestroyTokenInfo(token_info); if (token != (char *) NULL) token=DestroyString(token); if (newstr != (char *) NULL) newstr=DestroyString(newstr); if (name != (char *) NULL) name=DestroyString(name); line=DestroyString(line); if (savedolen > 0) { MagickOffsetType offset; ssize_t diff = outputlen - savedolen; currentpos = TellBlob(ofile); if (currentpos < 0) return(-1); offset=SeekBlob(ofile,savedpos,SEEK_SET); if (offset < 0) return(-1); (void) WriteBlobMSBLong(ofile,(unsigned int) diff); offset=SeekBlob(ofile,currentpos,SEEK_SET); if (offset < 0) return(-1); savedolen = 0L; } return(outputlen); } /* some defines for the different JPEG block types */ #define M_SOF0 0xC0 /* Start Of Frame N */ #define M_SOF1 0xC1 /* N indicates which compression process */ #define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */ #define M_SOF3 0xC3 #define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */ #define M_SOF6 0xC6 #define M_SOF7 0xC7 #define M_SOF9 0xC9 #define M_SOF10 0xCA #define M_SOF11 0xCB #define M_SOF13 0xCD #define M_SOF14 0xCE #define M_SOF15 0xCF #define M_SOI 0xD8 #define M_EOI 0xD9 /* End Of Image (end of datastream) */ #define M_SOS 0xDA /* Start Of Scan (begins compressed data) */ #define M_APP0 0xe0 #define M_APP1 0xe1 #define M_APP2 0xe2 #define M_APP3 0xe3 #define M_APP4 0xe4 #define M_APP5 0xe5 #define M_APP6 0xe6 #define M_APP7 0xe7 #define M_APP8 0xe8 #define M_APP9 0xe9 #define M_APP10 0xea #define M_APP11 0xeb #define M_APP12 0xec #define M_APP13 0xed #define M_APP14 0xee #define M_APP15 0xef static int jpeg_transfer_1(Image *ifile, Image *ofile) { int c; c = ReadBlobByte(ifile); if (c == EOF) return EOF; (void) WriteBlobByte(ofile,(unsigned char) c); return c; } #if defined(future) static int jpeg_skip_1(Image *ifile) { int c; c = ReadBlobByte(ifile); if (c == EOF) return EOF; return c; } #endif static int jpeg_read_remaining(Image *ifile, Image *ofile) { int c; while ((c = jpeg_transfer_1(ifile, ofile)) != EOF) continue; return M_EOI; } static int jpeg_skip_variable(Image *ifile, Image *ofile) { unsigned int length; int c1,c2; if ((c1 = jpeg_transfer_1(ifile, ofile)) == EOF) return M_EOI; if ((c2 = jpeg_transfer_1(ifile, ofile)) == EOF) return M_EOI; length = (((unsigned char) c1) << 8) + ((unsigned char) c2); length -= 2; while (length--) if (jpeg_transfer_1(ifile, ofile) == EOF) return M_EOI; return 0; } static int jpeg_skip_variable2(Image *ifile, Image *ofile) { unsigned int length; int c1,c2; (void) ofile; if ((c1 = ReadBlobByte(ifile)) == EOF) return M_EOI; if ((c2 = ReadBlobByte(ifile)) == EOF) return M_EOI; length = (((unsigned char) c1) << 8) + ((unsigned char) c2); length -= 2; while (length--) if (ReadBlobByte(ifile) == EOF) return M_EOI; return 0; } static int jpeg_nextmarker(Image *ifile, Image *ofile) { int c; /* transfer anything until we hit 0xff */ do { c = ReadBlobByte(ifile); if (c == EOF) return M_EOI; /* we hit EOF */ else if (c != 0xff) (void) WriteBlobByte(ofile,(unsigned char) c); } while (c != 0xff); /* get marker byte, swallowing possible padding */ do { c = ReadBlobByte(ifile); if (c == EOF) return M_EOI; /* we hit EOF */ } while (c == 0xff); return c; } #if defined(future) static int jpeg_skip_till_marker(Image *ifile, int marker) { int c, i; do { /* skip anything until we hit 0xff */ i = 0; do { c = ReadBlobByte(ifile); i++; if (c == EOF) return M_EOI; /* we hit EOF */ } while (c != 0xff); /* get marker byte, swallowing possible padding */ do { c = ReadBlobByte(ifile); if (c == EOF) return M_EOI; /* we hit EOF */ } while (c == 0xff); } while (c != marker); return c; } #endif /* Embed binary IPTC data into a JPEG image. */ static int jpeg_embed(Image *ifile, Image *ofile, Image *iptc) { unsigned int marker; unsigned int done = 0; unsigned int len; int inx; if (jpeg_transfer_1(ifile, ofile) != 0xFF) return 0; if (jpeg_transfer_1(ifile, ofile) != M_SOI) return 0; while (done == MagickFalse) { marker=(unsigned int) jpeg_nextmarker(ifile, ofile); if (marker == M_EOI) { /* EOF */ break; } else { if (marker != M_APP13) { (void) WriteBlobByte(ofile,0xff); (void) WriteBlobByte(ofile,(unsigned char) marker); } } switch (marker) { case M_APP13: /* we are going to write a new APP13 marker, so don't output the old one */ jpeg_skip_variable2(ifile, ofile); break; case M_APP0: /* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */ jpeg_skip_variable(ifile, ofile); if (iptc != (Image *) NULL) { char psheader[] = "\xFF\xED\0\0Photoshop 3.0\0" "8BIM\x04\x04\0\0\0\0"; len=(unsigned int) GetBlobSize(iptc); if (len & 1) len++; /* make the length even */ psheader[2]=(char) ((len+16)>>8); psheader[3]=(char) ((len+16)&0xff); for (inx = 0; inx < 18; inx++) (void) WriteBlobByte(ofile,(unsigned char) psheader[inx]); jpeg_read_remaining(iptc, ofile); len=(unsigned int) GetBlobSize(iptc); if (len & 1) (void) WriteBlobByte(ofile,0); } break; case M_SOS: /* we hit data, no more marker-inserting can be done! */ jpeg_read_remaining(ifile, ofile); done = 1; break; default: jpeg_skip_variable(ifile, ofile); break; } } return 1; } /* handle stripping the APP13 data out of a JPEG */ #if defined(future) static void jpeg_strip(Image *ifile, Image *ofile) { unsigned int marker; marker = jpeg_skip_till_marker(ifile, M_SOI); if (marker == M_SOI) { (void) WriteBlobByte(ofile,0xff); (void) WriteBlobByte(ofile,M_SOI); jpeg_read_remaining(ifile, ofile); } } /* Extract any APP13 binary data into a file. */ static int jpeg_extract(Image *ifile, Image *ofile) { unsigned int marker; unsigned int done = 0; if (jpeg_skip_1(ifile) != 0xff) return 0; if (jpeg_skip_1(ifile) != M_SOI) return 0; while (done == MagickFalse) { marker = jpeg_skip_till_marker(ifile, M_APP13); if (marker == M_APP13) { marker = jpeg_nextmarker(ifile, ofile); break; } } return 1; } #endif static inline void CopyBlob(Image *source,Image *destination) { ssize_t i; unsigned char *buffer; ssize_t count, length; buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent, sizeof(*buffer)); if (buffer != (unsigned char *) NULL) { i=0; while ((length=ReadBlob(source,MagickMaxBufferExtent,buffer)) != 0) { count=0; for (i=0; i < (ssize_t) length; i+=count) { count=WriteBlob(destination,(size_t) (length-i),buffer+i); if (count <= 0) break; } if (i < (ssize_t) length) break; } buffer=(unsigned char *) RelinquishMagickMemory(buffer); } } static Image *ReadMETAImage(const ImageInfo *image_info, ExceptionInfo *exception) { Image *buff, *image; MagickBooleanType status; StringInfo *profile; size_t length; void *blob; /* Open file containing binary metadata */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickCoreSignature); if (image_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", image_info->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); image=AcquireImage(image_info,exception); status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == MagickFalse) { image=DestroyImageList(image); return((Image *) NULL); } image->columns=1; image->rows=1; if (SetImageBackgroundColor(image,exception) == MagickFalse) { image=DestroyImageList(image); return((Image *) NULL); } length=1; if (LocaleNCompare(image_info->magick,"8BIM",4) == 0) { /* Read 8BIM binary metadata. */ buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char)); if (blob == (unsigned char *) NULL) { buff=DestroyImage(buff); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } AttachBlob(buff->blob,blob,length); if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0) { length=(size_t) parse8BIM(image, buff); if (length & 1) (void) WriteBlobByte(buff,0x0); } else if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0) { length=(size_t) parse8BIMW(image, buff); if (length & 1) (void) WriteBlobByte(buff,0x0); } else CopyBlob(image,buff); profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t) GetBlobSize(buff)); if (profile == (StringInfo *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); status=SetImageProfile(image,"8bim",profile,exception); profile=DestroyStringInfo(profile); if (status == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=DetachBlob(buff->blob); blob=(unsigned char *) RelinquishMagickMemory(blob); buff=DestroyImage(buff); } if (LocaleNCompare(image_info->magick,"APP1",4) == 0) { char name[MagickPathExtent]; (void) FormatLocaleString(name,MagickPathExtent,"APP%d",1); buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char)); if (blob == (unsigned char *) NULL) { buff=DestroyImage(buff); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } AttachBlob(buff->blob,blob,length); if (LocaleCompare(image_info->magick,"APP1JPEG") == 0) { Image *iptc; int result; if (image_info->profile == (void *) NULL) { blob=DetachBlob(buff->blob); blob=RelinquishMagickMemory(blob); buff=DestroyImage(buff); ThrowReaderException(CoderError,"NoIPTCProfileAvailable"); } profile=CloneStringInfo((StringInfo *) image_info->profile); iptc=AcquireImage((ImageInfo *) NULL,exception); if (iptc == (Image *) NULL) { blob=DetachBlob(buff->blob); blob=RelinquishMagickMemory(blob); buff=DestroyImage(buff); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } AttachBlob(iptc->blob,GetStringInfoDatum(profile), GetStringInfoLength(profile)); result=jpeg_embed(image,buff,iptc); blob=DetachBlob(iptc->blob); blob=RelinquishMagickMemory(blob); iptc=DestroyImage(iptc); if (result == 0) { blob=DetachBlob(buff->blob); blob=RelinquishMagickMemory(blob); buff=DestroyImage(buff); ThrowReaderException(CoderError,"JPEGEmbeddingFailed"); } } else CopyBlob(image,buff); profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t) GetBlobSize(buff)); if (profile == (StringInfo *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); status=SetImageProfile(image,name,profile,exception); profile=DestroyStringInfo(profile); if (status == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=DetachBlob(buff->blob); blob=RelinquishMagickMemory(blob); buff=DestroyImage(buff); } if ((LocaleCompare(image_info->magick,"ICC") == 0) || (LocaleCompare(image_info->magick,"ICM") == 0)) { buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char)); if (blob == (unsigned char *) NULL) { buff=DestroyImage(buff); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } AttachBlob(buff->blob,blob,length); CopyBlob(image,buff); profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t) GetBlobSize(buff)); if (profile == (StringInfo *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); (void) SetImageProfile(image,"icc",profile,exception); profile=DestroyStringInfo(profile); blob=DetachBlob(buff->blob); blob=(unsigned char *) RelinquishMagickMemory(blob); buff=DestroyImage(buff); } if (LocaleCompare(image_info->magick,"IPTC") == 0) { buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char)); if (blob == (unsigned char *) NULL) { buff=DestroyImage(buff); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } AttachBlob(buff->blob,blob,length); CopyBlob(image,buff); profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t) GetBlobSize(buff)); if (profile == (StringInfo *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); (void) SetImageProfile(image,"8bim",profile,exception); profile=DestroyStringInfo(profile); blob=DetachBlob(buff->blob); blob=(unsigned char *) RelinquishMagickMemory(blob); buff=DestroyImage(buff); } if (LocaleCompare(image_info->magick,"XMP") == 0) { buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); blob=(unsigned char *) AcquireQuantumMemory(length,sizeof(unsigned char)); if (blob == (unsigned char *) NULL) { buff=DestroyImage(buff); ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } AttachBlob(buff->blob,blob,length); CopyBlob(image,buff); profile=BlobToStringInfo(GetBlobStreamData(buff),(size_t) GetBlobSize(buff)); if (profile == (StringInfo *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); (void) SetImageProfile(image,"xmp",profile,exception); profile=DestroyStringInfo(profile); blob=DetachBlob(buff->blob); blob=(unsigned char *) RelinquishMagickMemory(blob); buff=DestroyImage(buff); } (void) CloseBlob(image); return(GetFirstImageInList(image)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e g i s t e r M E T A I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RegisterMETAImage() adds attributes for the META image format to % the list of supported formats. The attributes include the image format % tag, a method to read and/or write the format, whether the format % supports the saving of more than one frame to the same file or blob, % whether the format supports native in-memory I/O, and a brief % description of the format. % % The format of the RegisterMETAImage method is: % % size_t RegisterMETAImage(void) % */ ModuleExport size_t RegisterMETAImage(void) { MagickInfo *entry; entry=AcquireMagickInfo("META","8BIM","Photoshop resource format"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","8BIMTEXT","Photoshop resource text format"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","8BIMWTEXT", "Photoshop resource wide text format"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","APP1","Raw application information"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","APP1JPEG","Raw JPEG binary data"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","EXIF","Exif digital camera binary data"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","XMP","Adobe XML metadata"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","ICM","ICC Color Profile"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","ICC","ICC Color Profile"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","IPTC","IPTC Newsphoto"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","IPTCTEXT","IPTC Newsphoto text format"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("META","IPTCWTEXT","IPTC Newsphoto text format"); entry->decoder=(DecodeImageHandler *) ReadMETAImage; entry->encoder=(EncodeImageHandler *) WriteMETAImage; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderStealthFlag; entry->flags|=CoderSeekableStreamFlag; (void) RegisterMagickInfo(entry); return(MagickImageCoderSignature); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % U n r e g i s t e r M E T A I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % UnregisterMETAImage() removes format registrations made by the % META module from the list of supported formats. % % The format of the UnregisterMETAImage method is: % % UnregisterMETAImage(void) % */ ModuleExport void UnregisterMETAImage(void) { (void) UnregisterMagickInfo("8BIM"); (void) UnregisterMagickInfo("8BIMTEXT"); (void) UnregisterMagickInfo("8BIMWTEXT"); (void) UnregisterMagickInfo("EXIF"); (void) UnregisterMagickInfo("APP1"); (void) UnregisterMagickInfo("APP1JPEG"); (void) UnregisterMagickInfo("ICCTEXT"); (void) UnregisterMagickInfo("ICM"); (void) UnregisterMagickInfo("ICC"); (void) UnregisterMagickInfo("IPTC"); (void) UnregisterMagickInfo("IPTCTEXT"); (void) UnregisterMagickInfo("IPTCWTEXT"); (void) UnregisterMagickInfo("XMP"); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % W r i t e M E T A I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % WriteMETAImage() writes a META image to a file. % % The format of the WriteMETAImage method is: % % MagickBooleanType WriteMETAImage(const ImageInfo *image_info, % Image *image,ExceptionInfo *exception) % % Compression code contributed by Kyle Shorter. % % A description of each parameter follows: % % o image_info: Specifies a pointer to an ImageInfo structure. % % o image: A pointer to a Image structure. % % o exception: return any errors or warnings in this structure. % */ static size_t GetIPTCStream(unsigned char **info,size_t length) { int c; register ssize_t i; register unsigned char *p; size_t extent, info_length; unsigned int marker; size_t tag_length; p=(*info); extent=length; if ((*p == 0x1c) && (*(p+1) == 0x02)) return(length); /* Extract IPTC from 8BIM resource block. */ while (extent >= 12) { if (strncmp((const char *) p,"8BIM",4)) break; p+=4; extent-=4; marker=(unsigned int) (*p) << 8 | *(p+1); p+=2; extent-=2; c=*p++; extent--; c|=0x01; if ((size_t) c >= extent) break; p+=c; extent-=c; if (extent < 4) break; tag_length=(((size_t) *p) << 24) | (((size_t) *(p+1)) << 16) | (((size_t) *(p+2)) << 8) | ((size_t) *(p+3)); p+=4; extent-=4; if (tag_length > extent) break; if (marker == IPTC_ID) { *info=p; return(tag_length); } if ((tag_length & 0x01) != 0) tag_length++; p+=tag_length; extent-=tag_length; } /* Find the beginning of the IPTC info. */ p=(*info); tag_length=0; iptc_find: info_length=0; marker=MagickFalse; while (length != 0) { c=(*p++); length--; if (length == 0) break; if (c == 0x1c) { p--; *info=p; /* let the caller know were it is */ break; } } /* Determine the length of the IPTC info. */ while (length != 0) { c=(*p++); length--; if (length == 0) break; if (c == 0x1c) marker=MagickTrue; else if (marker) break; else continue; info_length++; /* Found the 0x1c tag; skip the dataset and record number tags. */ c=(*p++); /* should be 2 */ length--; if (length == 0) break; if ((info_length == 1) && (c != 2)) goto iptc_find; info_length++; c=(*p++); /* should be 0 */ length--; if (length == 0) break; if ((info_length == 2) && (c != 0)) goto iptc_find; info_length++; /* Decode the length of the block that follows - ssize_t or short format. */ c=(*p++); length--; if (length == 0) break; info_length++; if ((c & 0x80) != 0) { /* Long format. */ tag_length=0; for (i=0; i < 4; i++) { tag_length<<=8; tag_length|=(*p++); length--; if (length == 0) break; info_length++; } } else { /* Short format. */ tag_length=((long) c) << 8; c=(*p++); length--; if (length == 0) break; info_length++; tag_length|=(long) c; } if (tag_length > (length+1)) break; p+=tag_length; length-=tag_length; if (length == 0) break; info_length+=tag_length; } return(info_length); } static void formatString(Image *ofile, const char *s, int len) { char temp[MagickPathExtent]; (void) WriteBlobByte(ofile,'"'); for (; len > 0; len--, s++) { int c = (*s) & 255; switch (c) { case '&': (void) WriteBlobString(ofile,"&"); break; #ifdef HANDLE_GT_LT case '<': (void) WriteBlobString(ofile,"<"); break; case '>': (void) WriteBlobString(ofile,">"); break; #endif case '"': (void) WriteBlobString(ofile,"""); break; default: if (isprint(c)) (void) WriteBlobByte(ofile,(unsigned char) *s); else { (void) FormatLocaleString(temp,MagickPathExtent,"&#%d;", c & 255); (void) WriteBlobString(ofile,temp); } break; } } #if defined(MAGICKCORE_WINDOWS_SUPPORT) (void) WriteBlobString(ofile,"\"\r\n"); #else #if defined(macintosh) (void) WriteBlobString(ofile,"\"\r"); #else (void) WriteBlobString(ofile,"\"\n"); #endif #endif } typedef struct _tag_spec { const short id; const char *name; } tag_spec; static const tag_spec tags[] = { { 5, "Image Name" }, { 7, "Edit Status" }, { 10, "Priority" }, { 15, "Category" }, { 20, "Supplemental Category" }, { 22, "Fixture Identifier" }, { 25, "Keyword" }, { 30, "Release Date" }, { 35, "Release Time" }, { 40, "Special Instructions" }, { 45, "Reference Service" }, { 47, "Reference Date" }, { 50, "Reference Number" }, { 55, "Created Date" }, { 60, "Created Time" }, { 65, "Originating Program" }, { 70, "Program Version" }, { 75, "Object Cycle" }, { 80, "Byline" }, { 85, "Byline Title" }, { 90, "City" }, { 92, "Sub-Location" }, { 95, "Province State" }, { 100, "Country Code" }, { 101, "Country" }, { 103, "Original Transmission Reference" }, { 105, "Headline" }, { 110, "Credit" }, { 115, "Source" }, { 116, "Copyright String" }, { 120, "Caption" }, { 121, "Image Orientation" }, { 122, "Caption Writer" }, { 131, "Local Caption" }, { 200, "Custom Field 1" }, { 201, "Custom Field 2" }, { 202, "Custom Field 3" }, { 203, "Custom Field 4" }, { 204, "Custom Field 5" }, { 205, "Custom Field 6" }, { 206, "Custom Field 7" }, { 207, "Custom Field 8" }, { 208, "Custom Field 9" }, { 209, "Custom Field 10" }, { 210, "Custom Field 11" }, { 211, "Custom Field 12" }, { 212, "Custom Field 13" }, { 213, "Custom Field 14" }, { 214, "Custom Field 15" }, { 215, "Custom Field 16" }, { 216, "Custom Field 17" }, { 217, "Custom Field 18" }, { 218, "Custom Field 19" }, { 219, "Custom Field 20" } }; static int formatIPTC(Image *ifile, Image *ofile) { char temp[MagickPathExtent]; unsigned int foundiptc, tagsfound; unsigned char recnum, dataset; unsigned char *readable, *str; ssize_t tagindx, taglen; int i, tagcount = (int) (sizeof(tags) / sizeof(tag_spec)); int c; foundiptc = 0; /* found the IPTC-Header */ tagsfound = 0; /* number of tags found */ c = ReadBlobByte(ifile); while (c != EOF) { if (c == 0x1c) foundiptc = 1; else { if (foundiptc) return(-1); else { c=0; continue; } } /* we found the 0x1c tag and now grab the dataset and record number tags */ c = ReadBlobByte(ifile); if (c == EOF) return -1; dataset = (unsigned char) c; c = ReadBlobByte(ifile); if (c == EOF) return -1; recnum = (unsigned char) c; /* try to match this record to one of the ones in our named table */ for (i=0; i< tagcount; i++) { if (tags[i].id == (short) recnum) break; } if (i < tagcount) readable = (unsigned char *) tags[i].name; else readable = (unsigned char *) ""; /* We decode the length of the block that follows - ssize_t or short fmt. */ c=ReadBlobByte(ifile); if (c == EOF) return -1; if (c & (unsigned char) 0x80) return 0; else { int c0; c0=ReadBlobByte(ifile); if (c0 == EOF) return -1; taglen = (c << 8) | c0; } if (taglen < 0) return -1; /* make a buffer to hold the tag datand snag it from the input stream */ str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent), sizeof(*str)); if (str == (unsigned char *) NULL) { printf("MemoryAllocationFailed"); return 0; } for (tagindx=0; tagindx<taglen; tagindx++) { c=ReadBlobByte(ifile); if (c == EOF) return -1; str[tagindx] = (unsigned char) c; } str[taglen] = 0; /* now finish up by formatting this binary data into ASCII equivalent */ if (strlen((char *)readable) > 0) (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=", (unsigned int) dataset, (unsigned int) recnum, readable); else (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=", (unsigned int) dataset,(unsigned int) recnum); (void) WriteBlobString(ofile,temp); formatString( ofile, (char *)str, taglen ); str=(unsigned char *) RelinquishMagickMemory(str); tagsfound++; c=ReadBlobByte(ifile); } return((int) tagsfound); } static int readWordFromBuffer(char **s, ssize_t *len) { unsigned char buffer[2]; int i, c; for (i=0; i<2; i++) { c = *(*s)++; (*len)--; if (*len < 0) return -1; buffer[i] = (unsigned char) c; } return (((int) buffer[ 0 ]) << 8) | (((int) buffer[ 1 ])); } static int formatIPTCfromBuffer(Image *ofile, char *s, ssize_t len) { char temp[MagickPathExtent]; unsigned int foundiptc, tagsfound; unsigned char recnum, dataset; unsigned char *readable, *str; ssize_t tagindx, taglen; int i, tagcount = (int) (sizeof(tags) / sizeof(tag_spec)); int c; foundiptc = 0; /* found the IPTC-Header */ tagsfound = 0; /* number of tags found */ while (len > 0) { c = *s++; len--; if (c == 0x1c) foundiptc = 1; else { if (foundiptc) return -1; else continue; } /* We found the 0x1c tag and now grab the dataset and record number tags. */ c = *s++; len--; if (len < 0) return -1; dataset = (unsigned char) c; c = *s++; len--; if (len < 0) return -1; recnum = (unsigned char) c; /* try to match this record to one of the ones in our named table */ for (i=0; i< tagcount; i++) if (tags[i].id == (short) recnum) break; if (i < tagcount) readable=(unsigned char *) tags[i].name; else readable=(unsigned char *) ""; /* We decode the length of the block that follows - ssize_t or short fmt. */ c=(*s++); len--; if (len < 0) return(-1); if (c & (unsigned char) 0x80) return(0); else { s--; len++; taglen=readWordFromBuffer(&s, &len); } if (taglen < 0) return(-1); if (taglen > 65535) return(-1); /* make a buffer to hold the tag datand snag it from the input stream */ str=(unsigned char *) AcquireQuantumMemory((size_t) (taglen+MagickPathExtent), sizeof(*str)); if (str == (unsigned char *) NULL) { printf("MemoryAllocationFailed"); return 0; } for (tagindx=0; tagindx<taglen; tagindx++) { c = *s++; len--; if (len < 0) return(-1); str[tagindx]=(unsigned char) c; } str[taglen]=0; /* now finish up by formatting this binary data into ASCII equivalent */ if (strlen((char *)readable) > 0) (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d#%s=", (unsigned int) dataset,(unsigned int) recnum, readable); else (void) FormatLocaleString(temp,MagickPathExtent,"%d#%d=", (unsigned int) dataset,(unsigned int) recnum); (void) WriteBlobString(ofile,temp); formatString( ofile, (char *)str, taglen ); str=(unsigned char *) RelinquishMagickMemory(str); tagsfound++; } return ((int) tagsfound); } static int format8BIM(Image *ifile, Image *ofile) { char temp[MagickPathExtent]; unsigned int foundOSType; int ID, resCount, i, c; ssize_t count; unsigned char *PString, *str; resCount=0; foundOSType=0; /* found the OSType */ (void) foundOSType; c=ReadBlobByte(ifile); while (c != EOF) { if (c == '8') { unsigned char buffer[5]; buffer[0]=(unsigned char) c; for (i=1; i<4; i++) { c=ReadBlobByte(ifile); if (c == EOF) return(-1); buffer[i] = (unsigned char) c; } buffer[4]=0; if (strcmp((const char *)buffer, "8BIM") == 0) foundOSType=1; else continue; } else { c=ReadBlobByte(ifile); continue; } /* We found the OSType (8BIM) and now grab the ID, PString, and Size fields. */ ID=ReadBlobMSBSignedShort(ifile); if (ID < 0) return(-1); { unsigned char plen; c=ReadBlobByte(ifile); if (c == EOF) return(-1); plen = (unsigned char) c; PString=(unsigned char *) AcquireQuantumMemory((size_t) (plen+ MagickPathExtent),sizeof(*PString)); if (PString == (unsigned char *) NULL) { printf("MemoryAllocationFailed"); return 0; } for (i=0; i<plen; i++) { c=ReadBlobByte(ifile); if (c == EOF) return -1; PString[i] = (unsigned char) c; } PString[ plen ] = 0; if ((plen & 0x01) == 0) { c=ReadBlobByte(ifile); if (c == EOF) return(-1); } } count=ReadBlobMSBSignedLong(ifile); if (count < 0) return -1; /* make a buffer to hold the datand snag it from the input stream */ str=(unsigned char *) AcquireQuantumMemory((size_t) count,sizeof(*str)); if (str == (unsigned char *) NULL) { printf("MemoryAllocationFailed"); return 0; } for (i=0; i < (ssize_t) count; i++) { c=ReadBlobByte(ifile); if (c == EOF) return(-1); str[i]=(unsigned char) c; } /* we currently skip thumbnails, since it does not make * any sense preserving them in a real world application */ if (ID != THUMBNAIL_ID) { /* now finish up by formatting this binary data into * ASCII equivalent */ if (strlen((const char *)PString) > 0) (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d#%s=",ID, PString); else (void) FormatLocaleString(temp,MagickPathExtent,"8BIM#%d=",ID); (void) WriteBlobString(ofile,temp); if (ID == IPTC_ID) { formatString(ofile, "IPTC", 4); formatIPTCfromBuffer(ofile, (char *)str, (ssize_t) count); } else formatString(ofile, (char *)str, (ssize_t) count); } str=(unsigned char *) RelinquishMagickMemory(str); PString=(unsigned char *) RelinquishMagickMemory(PString); resCount++; c=ReadBlobByte(ifile); } return resCount; } static MagickBooleanType WriteMETAImage(const ImageInfo *image_info, Image *image,ExceptionInfo *exception) { const StringInfo *profile; MagickBooleanType status; size_t length; /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickCoreSignature); assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); length=0; if (LocaleCompare(image_info->magick,"8BIM") == 0) { /* Write 8BIM image. */ profile=GetImageProfile(image,"8bim"); if (profile == (StringInfo *) NULL) ThrowWriterException(CoderError,"No8BIMDataIsAvailable"); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); (void) WriteBlob(image,GetStringInfoLength(profile), GetStringInfoDatum(profile)); (void) CloseBlob(image); return(MagickTrue); } if (LocaleCompare(image_info->magick,"iptc") == 0) { size_t length; unsigned char *info; profile=GetImageProfile(image,"iptc"); if (profile == (StringInfo *) NULL) profile=GetImageProfile(image,"8bim"); if (profile == (StringInfo *) NULL) ThrowWriterException(CoderError,"No8BIMDataIsAvailable"); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); info=GetStringInfoDatum(profile); length=GetStringInfoLength(profile); length=GetIPTCStream(&info,length); if (length == 0) ThrowWriterException(CoderError,"NoIPTCProfileAvailable"); (void) WriteBlob(image,length,info); (void) CloseBlob(image); return(MagickTrue); } if (LocaleCompare(image_info->magick,"8BIMTEXT") == 0) { Image *buff; profile=GetImageProfile(image,"8bim"); if (profile == (StringInfo *) NULL) ThrowWriterException(CoderError,"No8BIMDataIsAvailable"); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); AttachBlob(buff->blob,GetStringInfoDatum(profile), GetStringInfoLength(profile)); format8BIM(buff,image); (void) DetachBlob(buff->blob); buff=DestroyImage(buff); (void) CloseBlob(image); return(MagickTrue); } if (LocaleCompare(image_info->magick,"8BIMWTEXT") == 0) return(MagickFalse); if (LocaleCompare(image_info->magick,"IPTCTEXT") == 0) { Image *buff; unsigned char *info; profile=GetImageProfile(image,"8bim"); if (profile == (StringInfo *) NULL) ThrowWriterException(CoderError,"No8BIMDataIsAvailable"); info=GetStringInfoDatum(profile); length=GetStringInfoLength(profile); length=GetIPTCStream(&info,length); if (length == 0) ThrowWriterException(CoderError,"NoIPTCProfileAvailable"); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); buff=AcquireImage((ImageInfo *) NULL,exception); if (buff == (Image *) NULL) ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); AttachBlob(buff->blob,info,length); formatIPTC(buff,image); (void) DetachBlob(buff->blob); buff=DestroyImage(buff); (void) CloseBlob(image); return(MagickTrue); } if (LocaleCompare(image_info->magick,"IPTCWTEXT") == 0) return(MagickFalse); if ((LocaleCompare(image_info->magick,"APP1") == 0) || (LocaleCompare(image_info->magick,"EXIF") == 0) || (LocaleCompare(image_info->magick,"XMP") == 0)) { /* (void) Write APP1 image. */ profile=GetImageProfile(image,image_info->magick); if (profile == (StringInfo *) NULL) ThrowWriterException(CoderError,"NoAPP1DataIsAvailable"); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); (void) WriteBlob(image,GetStringInfoLength(profile), GetStringInfoDatum(profile)); (void) CloseBlob(image); return(MagickTrue); } if ((LocaleCompare(image_info->magick,"ICC") == 0) || (LocaleCompare(image_info->magick,"ICM") == 0)) { /* Write ICM image. */ profile=GetImageProfile(image,"icc"); if (profile == (StringInfo *) NULL) ThrowWriterException(CoderError,"NoColorProfileIsAvailable"); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); (void) WriteBlob(image,GetStringInfoLength(profile), GetStringInfoDatum(profile)); (void) CloseBlob(image); return(MagickTrue); } return(MagickFalse); }