/*-------------------------------------------------------------------------- Process a EXIF marker Describes all the drivel that most digital cameras include... --------------------------------------------------------------------------*/ bool Cexif::process_EXIF(unsigned char * CharBuf, unsigned int length) { m_exifinfo->FlashUsed = 0; /* If it's from a digicam, and it used flash, it says so. */ m_exifinfo->Comments[0] = '\0'; /* Initial value - null string */ ExifImageWidth = 0; { /* Check the EXIF header component */ static const unsigned char ExifHeader[] = "Exif\0\0"; if (memcmp(CharBuf+0, ExifHeader,6)){ strcpy(m_szLastError,"Incorrect Exif header"); return 0; } } if (memcmp(CharBuf+6,"II",2) == 0){ MotorolaOrder = 0; }else{ if (memcmp(CharBuf+6,"MM",2) == 0){ MotorolaOrder = 1; }else{ strcpy(m_szLastError,"Invalid Exif alignment marker."); return 0; } } /* Check the next two values for correctness. */ if (Get16u(CharBuf+8) != 0x2a){ strcpy(m_szLastError,"Invalid Exif start (1)"); return 0; } int FirstOffset = Get32u(CharBuf+10); if (FirstOffset < 8 || FirstOffset > 16){ // I used to ensure this was set to 8 (website I used indicated its 8) // but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002) strcpy(m_szLastError,"Suspicious offset of first IFD value"); return 0; } unsigned char * LastExifRefd = CharBuf; /* First directory starts 16 unsigned chars in. Offsets start at 8 unsigned chars in. */ if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd)) return 0; /* This is how far the interesting (non thumbnail) part of the exif went. */ // int ExifSettingsLength = LastExifRefd - CharBuf; /* Compute the CCD width, in milimeters. */ if (m_exifinfo->FocalplaneXRes != 0){ m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes); } return 1; }
/*-------------------------------------------------------------------------- Process a EXIF marker Describes all the drivel that most digital cameras include... --------------------------------------------------------------------------*/ bool CxImageJPG::CxExifInfo::exif_process(unsigned char * CharBuf, unsigned int length, EXIFINFO* pInfo) { int ExifSettingsLength; unsigned char * LastExifRefd; pInfo->FlashUsed = 0; /* If it's from a digicam, and it used flash, it says so. */ pInfo->Comments[0] = '\0'; /* Initial value - null string */ FocalplaneXRes = 0; FocalplaneUnits = 0; ExifImageWidth = 0; { /* Check the EXIF header component */ static const unsigned char ExifHeader[] = "Exif\0\0"; if (memcmp(CharBuf+0, ExifHeader,6)){ strcpy(m_szLastError,"Incorrect Exif header"); return false; } } if (memcmp(CharBuf+6,"II",2) == 0){ MotorolaOrder = 0; }else{ if (memcmp(CharBuf+6,"MM",2) == 0){ MotorolaOrder = 1; }else{ strcpy(m_szLastError,"Invalid Exif alignment marker."); return false; } } /* Check the next two values for correctness. */ if (Get16u(CharBuf+8) != 0x2a || Get32u(CharBuf+10) != 0x08){ strcpy(m_szLastError,"Invalid Exif start (1)"); return false; } LastExifRefd = CharBuf; /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */ if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, pInfo, &LastExifRefd)) return false; /* This is how far the interesting (non thumbnail) part of the exif went. */ ExifSettingsLength = LastExifRefd - CharBuf; /* Compute the CCD width, in milimeters. */ if (FocalplaneXRes != 0){ pInfo->CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); } return true; }
//-------------------------------------------------------------------------- // Process a EXIF marker // Describes all the drivel that most digital cameras include... //-------------------------------------------------------------------------- void process_EXIF (char * CharBuf, unsigned int length) { ImageInfo.FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. FocalplaneXRes = 0; FocalplaneUnits = 0; ExifImageWidth = 0; if (ShowTags){ printf("Exif header %d bytes long\n",length); } { // Check the EXIF header component static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; if (memcmp(CharBuf+2, ExifHeader,6)){ ErrExit("Incorrect Exif header"); } } if (memcmp(CharBuf+8,"II",2) == 0){ if (ShowTags) printf("Exif section in Intel order\n"); MotorolaOrder = 0; }else{ if (memcmp(CharBuf+8,"MM",2) == 0){ if (ShowTags) printf("Exif section in Motorola order\n"); MotorolaOrder = 1; }else{ ErrExit("Invalid Exif alignment marker."); } } // Check the next two values for correctness. if (Get16u(CharBuf+10) != 0x2a || Get32u(CharBuf+12) != 0x08){ ErrExit("Invalid Exif start (1)"); } LastExifRefd = CharBuf; // First directory starts 16 bytes in. Offsets start at 8 bytes in. ProcessExifDir(CharBuf+16, CharBuf+8, length-6); // This is how far the interesting (non thumbnail) part of the exif went. ExifNonThumbnailLength = LastExifRefd - CharBuf; // Compute the CCD width, in milimeters. if (FocalplaneXRes != 0){ ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); } if (ShowTags){ printf("Thunbnail size of Exif header: %d\n",length-ExifNonThumbnailLength); } }
//-------------------------------------------------------------------------- // Process a EXIF marker // Describes all the drivel that most digital cameras include... //-------------------------------------------------------------------------- void ExifData::process_EXIF(unsigned char * CharBuf, unsigned int length) { ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. FocalplaneXRes = 0; FocalplaneUnits = 0; ExifImageWidth = 0; ExifImageLength = 0; { // Check the EXIF header component static const uchar ExifHeader[] = "Exif\0\0"; if (memcmp(CharBuf+2, ExifHeader,6)) { throw FatalError("Incorrect Exif header"); } } if (memcmp(CharBuf+8,"II",2) == 0) { // printf("Exif section in Intel order\n"); MotorolaOrder = 0; } else { if (memcmp(CharBuf+8,"MM",2) == 0) { // printf("Exif section in Motorola order\n"); MotorolaOrder = 1; } else { throw FatalError("Invalid Exif alignment marker."); } } // Check the next two values for correctness. if (Get16u(CharBuf+10) != 0x2a) { throw FatalError("Invalid Exif start (1)"); } long IFDoffset = Get32u(CharBuf+12); LastExifRefd = CharBuf; // First directory starts 16 bytes in. Offsets start at 8 bytes in. ProcessExifDir(&CharBuf[8+IFDoffset], CharBuf+8, length-6, 0); // This is how far the interesting (non thumbnail) part of the exif went. ExifSettingsLength = LastExifRefd - CharBuf; // Compute the CCD width, in milimeters. if (FocalplaneXRes != 0) { kdDebug(7034) << "ExifImageWidth " << ExifImageWidth << " FocalplaneUnits " << FocalplaneUnits << " FocalplaneXRes " << FocalplaneXRes << endl; ExifData::CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); } }
//-------------------------------------------------------------------------- // Process a EXIF marker // Describes all the drivel that most digital cameras include... //-------------------------------------------------------------------------- void ExifData::process_EXIF ( unsigned char * CharBuf, unsigned int length ) { ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. FocalplaneXRes = 0; FocalplaneUnits = 0; ExifImageWidth = 0; ExifImageLength = 0; // Check the EXIF header component static const uchar ExifHeader[] = "Exif\0\0"; if ( memcmp ( CharBuf+2, ExifHeader,6 ) ) return ; if ( memcmp ( CharBuf+8,"II",2 ) == 0 ) MotorolaOrder = 0; else { if ( memcmp ( CharBuf+8,"MM",2 ) == 0 ) MotorolaOrder = 1; else return ; } // Check the next two values for correctness. if ( Get16u ( CharBuf+10 ) != 0x2a || Get32u ( CharBuf+12 ) != 0x08 ) return ; long IFDoffset = Get32u(CharBuf+12); LastExifRefd = CharBuf; // First directory starts 16 bytes in. Offsets start at 8 bytes in. ProcessExifDir(&CharBuf[8+IFDoffset], CharBuf+8, length-6); recurseLevel--; // This is how far the interesting (non thumbnail) part of the exif went. ExifSettingsLength = LastExifRefd - CharBuf; // Compute the CCD width, in milimeters. if ( FocalplaneXRes != 0 ) { ExifData::CCDWidth = ( float ) ( ExifImageWidth * FocalplaneUnits / FocalplaneXRes ); } }
bool Cexif::process_EXIF(unsigned char * CharBuf, unsigned int length) { m_exifinfo->Comments[0] = '\0'; ExifImageWidth = 0; static const unsigned char ExifHeader[] = "Exif\0\0"; if(memcmp(CharBuf+0, ExifHeader,6)) { strcpy(m_szLastError,"Incorrect Exif header"); return false; } if (memcmp(CharBuf+6,"II",2) == 0) MotorolaOrder = 0; else { if (memcmp(CharBuf+6,"MM",2) == 0) MotorolaOrder = 1; else { strcpy(m_szLastError,"Invalid Exif alignment marker."); return false; } } if (Get16u(CharBuf+8) != 0x2a) { strcpy(m_szLastError,"Invalid Exif start (1)"); return false; } int FirstOffset = Get32u(CharBuf+10); if (FirstOffset < 8 || FirstOffset > 16) { strcpy(m_szLastError,"Suspicious offset of first IFD value"); return 0; } unsigned char * LastExifRefd = CharBuf; if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd)) return false; if (m_exifinfo->FocalplaneXRes != 0) m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes); return true; }
/*-------------------------------------------------------------------------- Process one of the nested EXIF directories. --------------------------------------------------------------------------*/ bool CxImageJPG::CxExifInfo::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength, EXIFINFO * const pInfo, unsigned char ** const LastExifRefdP ) { int de; int a; int NumDirEntries; unsigned ThumbnailOffset = 0; unsigned ThumbnailSize = 0; NumDirEntries = Get16u(DirStart); if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){ strcpy(m_szLastError,"Illegally sized directory"); return false; } for (de=0;de<NumDirEntries;de++){ int Tag, Format, Components; unsigned char * ValuePtr; /* This actually can point to a variety of things; it must be cast to other types when used. But we use it as a byte-by-byte cursor, so we declare it as a pointer to a generic byte here. */ int ByteCount; unsigned char * DirEntry; DirEntry = DirStart+2+12*de; Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { /* (-1) catches illegal zero case as unsigned underflows to positive large. */ strcpy(m_szLastError,"Illegal format code in EXIF dir"); return false; } ByteCount = Components * BytesPerFormat[Format]; if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); /* If its bigger than 4 bytes, the dir entry contains an offset.*/ if (OffsetVal+ByteCount > ExifLength){ /* Bogus pointer offset and / or bytecount value */ strcpy(m_szLastError,"Illegal pointer offset value in EXIF."); return false; } ValuePtr = OffsetBase+OffsetVal; }else{ /* 4 bytes or less and value is in the dir entry itself */ ValuePtr = DirEntry+8; } if (*LastExifRefdP < ValuePtr+ByteCount){ /* Keep track of last byte in the exif header that was actually referenced. That way, we know where the discardable thumbnail data begins. */ *LastExifRefdP = ValuePtr+ByteCount; } /* Extract useful components of tag */ switch(Tag){ case TAG_MAKE: strncpy(pInfo->CameraMake, (char*)ValuePtr, 31); break; case TAG_MODEL: strncpy(pInfo->CameraModel, (char*)ValuePtr, 39); break; case TAG_DATETIME_ORIGINAL: strncpy(pInfo->DateTime, (char*)ValuePtr, 19); break; case TAG_USERCOMMENT: /* Olympus has this padded with trailing spaces. Remove these first. */ for (a=ByteCount;;){ a--; if (((char*)ValuePtr)[a] == ' '){ ((char*)ValuePtr)[a] = '\0'; }else{ break; } if (a == 0) break; } /* Copy the comment */ if (memcmp(ValuePtr, "ASCII",5) == 0){ for (a=5;a<10;a++){ char c; c = ((char*)ValuePtr)[a]; if (c != '\0' && c != ' '){ strncpy(pInfo->Comments, (char*)ValuePtr+a, 199); break; } } }else{ strncpy(pInfo->Comments, (char*)ValuePtr, 199); } break; case TAG_FNUMBER: /* Simplest way of expressing aperture, so I trust it the most. (overwrite previously computd value if there is one) */ pInfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: /* More relevant info always comes earlier, so only use this field if we don't have appropriate aperture information yet. */ if (pInfo->ApertureFNumber == 0){ pInfo->ApertureFNumber = (float) exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); } break; case TAG_FOCALLENGTH: /* Nice digital cameras actually save the focal length as a function of how farthey are zoomed in. */ pInfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: /* Inidcates the distacne the autofocus camera is focused to. Tends to be less accurate as distance increases. */ pInfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: /* Simplest way of expressing exposure time, so I trust it most. (overwrite previously computd value if there is one) */ pInfo->ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: /* More complicated way of expressing exposure time, so only use this value if we don't already have it from somewhere else. */ if (pInfo->ExposureTime == 0){ pInfo->ExposureTime = (float) (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); } break; case TAG_FLASH: if (ConvertAnyFormat(ValuePtr, Format)){ pInfo->FlashUsed = 1; } break; case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: /* Use largest of height and width to deal with images that have been rotated to portrait format. */ a = (int)ConvertAnyFormat(ValuePtr, Format); if (ExifImageWidth < a) ExifImageWidth = a; break; case TAG_FOCALPLANEXRES: FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEUNITS: switch((int)ConvertAnyFormat(ValuePtr, Format)){ case 1: FocalplaneUnits = 25.4; break; /* 1 inch */ case 2: /* According to the information I was using, 2 means meters. But looking at the Cannon powershot's files, inches is the only sensible value. */ FocalplaneUnits = 25.4; break; case 3: FocalplaneUnits = 10; break; /* 1 centimeter*/ case 4: FocalplaneUnits = 1; break; /* 1 millimeter*/ case 5: FocalplaneUnits = .001; break; /* 1 micrometer*/ } break; /* Remaining cases contributed by: Volker C. Schoech ([email protected]) */ case TAG_EXPOSURE_BIAS: pInfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: pInfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: pInfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: pInfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_ISO_EQUIVALENT: pInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( pInfo->ISOequivalent < 50 ) pInfo->ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: pInfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_OFFSET: ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_LENGTH: ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ unsigned char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ strcpy(m_szLastError,"Illegal subdirectory link"); return false; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, pInfo, LastExifRefdP); continue; } } { /* In addition to linking to subdirectories via exif tags, there's also a potential link to another directory at the end of each directory. This has got to be the result of a committee! */ unsigned char * SubdirStart; unsigned Offset; Offset = Get16u(DirStart+2+12*NumDirEntries); if (Offset){ SubdirStart = OffsetBase + Offset; if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ strcpy(m_szLastError,"Illegal subdirectory link"); return false; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, pInfo, LastExifRefdP); } } if (ThumbnailSize && ThumbnailOffset){ if (ThumbnailSize + ThumbnailOffset <= ExifLength){ /* The thumbnail pointer appears to be valid. Store it. */ pInfo->ThumbnailPointer = OffsetBase + ThumbnailOffset; pInfo->ThumbnailSize = ThumbnailSize; } } return true; }
bool Cexif::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength, EXIFINFO * const m_exifinfo, unsigned char ** const LastExifRefdP ) { int de, a, NumDirEntries; unsigned ThumbnailOffset = 0; unsigned ThumbnailSize = 0; NumDirEntries = Get16u(DirStart); if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) { strcpy(m_szLastError,"Illegally sized directory"); return 0; } for (de=0;de<NumDirEntries;de++) { int Tag, Format, Components; unsigned char * ValuePtr; int BytesCount; unsigned char * DirEntry; DirEntry = DirStart+2+12*de; Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { strcpy(m_szLastError,"Illegal format code in EXIF dir"); return 0; } BytesCount = Components * BytesPerFormat[Format]; if (BytesCount > 4) { unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); if (OffsetVal+BytesCount > ExifLength) { strcpy(m_szLastError,"Illegal pointer offset value in EXIF."); return 0; } ValuePtr = OffsetBase+OffsetVal; } else ValuePtr = DirEntry+8; if (*LastExifRefdP < ValuePtr+BytesCount) *LastExifRefdP = ValuePtr+BytesCount; switch(Tag) { case TAG_MAKE: strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31); break; case TAG_MODEL: strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39); break; case TAG_EXIF_VERSION: strncpy(m_exifinfo->Version,(char*)ValuePtr, 4); break; case TAG_DATETIME_ORIGINAL: strncpy(m_exifinfo->DateTime, (char*)ValuePtr, 19); break; case TAG_USERCOMMENT: for (a=BytesCount;;) { a--; if (((char*)ValuePtr)[a] == ' ') ((char*)ValuePtr)[a] = '\0'; else break; if (a == 0) break; } if (memcmp(ValuePtr, "ASCII",5) == 0) { for (a=5;a<10;a++) { char c; c = ((char*)ValuePtr)[a]; if (c != '\0' && c != ' ') { strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, 199); break; } } } else strncpy(m_exifinfo->Comments, (char*)ValuePtr, 199); break; case TAG_FNUMBER: m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: if (m_exifinfo->ApertureFNumber == 0) { //m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); } break; case TAG_BRIGHTNESS: m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALLENGTH: m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: m_exifinfo->ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: if (m_exifinfo->ExposureTime == 0) { //m_exifinfo->ExposureTime = (float) (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); } break; case TAG_FLASH: if ((int)ConvertAnyFormat(ValuePtr, Format) & 7) strcpy(m_exifinfo->FlashUsed,"fire"); else strcpy(m_exifinfo->FlashUsed,"not fired"); break; case TAG_ORIENTATION: m_exifinfo->Orient = (int)ConvertAnyFormat(ValuePtr, Format); switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 1: strcpy(m_exifinfo->Orientation,"Top-Left"); break; case 2: strcpy(m_exifinfo->Orientation,"Top-Right"); break; case 3: strcpy(m_exifinfo->Orientation,"Bottom-Right"); break; case 4: strcpy(m_exifinfo->Orientation,"Bottom-Left"); break; case 5: strcpy(m_exifinfo->Orientation,"Left-Top"); break; case 6: strcpy(m_exifinfo->Orientation,"Right-Top"); break; case 7: strcpy(m_exifinfo->Orientation,"Right-Bottom"); break; case 8: strcpy(m_exifinfo->Orientation,"Left-Bottom"); break; default: strcpy(m_exifinfo->Orientation,"Undefined"); break; } break; case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: a = (int)ConvertAnyFormat(ValuePtr, Format); if (ExifImageWidth < a) ExifImageWidth = a; break; case TAG_FOCALPLANEXRES: m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEYRES: m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_RESOLUTIONUNIT: switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 2: strcpy(m_exifinfo->ResolutionUnit,"inches"); break; case 3: strcpy(m_exifinfo->ResolutionUnit,"centimeters"); break; default: strcpy(m_exifinfo->ResolutionUnit,"reserved"); } break; case TAG_FOCALPLANEUNITS: switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; case 2: m_exifinfo->FocalplaneUnits = 1.0f; break; case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; } break; case TAG_EXPOSURE_BIAS: m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 0: strcpy(m_exifinfo->LightSource,"unknown"); break; case 1: strcpy(m_exifinfo->LightSource,"Daylight"); break; case 2: strcpy(m_exifinfo->LightSource,"Fluorescent"); break; case 3: strcpy(m_exifinfo->LightSource,"Tungsten"); break; case 17: strcpy(m_exifinfo->LightSource,"Standard light A"); break; case 18: strcpy(m_exifinfo->LightSource,"Standard light B"); break; case 19: strcpy(m_exifinfo->LightSource,"Standard light C"); break; case 20: strcpy(m_exifinfo->LightSource,"D55"); break; case 21: strcpy(m_exifinfo->LightSource,"D65"); break; case 22: strcpy(m_exifinfo->LightSource,"D75"); break; default: strcpy(m_exifinfo->LightSource,"other"); break; } break; case TAG_METERING_MODE: switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 0: strcpy(m_exifinfo->MeteringMode,"unknown"); break; case 1: strcpy(m_exifinfo->MeteringMode,"Average"); break; case 2: strcpy(m_exifinfo->MeteringMode,"Center-Weighted-Average"); break; case 3: strcpy(m_exifinfo->MeteringMode,"Spot"); break; case 4: strcpy(m_exifinfo->MeteringMode,"MultiSpot"); break; case 5: strcpy(m_exifinfo->MeteringMode,"Pattern"); break; case 6: strcpy(m_exifinfo->MeteringMode,"Partial"); break; default: strcpy(m_exifinfo->MeteringMode,"other"); break; } break; case TAG_EXPOSURE_PROGRAM: switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 0: strcpy(m_exifinfo->ExposureProgram,"not defined"); break; case 1: strcpy(m_exifinfo->ExposureProgram,"Manual"); break; case 2: strcpy(m_exifinfo->ExposureProgram,"Normal program"); break; case 3: strcpy(m_exifinfo->ExposureProgram,"Aperture priority"); break; case 4: strcpy(m_exifinfo->ExposureProgram,"Shutter priority"); break; case 5: strcpy(m_exifinfo->ExposureProgram,"Creative program"); break; case 6: strcpy(m_exifinfo->ExposureProgram,"Action program"); break; case 7: strcpy(m_exifinfo->ExposureProgram,"Portrait mode"); break; case 8: strcpy(m_exifinfo->ExposureProgram,"Landscape mode"); break; default: strcpy(m_exifinfo->ExposureProgram,"reserved"); break; } break; case TAG_ISO_EQUIVALENT: m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_XRESOLUTION: m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_YRESOLUTION: m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_OFFSET: ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_LENGTH: ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) { unsigned char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { strcpy(m_szLastError,"Illegal subdirectory link"); return 0; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP); continue; } } unsigned char * SubdirStart; unsigned Offset; Offset = Get16u(DirStart+2+12*NumDirEntries); if (Offset) { SubdirStart = OffsetBase + Offset; if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { strcpy(m_szLastError,"Illegal subdirectory link"); return 0; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP); } if (ThumbnailSize && ThumbnailOffset && m_exifinfo->Thumnailstate) { if (ThumbnailSize + ThumbnailOffset <= ExifLength) { if(FILE *tf = fopen(THUMBNAILTMPFILE, "w")) { fwrite( OffsetBase + ThumbnailOffset, ThumbnailSize, 1, tf); fclose(tf); m_exifinfo->Thumnailstate = 2; } } } return 1; }
/*-------------------------------------------------------------------------- Process one of the nested EXIF directories. --------------------------------------------------------------------------*/ bool CExifRead::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned int ExifLength, unsigned char ** const LastExifRefdP ) { int de; int a; int NumDirEntries; unsigned ThumbnailOffset = 0; unsigned ThumbnailSize = 0; NumDirEntries = Get16u(DirStart); if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) { #ifdef GetErrorCode strcpy(m_szLastError,"Illegally sized directory"); #endif return 0; } for (de=0;de<NumDirEntries;de++){ int Tag, Format, Components; unsigned char * ValuePtr; /* This actually can point to a variety of things; it must be cast to other types when used. But we use it as a unsigned char-by-unsigned char cursor, so we declare it as a pointer to a generic unsigned char here. */ int BytesCount; unsigned char * DirEntry; DirEntry = DirStart+2+12*de; Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { #ifdef GetErrorCode /* (-1) catches illegal zero case as unsigned underflows to positive large */ strcpy(m_szLastError,"Illegal format code in EXIF dir"); #endif return 0; } BytesCount = Components * BytesPerFormat[Format]; if (BytesCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); /* If its bigger than 4 unsigned chars, the dir entry contains an offset.*/ if (OffsetVal+BytesCount > ExifLength) { #ifdef GetErrorCode /* Bogus pointer offset and / or unsigned charcount value */ strcpy(m_szLastError,"Illegal pointer offset value in EXIF."); #endif return 0; } ValuePtr = OffsetBase+OffsetVal; } else { /* 4 unsigned chars or less and value is in the dir entry itself */ ValuePtr = DirEntry+8; } if (*LastExifRefdP < ValuePtr+BytesCount) { /* Keep track of last unsigned char in the exif header that was actually referenced. That way, we know where the discardable thumbnail data begins. */ *LastExifRefdP = ValuePtr+BytesCount; } /* Extract useful components of tag */ switch(Tag) { case TAG_MAKE: if ( m_flags & EXIF_CAMERA_MAKER) strncpy(m_pExifInfo->CameraMake, (char*)ValuePtr, 31); break; case TAG_MODEL: if ( m_flags & EXIF_CAMERA_MODEL) strncpy_s(m_pExifInfo->CameraModel,40, (char*)ValuePtr, 39); break; case TAG_EXIF_VERSION: if ( m_flags & EXIF_VERSION) strncpy_s(m_pExifInfo->Version,5,(char*)ValuePtr, 4); break; case TAG_DATETIME_ORIGINAL: if ( m_flags & EXIF_DATA_TIME) strncpy_s(m_pExifInfo->DateTime,20, (char*)ValuePtr, 19); break; case TAG_USERCOMMENT: if ( m_flags & EXIF_USER_COMMENT) { // Olympus has this padded with trailing spaces. Remove these first. for ( a=BytesCount; ;) { a--; if (((char*)ValuePtr)[a] == ' ') { ((char*)ValuePtr)[a] = '\0'; } else { break; } if (a == 0) break; } /* Copy the comment */ if (memcmp(ValuePtr, "ASCII",5) == 0) { for (a=5;a<10;a++) { char c; c = ((char*)ValuePtr)[a]; if (c != '\0' && c != ' ') { strncpy_s(m_pExifInfo->Comments,MAX_COMMENT, (char*)ValuePtr+a, 199); break; } } }else { strncpy_s(m_pExifInfo->Comments,MAX_COMMENT, (char*)ValuePtr, 199); } } break; case TAG_FNUMBER: /* Simplest way of expressing aperture, so I trust it the most. (overwrite previously computd value if there is one) */ if ( m_flags & EXIF_APERTUREF_NUMBER) m_pExifInfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: /* More relevant info always comes earlier, so only use this field if we don't have appropriate aperture information yet. */ if ((m_flags & EXIF_APERTUREF_NUMBER) && m_pExifInfo->ApertureFNumber == 0) { m_pExifInfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5); } break; case TAG_BRIGHTNESS: if ( m_flags & EXIF_BRIGHTNESS) m_pExifInfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALLENGTH: /* Nice digital cameras actually save the focal length as a function of how farthey are zoomed in. */ if ( m_flags & EXIF_FOCAL_LENGTH) m_pExifInfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: /* Inidcates the distacne the autofocus camera is focused to. Tends to be less accurate as distance increases. */ if ( m_flags & EXIF_DISTANCE) m_pExifInfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: /* Simplest way of expressing exposure time, so I trust it most. (overwrite previously computd value if there is one) */ if ( m_flags & EXIF_EXPOSURE_TIME) m_pExifInfo->ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: /* More complicated way of expressing exposure time, so only use this value if we don't already have it from somewhere else. */ if (( m_flags & EXIF_EXPOSURE_TIME) && m_pExifInfo->ExposureTime == 0) { m_pExifInfo->ExposureTime = (float) (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0))); } break; case TAG_FLASH: if ( m_flags & EXIF_FLASH_USED) { if ((int)ConvertAnyFormat(ValuePtr, Format) & 7) { m_pExifInfo->FlashUsed = 1; } else { m_pExifInfo->FlashUsed = 0; } } break; case TAG_ORIENTATION: if ( m_flags & EXIF_ORIENTATION) { m_pExifInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format); if (m_pExifInfo->Orientation < 1 || m_pExifInfo->Orientation > 8) { #ifdef GetErrorCode strcpy(m_szLastError,"Undefined rotation value"); #endif m_pExifInfo->Orientation = 0; } } break; case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: /* Use largest of height and width to deal with images that have been rotated to portrait format. */ a = (int)ConvertAnyFormat(ValuePtr, Format); if (m_ExifImageWidth < a) m_ExifImageWidth = a; break; case TAG_FOCALPLANEXRES: if ( m_flags & EXIF_FOCALPLANE_XRES) m_pExifInfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEYRES: if ( m_flags & EXIF_FOCALPLANE_YRES) m_pExifInfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_RESOLUTIONUNIT: if ( m_flags & EXIF_RESOLUTION_UNITS) { switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 1: m_pExifInfo->ResolutionUnit = 1.0f; break; /* 1 inch */ case 2: m_pExifInfo->ResolutionUnit = 1.0f; break; case 3: m_pExifInfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/ case 4: m_pExifInfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/ case 5: m_pExifInfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/ } } break; case TAG_FOCALPLANEUNITS: if ( m_flags & EXIF_FOCALPLANE_UNITS) { switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 1: m_pExifInfo->FocalplaneUnits = 1.0f; break; /* 1 inch */ case 2: m_pExifInfo->FocalplaneUnits = 1.0f; break; case 3: m_pExifInfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/ case 4: m_pExifInfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/ case 5: m_pExifInfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/ } } break; // Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de> case TAG_EXPOSURE_BIAS: if ( m_flags & EXIF_EXPOSURE_BIAS) m_pExifInfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: if ( m_flags & EXIF_WHITE_BALANCE) m_pExifInfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: if ( m_flags & EXIF_METERING_MODE) m_pExifInfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: if ( m_flags & EXIF_EXPOSURE_PROGRAM) m_pExifInfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_ISO_EQUIVALENT: if ( m_flags & EXIF_ISO_EAUIVALENT) { m_pExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( m_pExifInfo->ISOequivalent < 50 ) m_pExifInfo->ISOequivalent *= 200; } break; case TAG_COMPRESSION_LEVEL: if ( m_flags & EXIF_COMPRESSION_LEVEL) m_pExifInfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_XRESOLUTION: if ( m_flags & EXIF_XRESOLUTION) m_pExifInfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_YRESOLUTION: if ( m_flags & EXIF_YRESOLUTION) m_pExifInfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_OFFSET: ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_LENGTH: ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET ) { unsigned char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if ( SubdirStart < OffsetBase || SubdirStart > OffsetBase + ExifLength ) { #ifdef GetErrorCode strcpy(m_szLastError,"Illegal subdirectory link"); #endif return 0; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, LastExifRefdP); continue; } } { /* In addition to linking to subdirectories via exif tags, there's also a potential link to another directory at the end of each directory. This has got to be the result of a committee! */ unsigned char * SubdirStart; unsigned Offset; Offset = Get32u(DirStart+2+12*NumDirEntries); if (Offset > 0) { SubdirStart = OffsetBase + Offset; if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { #ifdef GetErrorCode strcpy(m_szLastError,"Illegal subdirectory link"); #endif return 0; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, LastExifRefdP); } } if (ThumbnailSize && ThumbnailOffset) { if (ThumbnailSize + ThumbnailOffset <= ExifLength) { if ( m_pExifInfo->pThumbnailPointer != NULL) { delete m_pExifInfo->pThumbnailPointer; } /* The thumbnail pointer appears to be valid. Store it. */ m_pExifInfo->pThumbnailPointer = OffsetBase + ThumbnailOffset; m_pExifInfo->ThumbnailOffset = ThumbnailOffset; m_pExifInfo->ThumbnailSize = ThumbnailSize; } } return 1; }
//-------------------------------------------------------------------------- // Process one of the nested EXIF directories. //-------------------------------------------------------------------------- void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength, unsigned NestingLevel) { int de; int a; int NumDirEntries; unsigned ThumbnailOffset = 0; unsigned ThumbnailSize = 0; if ( NestingLevel > 4) throw FatalError("Maximum directory nesting exceeded (corrupt exif header)"); NumDirEntries = Get16u(DirStart); #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) { unsigned char * DirEnd; DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); if (DirEnd+4 > (OffsetBase+ExifLength)) { if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength) { // Version 1.3 of jhead would truncate a bit too much. // This also caught later on as well. } else { // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier // might trigger this. throw FatalError("Illegally sized directory"); } } if (DirEnd < LastExifRefd) LastExifRefd = DirEnd; } for (de=0; de<NumDirEntries; de++) { int Tag, Format, Components; unsigned char * ValuePtr; unsigned ByteCount; char * DirEntry; DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de); Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. throw FatalError("Illegal format code in EXIF dir"); } if ((unsigned)Components > 0x10000) { throw FatalError("Illegal number of components for tag"); continue; } ByteCount = Components * BytesPerFormat[Format]; if (ByteCount > 4) { unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength) { // Bogus pointer offset and / or bytecount value //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); throw FatalError("Illegal pointer offset value in EXIF"); } ValuePtr = OffsetBase+OffsetVal; } else { // 4 bytes or less and value is in the dir entry itself ValuePtr = (unsigned char *)DirEntry+8; } if (LastExifRefd < ValuePtr+ByteCount) { // Keep track of last byte in the exif header that was actually referenced. // That way, we know where the discardable thumbnail data begins. LastExifRefd = ValuePtr+ByteCount; } // Extract useful components of tag switch(Tag) { case TAG_MAKE: ExifData::CameraMake = QString::fromLatin1((const char*)ValuePtr, 31); break; case TAG_MODEL: ExifData::CameraModel = QString::fromLatin1((const char*)ValuePtr, 39); break; case TAG_ORIENTATION: Orientation = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_DATETIME_ORIGINAL: DateTime = QString::fromLatin1((const char*)ValuePtr, 19); break; case TAG_USERCOMMENT: // Olympus has this padded with trailing spaces. Remove these first. for (a=ByteCount;;) { a--; if ((ValuePtr)[a] == ' ') { (ValuePtr)[a] = '\0'; } else { break; } if (a == 0) break; } // Copy the comment if (memcmp(ValuePtr, "ASCII",5) == 0) { for (a=5; a<10; a++) { int c; c = (ValuePtr)[a]; if (c != '\0' && c != ' ') { UserComment = QString::fromLatin1((const char*)(a+ValuePtr), 199); break; } } } else { UserComment = QString::fromLatin1((const char*)ValuePtr, 199); } break; case TAG_FNUMBER: // Simplest way of expressing aperture, so I trust it the most. // (overwrite previously computd value if there is one) ExifData::ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: // More relevant info always comes earlier, so only use this field if we don't // have appropriate aperture information yet. if (ExifData::ApertureFNumber == 0) { ExifData::ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5); } break; case TAG_FOCALLENGTH: // Nice digital cameras actually save the focal length as a function // of how far they are zoomed in. ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: // Inidcates the distacne the autofocus camera is focused to. // Tends to be less accurate as distance increases. ExifData::Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: // Simplest way of expressing exposure time, so I trust it most. // (overwrite previously computd value if there is one) ExifData::ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: // More complicated way of expressing exposure time, so only use // this value if we don't already have it from somewhere else. if (ExifData::ExposureTime == 0) { ExifData::ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0))); } break; case TAG_FLASH: ExifData::FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXIF_IMAGELENGTH: ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXIF_IMAGEWIDTH: ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEXRES: FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEUNITS: switch((int)ConvertAnyFormat(ValuePtr, Format)) { case 1: FocalplaneUnits = 25.4; break; // inch case 2: // According to the information I was using, 2 means meters. // But looking at the Cannon powershot's files, inches is the only // sensible value. FocalplaneUnits = 25.4; break; case 3: FocalplaneUnits = 10; break; // centimeter case 4: FocalplaneUnits = 1; break; // milimeter case 5: FocalplaneUnits = .001; break; // micrometer } break; // Remaining cases contributed by: Volker C. Schoech ([email protected]) case TAG_EXPOSURE_BIAS: ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_ISO_EQUIVALENT: ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_OFFSET: ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_LENGTH: ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) { unsigned char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart <= OffsetBase || SubdirStart >= OffsetBase+ExifLength) { throw FatalError("Illegal subdirectory link"); } ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1); continue; } } { // In addition to linking to subdirectories via exif tags, // there's also a potential link to another directory at the end of each // directory. this has got to be the result of a comitee! unsigned char * SubdirStart; unsigned Offset; if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength) { Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries)); // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT. // Adding OffsetBase to it produces an overflow, so compare with ExifLength here. // See http://bugs.kde.org/show_bug.cgi?id=54542 if (Offset && Offset < ExifLength) { SubdirStart = OffsetBase + Offset; if (SubdirStart > OffsetBase+ExifLength) { if (SubdirStart < OffsetBase+ExifLength+20) { // Jhead 1.3 or earlier would crop the whole directory! // As Jhead produces this form of format incorrectness, // I'll just let it pass silently kdDebug(7034) << "Thumbnail removed with Jhead 1.3 or earlier\n"; } else { throw FatalError("Illegal subdirectory link 2"); } } else { if (SubdirStart <= OffsetBase+ExifLength) { ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1); } } } } else { // The exif header ends before the last next directory pointer. } } if (ThumbnailSize && ThumbnailOffset) { if (ThumbnailSize + ThumbnailOffset < ExifLength) { // The thumbnail pointer appears to be valid. Store it. Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG"); } } }
//-------------------------------------------------------------------------- // Process one of the nested EXIF directories. //-------------------------------------------------------------------------- static void ProcessExifDir(char * DirStart, char * OffsetBase, unsigned ExifLength) { int de; int a; int NumDirEntries; NumDirEntries = Get16u(DirStart); if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){ ErrExit("Illegally sized directory"); } if (ShowTags){ printf("Directory with %d entries\n",NumDirEntries); } for (de=0;de<NumDirEntries;de++){ int Tag, Format, Components; char * ValuePtr; int ByteCount; char * DirEntry; DirEntry = DirStart+2+12*de; Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrExit("Illegal format code in EXIF dir"); } ByteCount = Components * BytesPerFormat[Format]; if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); ErrExit("Illegal pointer offset value in EXIF"); } ValuePtr = OffsetBase+OffsetVal; }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } if (LastExifRefd < ValuePtr+ByteCount){ // Keep track of last byte in the exif header that was actually referenced. // That way, we know where the discardable thumbnail data begins. LastExifRefd = ValuePtr+ByteCount; } if (ShowTags){ // Show tag name for (a=0;;a++){ if (TagTable[a].Tag == 0){ printf(" Unknown Tag %04x Value = ", Tag); break; } if (TagTable[a].Tag == Tag){ printf(" %s = ",TagTable[a].Desc); break; } } // Show tag value. switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) printf("\""); for (a=0;a<ByteCount;a++){ if (isprint((ValuePtr)[a])){ putchar((ValuePtr)[a]); } } printf("\"\n"); break; default: // Handle arrays of numbers later (will there ever be?) PrintFormatNumber(ValuePtr, Format); } } // Extract useful components of tag switch(Tag){ case TAG_MAKE: strncpy(ImageInfo.CameraMake, ValuePtr, 31); break; case TAG_MODEL: strncpy(ImageInfo.CameraModel, ValuePtr, 39); break; case TAG_DATETIME_ORIGINAL: strncpy(ImageInfo.DateTime, ValuePtr, 19); break; case TAG_USERCOMMENT: // Olympus has this padded with trailing spaces. Remove these first. for (a=ByteCount;;){ a--; if ((ValuePtr)[a] == ' '){ (ValuePtr)[a] = '\0'; }else{ break; } if (a == 0) break; } // Copy the comment if (memcmp(ValuePtr, "ASCII",5) == 0){ for (a=5;a<10;a++){ int c; c = (ValuePtr)[a]; if (c != '\0' && c != ' '){ strncpy(ImageInfo.Comments, a+ValuePtr, 199); break; } } }else{ strncpy(ImageInfo.Comments, ValuePtr, 199); } break; case TAG_FNUMBER: // Simplest way of expressing aperture, so I trust it the most. // (overwrite previously computd value if there is one) ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_APERTURE: case TAG_MAXAPERTURE: // More relevant info always comes earlier, so only use this field if we don't // have appropriate aperture information yet. if (ImageInfo.ApertureFNumber == 0){ ImageInfo.ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); } break; case TAG_FOCALLENGTH: // Nice digital cameras actually save the focal length as a function // of how farthey are zoomed in. ImageInfo.FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SUBJECT_DISTANCE: // Inidcates the distacne the autofocus camera is focused to. // Tends to be less accurate as distance increases. ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURETIME: // Simplest way of expressing exposure time, so I trust it most. // (overwrite previously computd value if there is one) ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_SHUTTERSPEED: // More complicated way of expressing exposure time, so only use // this value if we don't already have it from somewhere else. if (ImageInfo.ExposureTime == 0){ ImageInfo.ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); } break; case TAG_FLASH: if (ConvertAnyFormat(ValuePtr, Format)){ ImageInfo.FlashUsed = 1; } break; case TAG_EXIF_IMAGELENGTH: case TAG_EXIF_IMAGEWIDTH: // Use largest of height and width to deal with images that have been // rotated to portrait format. a = (int)ConvertAnyFormat(ValuePtr, Format); if (ExifImageWidth < a) ExifImageWidth = a; break; case TAG_FOCALPLANEXRES: FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); break; case TAG_FOCALPLANEUNITS: switch((int)ConvertAnyFormat(ValuePtr, Format)){ case 1: FocalplaneUnits = 25.4; break; // inch case 2: // According to the information I was using, 2 means meters. // But looking at the Cannon powershot's files, inches is the only // sensible value. FocalplaneUnits = 25.4; break; case 3: FocalplaneUnits = 10; break; // centimeter case 4: FocalplaneUnits = 1; break; // milimeter case 5: FocalplaneUnits = .001; break; // micrometer } break; // Remaining cases contributed by: Volker C. Schoech ([email protected]) case TAG_EXPOSURE_BIAS: ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); break; case TAG_WHITEBALANCE: ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_METERING_MODE: ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_EXPOSURE_PROGRAM: ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_ISO_EQUIVALENT: ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( ImageInfo.ISOequivalent < 80 ) ImageInfo.ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: ImageInfo.CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ ErrExit("Illegal subdirectory link"); } ProcessExifDir(SubdirStart, OffsetBase, ExifLength); continue; } } }