//-------------------------------------------------------------------------- // Add a section (assume it doesn't already exist) - used for // adding comment sections and exif sections //-------------------------------------------------------------------------- Section_t * CreateSection(int SectionType, unsigned char * Data, int Size) { Section_t * NewSection; int a; int NewIndex; NewIndex = 2; if (SectionType == M_EXIF) NewIndex = 0; // Exif alwas goes first! // Insert it in third position - seems like a safe place to put // things like comments. if (SectionsRead < NewIndex){ // ErrFatal("Too few sections!"); ALOGE("Too few sections!"); return FALSE; } CheckSectionsAllocated(); for (a=SectionsRead;a>NewIndex;a--){ Sections[a] = Sections[a-1]; } SectionsRead += 1; NewSection = Sections+NewIndex; NewSection->Type = SectionType; NewSection->Size = Size; NewSection->Data = Data; return NewSection; }
//-------------------------------------------------------------------------- // Discard everything but the exif and comment sections. //-------------------------------------------------------------------------- void DiscardAllButExif(void) { Section_t ExifKeeper; Section_t CommentKeeper; Section_t IptcKeeper; Section_t XmpKeeper; int a; memset(&ExifKeeper, 0, sizeof(ExifKeeper)); memset(&CommentKeeper, 0, sizeof(CommentKeeper)); memset(&IptcKeeper, 0, sizeof(IptcKeeper)); memset(&XmpKeeper, 0, sizeof(IptcKeeper)); for (a=0;a<SectionsRead;a++){ if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){ ExifKeeper = Sections[a]; }else if (Sections[a].Type == M_XMP && XmpKeeper.Type == 0){ XmpKeeper = Sections[a]; }else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){ CommentKeeper = Sections[a]; }else if (Sections[a].Type == M_IPTC && IptcKeeper.Type == 0){ IptcKeeper = Sections[a]; }else{ free(Sections[a].Data); } } SectionsRead = 0; if (ExifKeeper.Type){ CheckSectionsAllocated(); Sections[SectionsRead++] = ExifKeeper; } if (CommentKeeper.Type){ CheckSectionsAllocated(); Sections[SectionsRead++] = CommentKeeper; } if (IptcKeeper.Type){ CheckSectionsAllocated(); Sections[SectionsRead++] = IptcKeeper; } if (XmpKeeper.Type){ CheckSectionsAllocated(); Sections[SectionsRead++] = XmpKeeper; } }
//-------------------------------------------------------------------------- // Parse the marker stream until SOS or EOI is seen; //-------------------------------------------------------------------------- int ReadJpegSections (FILE * infile, ReadMode_t ReadMode) { int a; int HaveCom = FALSE; a = fgetc(infile); if (a != 0xff || fgetc(infile) != M_SOI){ return FALSE; } ImageInfo.JfifHeader.XDensity = ImageInfo.JfifHeader.YDensity = 300; ImageInfo.JfifHeader.ResolutionUnits = 1; for(;;){ int itemlen; int prev; int marker = 0; int ll,lh, got; uchar * Data; CheckSectionsAllocated(); prev = 0; for (a=0;;a++){ marker = fgetc(infile); if (marker != 0xff && prev == 0xff) break; prev = marker; } if (a > 10){ ErrNonfatal("Extraneous %d padding bytes before section %02X",a-1,marker); } Sections[SectionsRead].Type = marker; // Read the length of the section. lh = fgetc(infile); ll = fgetc(infile); itemlen = (lh << 8) | ll; if (itemlen < 2){ ErrFatal("invalid marker"); } Sections[SectionsRead].Size = itemlen; Data = (uchar *)malloc(itemlen); if (Data == NULL){ ErrFatal("Could not allocate memory"); } Sections[SectionsRead].Data = Data; // Store first two pre-read bytes. Data[0] = (uchar)lh; Data[1] = (uchar)ll; got = fread(Data+2, 1, itemlen-2, infile); // Read the whole section. if (got != itemlen-2){ ErrFatal("Premature end of file?"); } SectionsRead += 1; switch(marker){ case M_SOS: // stop before hitting compressed data // If reading entire image is requested, read the rest of the data. if (ReadMode & READ_IMAGE){ int cp, ep, size; // Determine how much file is left. cp = ftell(infile); fseek(infile, 0, SEEK_END); ep = ftell(infile); fseek(infile, cp, SEEK_SET); size = ep-cp; Data = (uchar *)malloc(size); if (Data == NULL){ ErrFatal("could not allocate data for entire image"); } got = fread(Data, 1, size, infile); if (got != size){ ErrFatal("could not read the rest of the image"); } CheckSectionsAllocated(); Sections[SectionsRead].Data = Data; Sections[SectionsRead].Size = size; Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; SectionsRead ++; HaveAll = 1; } return TRUE; case M_EOI: // in case it's a tables-only JPEG stream fprintf(stderr,"No image in jpeg!\n"); return FALSE; case M_COM: // Comment section if (HaveCom || ((ReadMode & READ_METADATA) == 0)){ // Discard this section. free(Sections[--SectionsRead].Data); }else{ process_COM(Data, itemlen); HaveCom = TRUE; } break; case M_JFIF: // Regular jpegs always have this tag, exif images have the exif // marker instead, althogh ACDsee will write images with both markers. // this program will re-create this marker on absence of exif marker. // hence no need to keep the copy from the file. if (memcmp(Data+2, "JFIF\0",5)){ fprintf(stderr,"Header missing JFIF marker\n"); } if (itemlen < 16){ fprintf(stderr,"Jfif header too short\n"); goto ignore; } ImageInfo.JfifHeader.Present = TRUE; ImageInfo.JfifHeader.ResolutionUnits = Data[9]; ImageInfo.JfifHeader.XDensity = (Data[10]<<8) | Data[11]; ImageInfo.JfifHeader.YDensity = (Data[12]<<8) | Data[13]; if (ShowTags){ printf("JFIF SOI marker: Units: %d ",ImageInfo.JfifHeader.ResolutionUnits); switch(ImageInfo.JfifHeader.ResolutionUnits){ case 0: printf("(aspect ratio)"); break; case 1: printf("(dots per inch)"); break; case 2: printf("(dots per cm)"); break; default: printf("(unknown)"); break; } printf(" X-density=%d Y-density=%d\n",ImageInfo.JfifHeader.XDensity, ImageInfo.JfifHeader.YDensity); if (Data[14] || Data[15]){ fprintf(stderr,"Ignoring jfif header thumbnail\n"); } } ignore: free(Sections[--SectionsRead].Data); break; case M_EXIF: // There can be different section using the same marker. if (ReadMode & READ_METADATA){ if (memcmp(Data+2, "Exif", 4) == 0){ process_EXIF(Data, itemlen); break; }else if (memcmp(Data+2, "http:", 5) == 0){ Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes. if (ShowTags){ printf("Image cotains XMP section, %d bytes long\n", itemlen); if (ShowTags){ ShowXmp(Sections[SectionsRead-1]); } } break; } } // Oterwise, discard this section. free(Sections[--SectionsRead].Data); break; case M_IPTC: if (ReadMode & READ_METADATA){ if (ShowTags){ printf("Image cotains IPTC section, %d bytes long\n", itemlen); } // Note: We just store the IPTC section. Its relatively straightforward // and we don't act on any part of it, so just display it at parse time. }else{ free(Sections[--SectionsRead].Data); } break; case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: process_SOFn(Data, marker); break; default: // Skip any other sections. if (ShowTags){ printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen); } break; } } return TRUE; }
//-------------------------------------------------------------------------- // Parse the marker buffer until SOS or EOI is seen; //-------------------------------------------------------------------------- int ReadJpegSectionsFromBuffer (unsigned char* buffer, unsigned int buffer_size, ReadMode_t ReadMode) { int a; unsigned int pos = 0; int HaveCom = FALSE; if (!buffer) { return FALSE; } if (buffer_size < 1) { return FALSE; } a = (int) buffer[pos++]; if (a != 0xff || buffer[pos++] != M_SOI){ return FALSE; } for(;;){ int itemlen; int marker = 0; int ll,lh, got; uchar * Data; CheckSectionsAllocated(); for (a=0;a<=16;a++){ marker = buffer[pos++]; if (marker != 0xff) break; if (a >= 16){ fprintf(stderr,"too many padding bytes\n"); return FALSE; } } Sections[SectionsRead].Type = marker; Sections[SectionsRead].Offset = pos; // Read the length of the section. lh = buffer[pos++]; ll = buffer[pos++]; itemlen = (lh << 8) | ll; if (itemlen < 2) { ALOGE("invalid marker"); return FALSE; } Sections[SectionsRead].Size = itemlen; Data = (uchar *)malloc(itemlen); if (Data == NULL) { ALOGE("Could not allocate memory"); return 0; } Sections[SectionsRead].Data = Data; // Store first two pre-read bytes. Data[0] = (uchar)lh; Data[1] = (uchar)ll; if (pos+itemlen-2 > buffer_size) { ALOGE("Premature end of file?"); return FALSE; } memcpy(Data+2, buffer+pos, itemlen-2); // Read the whole section. pos += itemlen-2; SectionsRead += 1; printf("reading marker %d", marker); switch(marker){ case M_SOS: // stop before hitting compressed data // If reading entire image is requested, read the rest of the data. if (ReadMode & READ_IMAGE){ int size; // Determine how much file is left. size = buffer_size - pos; if (size < 1) { ALOGE("could not read the rest of the image"); return FALSE; } Data = (uchar *)malloc(size); if (Data == NULL) { ALOGE("%d: could not allocate data for entire image size: %d", __LINE__, size); return FALSE; } memcpy(Data, buffer+pos, size); CheckSectionsAllocated(); Sections[SectionsRead].Data = Data; Sections[SectionsRead].Offset = pos; Sections[SectionsRead].Size = size; Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; SectionsRead ++; HaveAll = 1; } return TRUE; case M_EOI: // in case it's a tables-only JPEG stream ALOGE("No image in jpeg!\n"); return FALSE; case M_COM: // Comment section if (HaveCom || ((ReadMode & READ_METADATA) == 0)){ // Discard this section. free(Sections[--SectionsRead].Data); }else{ process_COM(Data, itemlen); HaveCom = TRUE; } break; case M_JFIF: // Regular jpegs always have this tag, exif images have the exif // marker instead, althogh ACDsee will write images with both markers. // this program will re-create this marker on absence of exif marker. // hence no need to keep the copy from the file. free(Sections[--SectionsRead].Data); break; case M_EXIF: // There can be different section using the same marker. if (ReadMode & READ_METADATA){ if (memcmp(Data+2, "Exif", 4) == 0){ process_EXIF(Data, itemlen); break; }else if (memcmp(Data+2, "http:", 5) == 0){ Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes. if (ShowTags){ ALOGD("Image cotains XMP section, %d bytes long\n", itemlen); if (ShowTags){ ShowXmp(Sections[SectionsRead-1]); } } break; } } // Oterwise, discard this section. free(Sections[--SectionsRead].Data); break; case M_IPTC: if (ReadMode & READ_METADATA){ if (ShowTags){ ALOGD("Image cotains IPTC section, %d bytes long\n", itemlen); } // Note: We just store the IPTC section. Its relatively straightforward // and we don't act on any part of it, so just display it at parse time. }else{ free(Sections[--SectionsRead].Data); } break; case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: process_SOFn(Data, marker); break; default: // Skip any other sections. if (ShowTags){ ALOGD("Jpeg section marker 0x%02x size %d\n",marker, itemlen); } break; } } return TRUE; }
static int ReadJpegStreamSections (ReadMode_t ReadMode, CALLBACK_js_getc js_getc, CALLBACK_js_read js_read) { int a; int HaveCom = FALSE; a = js_getc(); if (a != 0xff || js_getc() != M_SOI){ ALOGE("a = %d",a); return FALSE; } for(;;){ int itemlen; int prev; int marker = 0; int ll,lh, got; uchar * Data; CheckSectionsAllocated(); prev = 0; for (a=0;;a++){ marker = js_getc(); if (marker != 0xff && prev == 0xff) break; prev = marker; if(marker == EOF){ ALOGE("hit file end!!\n"); return FALSE; } } if (a >= 4096){ ALOGD("get marker %x, prv %x, after (%d), L:%d!!\n", marker, prev, a, __LINE__); } Sections[SectionsRead].Type = marker; // Read the length of the section. lh = js_getc(); ll = js_getc(); itemlen = (lh << 8) | ll; if (itemlen < 2){ // ErrFatal("invalid marker"); ALOGE("invalid marker"); return FALSE; } Sections[SectionsRead].Size = itemlen; Data = (uchar *)malloc(itemlen); if (Data == NULL){ // ErrFatal("Could not allocate memory"); ALOGE("Could not allocate memory"); return 0; } Sections[SectionsRead].Data = Data; // Store first two pre-read bytes. Data[0] = (uchar)lh; Data[1] = (uchar)ll; got = js_read(Data+2, 1, itemlen-2); // Read the whole section. if (got != itemlen-2){ // ErrFatal("Premature end of file?"); ALOGE("Premature end of file?"); return FALSE; } SectionsRead += 1; printf("reading marker %d", marker); switch(marker){ case M_SOS: // stop before hitting compressed data // If reading entire image is requested, read the rest of the data. /* if (ReadMode & READ_IMAGE){ int cp, ep, size; // Determine how much file is left. cp = ftell(infile); fseek(infile, 0, SEEK_END); ep = ftell(infile); fseek(infile, cp, SEEK_SET); size = ep-cp; Data = (uchar *)malloc(size); if (Data == NULL){ // ErrFatal("could not allocate data for entire image"); ALOGE("could not allocate data for entire image"); return FALSE; } got = fread(Data, 1, size, infile); if (got != size){ // ErrFatal("could not read the rest of the image"); ALOGE("could not read the rest of the image"); return FALSE; } CheckSectionsAllocated(); Sections[SectionsRead].Data = Data; Sections[SectionsRead].Size = size; Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; SectionsRead ++; HaveAll = 1; }*/ return TRUE; case M_EOI: // in case it's a tables-only JPEG stream ALOGE("No image in jpeg!\n"); fprintf(stderr,"No image in jpeg!\n"); return FALSE; case M_COM: // Comment section if (HaveCom || ((ReadMode & READ_METADATA) == 0)){ // Discard this section. free(Sections[--SectionsRead].Data); }else{ process_COM(Data, itemlen); HaveCom = TRUE; } break; case M_JFIF: // Regular jpegs always have this tag, exif images have the exif // marker instead, althogh ACDsee will write images with both markers. // this program will re-create this marker on absence of exif marker. // hence no need to keep the copy from the file. free(Sections[--SectionsRead].Data); break; case M_EXIF: // There can be different section using the same marker. if (ReadMode & READ_METADATA){ if (memcmp(Data+2, "Exif", 4) == 0){ process_EXIF(Data, itemlen); break; }else if (memcmp(Data+2, "http:", 5) == 0){ Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes. if (ShowTags){ printf("Image cotains XMP section, %d bytes long\n", itemlen); if (ShowTags){ ShowXmp(Sections[SectionsRead-1]); } } break; } } // Oterwise, discard this section. free(Sections[--SectionsRead].Data); break; case M_IPTC: if (ReadMode & READ_METADATA){ if (ShowTags){ printf("Image cotains IPTC section, %d bytes long\n", itemlen); } // Note: We just store the IPTC section. Its relatively straightforward // and we don't act on any part of it, so just display it at parse time. }else{ free(Sections[--SectionsRead].Data); } break; case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: process_SOFn(Data, marker); break; default: // Skip any other sections. if (ShowTags){ printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen); } break; } } return TRUE; }