diff options
Diffstat (limited to 'converter/other/exif.c')
-rw-r--r-- | converter/other/exif.c | 1197 |
1 files changed, 559 insertions, 638 deletions
diff --git a/converter/other/exif.c b/converter/other/exif.c index 1bfe4b2b..87d89bd7 100644 --- a/converter/other/exif.c +++ b/converter/other/exif.c @@ -46,21 +46,22 @@ #include "exif.h" -static const unsigned char * DirWithThumbnailPtrs; +static unsigned char * LastExifRefd; +static unsigned char * DirWithThumbnailPtrs; static double FocalplaneXRes; bool HaveXRes; static double FocalplaneUnits; static int ExifImageWidth; +static int MotorolaOrder = 0; typedef struct { unsigned short Tag; const char * Desc; -} TagTable; - +}TagTable_t; /* Describes format descriptor */ -static int const bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; +static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; #define NUM_FORMATS 12 #define FMT_BYTE 1 @@ -119,7 +120,7 @@ static int const bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; #define TAG_THUMBNAIL_OFFSET 0x0201 #define TAG_THUMBNAIL_LENGTH 0x0202 -static TagTable const tagTable[] = { +static TagTable_t const TagTable[] = { { 0x100, "ImageWidth"}, { 0x101, "ImageLength"}, { 0x102, "BitsPerSample"}, @@ -207,583 +208,498 @@ static TagTable const tagTable[] = { -typedef enum { NORMAL, MOTOROLA } ByteOrder; - - - -static uint16_t -get16u(const void * const data, - ByteOrder const byteOrder) { /*-------------------------------------------------------------------------- Convert a 16 bit unsigned value from file's native byte order --------------------------------------------------------------------------*/ - if (byteOrder == MOTOROLA){ - return (((const unsigned char *)data)[0] << 8) | - ((const unsigned char *)data)[1]; +static int Get16u(void * Short) +{ + if (MotorolaOrder){ + return (((unsigned char *)Short)[0] << 8) | + ((unsigned char *)Short)[1]; }else{ - return (((const unsigned char *)data)[1] << 8) | - ((const unsigned char *)data)[0]; + return (((unsigned char *)Short)[1] << 8) | + ((unsigned char *)Short)[0]; } } - - -static int32_t -get32s(const void * const data, - ByteOrder const byteOrder) { /*-------------------------------------------------------------------------- Convert a 32 bit signed value from file's native byte order --------------------------------------------------------------------------*/ - if (byteOrder == MOTOROLA){ +static int Get32s(void * Long) +{ + if (MotorolaOrder){ return - (((const char *)data)[0] << 24) | - (((const unsigned char *)data)[1] << 16) | - (((const unsigned char *)data)[2] << 8 ) | - (((const unsigned char *)data)[3] << 0 ); - } else { + ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16) | + (((unsigned char *)Long)[2] << 8 ) | + (((unsigned char *)Long)[3] << 0 ); + }else{ return - (((const char *)data)[3] << 24) | - (((const unsigned char *)data)[2] << 16) | - (((const unsigned char *)data)[1] << 8 ) | - (((const unsigned char *)data)[0] << 0 ); + ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16) | + (((unsigned char *)Long)[1] << 8 ) | + (((unsigned char *)Long)[0] << 0 ); } } - - -static uint32_t -get32u(const void * const data, - ByteOrder const byteOrder) { /*-------------------------------------------------------------------------- Convert a 32 bit unsigned value from file's native byte order --------------------------------------------------------------------------*/ - return (uint32_t)get32s(data, byteOrder) & 0xffffffff; +static unsigned Get32u(void * Long) +{ + return (unsigned)Get32s(Long) & 0xffffffff; } - - -static void -printFormatNumber(FILE * const fileP, - const void * const ValuePtr, - int const Format, - int const ByteCount, - ByteOrder const byteOrder) { /*-------------------------------------------------------------------------- Display a number as one of its many formats --------------------------------------------------------------------------*/ +static void PrintFormatNumber(FILE * const file, + void * const ValuePtr, + int const Format, int const ByteCount) +{ switch(Format){ case FMT_SBYTE: - case FMT_BYTE: - fprintf(fileP, "%02x\n", *(unsigned char *)ValuePtr); - break; - case FMT_USHORT: - fprintf(fileP, "%d\n",get16u(ValuePtr, byteOrder)); - break; + case FMT_BYTE: printf("%02x\n",*(unsigned char *)ValuePtr); break; + case FMT_USHORT: fprintf(file, "%d\n",Get16u(ValuePtr)); break; case FMT_ULONG: - case FMT_SLONG: - fprintf(fileP, "%d\n",get32s(ValuePtr, byteOrder)); - break; + case FMT_SLONG: fprintf(file, "%d\n",Get32s(ValuePtr)); break; case FMT_SSHORT: - fprintf(fileP, "%hd\n",(signed short)get16u(ValuePtr, byteOrder)); - break; + fprintf(file, "%hd\n",(signed short)Get16u(ValuePtr)); break; case FMT_URATIONAL: case FMT_SRATIONAL: - fprintf(fileP, "%d/%d\n",get32s(ValuePtr, byteOrder), - get32s(4+(char *)ValuePtr, byteOrder)); + fprintf(file, "%d/%d\n",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr)); break; case FMT_SINGLE: - fprintf(fileP, "%f\n",(double)*(float *)ValuePtr); - break; - case FMT_DOUBLE: - fprintf(fileP, "%f\n",*(double *)ValuePtr); - break; + fprintf(file, "%f\n",(double)*(float *)ValuePtr); break; + case FMT_DOUBLE: fprintf(file, "%f\n",*(double *)ValuePtr); break; default: - fprintf(fileP, "Unknown format %d:", Format); + fprintf(file, "Unknown format %d:", Format); { - unsigned int a; - for (a = 0; a < ByteCount && a < 16; ++a) + int a; + for (a=0; a < ByteCount && a < 16; ++a) printf("%02x", ((unsigned char *)ValuePtr)[a]); } - fprintf(fileP, "\n"); + fprintf(file, "\n"); } } - -static double -convertAnyFormat(const void * const ValuePtr, - int const Format, - ByteOrder const byteOrder) { /*-------------------------------------------------------------------------- Evaluate number, be it int, rational, or float from directory. --------------------------------------------------------------------------*/ +static double ConvertAnyFormat(void * ValuePtr, int Format) +{ double Value; Value = 0; switch(Format){ - case FMT_SBYTE: - Value = *(signed char *)ValuePtr; - break; - case FMT_BYTE: - Value = *(unsigned char *)ValuePtr; - break; - case FMT_USHORT: - Value = get16u(ValuePtr, byteOrder); - break; - case FMT_ULONG: - Value = get32u(ValuePtr, byteOrder); - break; - case FMT_URATIONAL: - case FMT_SRATIONAL: { - int num, den; - num = get32s(ValuePtr, byteOrder); - den = get32s(4+(char *)ValuePtr, byteOrder); - Value = den == 0 ? 0 : (double)(num/den); - } break; - case FMT_SSHORT: - Value = (signed short)get16u(ValuePtr, byteOrder); - break; - case FMT_SLONG: - Value = get32s(ValuePtr, byteOrder); - break; - - /* Not sure if this is correct (never seen float used in Exif format) */ - case FMT_SINGLE: - Value = (double)*(float *)ValuePtr; - break; - case FMT_DOUBLE: - Value = *(double *)ValuePtr; - break; - } - return Value; -} - - - -static void -traceTag(int const tag, - int const format, - const unsigned char * const valuePtr, - unsigned int const byteCount, - ByteOrder const byteOrder) { - - /* Show tag name */ - unsigned int a; - bool found; - for (a = 0, found = false; !found; ++a){ - if (tagTable[a].Tag == 0){ - fprintf(stderr, " Unknown Tag %04x Value = ", tag); - found = true; - } - if (tagTable[a].Tag == tag){ - fprintf(stderr, " %s = ",tagTable[a].Desc); - found = true; - } - } - - /* 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) - */ - bool noPrint; - printf("\""); - for (a = 0, noPrint = false; a < byteCount; ++a){ - if (ISPRINT((valuePtr)[a])){ - fprintf(stderr, "%c", valuePtr[a]); - noPrint = false; - } else { - /* Avoiding indicating too many unprintable characters of - proprietary bits of binary information this program may not - know how to parse. - */ - if (!noPrint){ - fprintf(stderr, "?"); - noPrint = true; + case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; + case FMT_BYTE: Value = *(unsigned char *)ValuePtr; break; + + case FMT_USHORT: Value = Get16u(ValuePtr); break; + case FMT_ULONG: Value = Get32u(ValuePtr); break; + + case FMT_URATIONAL: + case FMT_SRATIONAL: + { + int Num,Den; + Num = Get32s(ValuePtr); + Den = Get32s(4+(char *)ValuePtr); + if (Den == 0){ + Value = 0; + }else{ + Value = (double)Num/Den; } + break; } - } - fprintf(stderr, "\"\n"); - } break; - default: - /* Handle arrays of numbers later (will there ever be?)*/ - printFormatNumber(stderr, valuePtr, format, byteCount, byteOrder); + case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break; + case FMT_SLONG: Value = Get32s(ValuePtr); break; + + /* Not sure if this is correct (never seen float used in Exif format) + */ + case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; + case FMT_DOUBLE: Value = *(double *)ValuePtr; break; } + return Value; } - - -/* Forward declaration for recursion */ - +/*-------------------------------------------------------------------------- + Process one of the nested EXIF directories. +--------------------------------------------------------------------------*/ static void -processExifDir(const unsigned char * const ExifData, - unsigned int const ExifLength, - unsigned int const DirOffset, - exif_ImageInfo * const imageInfoP, - ByteOrder const byteOrder, - bool const wantTagTrace, - const unsigned char ** const LastExifRefdP); - - -static void -processDirEntry(const unsigned char * const dirEntry, - const unsigned char * const exifData, - unsigned int const exifLength, - ByteOrder const byteOrder, - bool const wantTagTrace, - exif_ImageInfo * const imageInfoP, - unsigned int * const thumbnailOffsetP, - unsigned int * const thumbnailSizeP, - bool * const haveThumbnailP, - const unsigned char ** const lastExifRefdP) { - - int const tag = get16u(&dirEntry[0], byteOrder); - int const format = get16u(&dirEntry[2], byteOrder); - int const components = get32u(&dirEntry[4], byteOrder); - - const 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. - */ - unsigned int byteCount; +ProcessExifDir(unsigned char * const ExifData, + unsigned int const ExifLength, + unsigned int const DirOffset, + ImageInfo_t * const ImageInfoP, + int const ShowTags, + unsigned char ** const LastExifRefdP) { + + unsigned char * const DirStart = ExifData + DirOffset; + int de; + int a; + int NumDirEntries; + unsigned ThumbnailOffset = 0; + unsigned ThumbnailSize = 0; + + NumDirEntries = Get16u(DirStart); + #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) - if ((format-1) >= NUM_FORMATS) { - /* (-1) catches illegal zero case as unsigned underflows - to positive large. - */ - pm_message("Illegal number format %d for tag %04x", format, tag); - return; - } - - byteCount = components * bytesPerFormat[format]; - - if (byteCount > 4){ - unsigned const offsetVal = get32u(&dirEntry[8], byteOrder); - /* If its bigger than 4 bytes, the dir entry contains an offset.*/ - if (offsetVal + byteCount > exifLength){ - /* Bogus pointer offset and / or bytecount value */ - pm_message("Illegal pointer offset value in EXIF " - "for tag %04x. " - "Offset %d bytes %d ExifLen %d\n", - tag, offsetVal, byteCount, exifLength); - return; + { + unsigned char * DirEnd; + DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); + if (DirEnd+4 > (ExifData+ExifLength)){ + if (DirEnd+2 == ExifData+ExifLength || + DirEnd == ExifData+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. + */ + pm_message("Illegal directory entry size"); + return; + } } - valuePtr = &exifData[offsetVal]; - } else { - /* 4 bytes or less and value is in the dir entry itself */ - valuePtr = &dirEntry[8]; + if (DirEnd > LastExifRefd) LastExifRefd = DirEnd; } - 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; + if (ShowTags){ + pm_message("Directory with %d entries",NumDirEntries); } - if (wantTagTrace) - traceTag(tag, format, valuePtr, byteCount, byteOrder); - - *haveThumbnailP = (tag == TAG_THUMBNAIL_OFFSET); - - /* Extract useful components of tag */ - switch (tag){ - - case TAG_MAKE: - STRSCPY(imageInfoP->CameraMake, (const char*)valuePtr); - break; - - case TAG_MODEL: - STRSCPY(imageInfoP->CameraModel, (const char*)valuePtr); - break; - - case TAG_XRESOLUTION: - imageInfoP->XResolution = - convertAnyFormat(valuePtr, format, byteOrder); - break; - - case TAG_YRESOLUTION: - imageInfoP->YResolution = - convertAnyFormat(valuePtr, format, byteOrder); - break; - - case TAG_DATETIME_ORIGINAL: - STRSCPY(imageInfoP->DateTime, (const char*)valuePtr); - imageInfoP->DatePointer = (const char*)valuePtr; - break; - - case TAG_USERCOMMENT: { - /* Olympus has this padded with trailing spaces. We stop the copy - where those start. - */ - const char * const value = (const char *)valuePtr; - - unsigned int cursor; - unsigned int outCursor; - unsigned int end; - - for (end = byteCount; end > 0 && value[end] == ' '; --end); - - /* Skip "ASCII" if it is there */ - if (end >= 5 && MEMEQ(value, "ASCII", 5)) - cursor = 5; - else - cursor = 0; - - /* Skip consecutive blanks and NULs */ - - for (; - cursor < byteCount && - (value[cursor] == '\0' || value[cursor] == ' '); - ++cursor); - - /* Copy the rest as the comment */ - - for (outCursor = 0; - cursor < end && outCursor < MAX_COMMENT-1; - ++cursor) - imageInfoP->Comments[outCursor++] = value[cursor]; - - imageInfoP->Comments[outCursor++] = '\0'; - } break; - - case TAG_FNUMBER: - /* Simplest way of expressing aperture, so I trust it the most. - (overwrite previously computd value if there is one) - */ - imageInfoP->ApertureFNumber = - (float)convertAnyFormat(valuePtr, format, byteOrder); - 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 (imageInfoP->ApertureFNumber == 0){ - imageInfoP->ApertureFNumber = (float) - exp(convertAnyFormat(valuePtr, format, byteOrder) - * 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. - */ - - imageInfoP->FocalLength = - (float)convertAnyFormat(valuePtr, format, byteOrder); - break; - - case TAG_SUBJECT_DISTANCE: - /* Inidcates the distacne the autofocus camera is focused to. - Tends to be less accurate as distance increases. - */ - imageInfoP->Distance = - (float)convertAnyFormat(valuePtr, format, byteOrder); - break; + 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 = DIR_ENTRY_ADDR(DirStart, de); - case TAG_EXPOSURETIME: - /* Simplest way of expressing exposure time, so I - trust it most. (overwrite previously computd value - if there is one) - */ - imageInfoP->ExposureTime = - (float)convertAnyFormat(valuePtr, format, byteOrder); - break; + Tag = Get16u(DirEntry); + Format = Get16u(DirEntry+2); + Components = Get32u(DirEntry+4); - 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 (imageInfoP->ExposureTime == 0){ - imageInfoP->ExposureTime = (float) - (1/exp(convertAnyFormat(valuePtr, format, byteOrder) - * log(2))); + if ((Format-1) >= NUM_FORMATS) { + /* (-1) catches illegal zero case as unsigned underflows + to positive large. + */ + pm_message("Illegal number format %d for tag %04x", Format, Tag); + continue; } - break; - - case TAG_FLASH: - if ((int)convertAnyFormat(valuePtr, format, byteOrder) & 0x7){ - imageInfoP->FlashUsed = TRUE; + + 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 */ + pm_message("Illegal pointer offset value in EXIF " + "for tag %04x. " + "Offset %d bytes %d ExifLen %d\n", + Tag, OffsetVal, ByteCount, ExifLength); + continue; + } + ValuePtr = ExifData+OffsetVal; }else{ - imageInfoP->FlashUsed = FALSE; - } - break; - - case TAG_ORIENTATION: - imageInfoP->Orientation = - (int)convertAnyFormat(valuePtr, format, byteOrder); - if (imageInfoP->Orientation < 1 || - imageInfoP->Orientation > 8){ - pm_message("Undefined rotation value %d", - imageInfoP->Orientation); - imageInfoP->Orientation = 0; + /* 4 bytes or less and value is in the dir entry itself */ + ValuePtr = DirEntry+8; } - 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. - */ - ExifImageWidth = - MIN(ExifImageWidth, - (int)convertAnyFormat(valuePtr, format, byteOrder)); - break; - - case TAG_FOCALPLANEXRES: - HaveXRes = TRUE; - FocalplaneXRes = convertAnyFormat(valuePtr, format, byteOrder); - break; - case TAG_FOCALPLANEUNITS: - switch((int)convertAnyFormat(valuePtr, format, byteOrder)){ - 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. + 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. */ - 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*/ + *LastExifRefdP = ValuePtr+ByteCount; } - break; - - /* Remaining cases contributed by: Volker C. Schoech - (schoech@gmx.de) - */ - - case TAG_EXPOSURE_BIAS: - imageInfoP->ExposureBias = - (float) convertAnyFormat(valuePtr, format, byteOrder); - break; - case TAG_WHITEBALANCE: - imageInfoP->Whitebalance = - (int)convertAnyFormat(valuePtr, format, byteOrder); - break; - - case TAG_METERING_MODE: - imageInfoP->MeteringMode = - (int)convertAnyFormat(valuePtr, format, byteOrder); - break; + if (ShowTags){ + /* Show tag name */ + for (a=0;;a++){ + if (TagTable[a].Tag == 0){ + fprintf(stderr, " Unknown Tag %04x Value = ", Tag); + break; + } + if (TagTable[a].Tag == Tag){ + fprintf(stderr, " %s = ",TagTable[a].Desc); + break; + } + } - case TAG_EXPOSURE_PROGRAM: - imageInfoP->ExposureProgram = - (int)convertAnyFormat(valuePtr, format, byteOrder); - 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) + */ + { + int NoPrint = 0; + printf("\""); + for (a=0;a<ByteCount;a++){ + if (ISPRINT((ValuePtr)[a])){ + fprintf(stderr, "%c", (ValuePtr)[a]); + NoPrint = 0; + }else{ + + /* Avoiding indicating too many + unprintable characters of proprietary + bits of binary information this + program may not know how to parse. + */ + if (!NoPrint){ + fprintf(stderr, "?"); + NoPrint = 1; + } + } + } + fprintf(stderr, "\"\n"); + } + break; - case TAG_ISO_EQUIVALENT: - imageInfoP->ISOequivalent = - (int)convertAnyFormat(valuePtr, format, byteOrder); - if ( imageInfoP->ISOequivalent < 50 ) - imageInfoP->ISOequivalent *= 200; - break; + default: + /* Handle arrays of numbers later (will there ever be?)*/ + PrintFormatNumber(stderr, ValuePtr, Format, ByteCount); + } + } - case TAG_COMPRESSION_LEVEL: - imageInfoP->CompressionLevel = - (int)convertAnyFormat(valuePtr, format, byteOrder); - break; + /* Extract useful components of tag */ + switch(Tag){ + + case TAG_MAKE: + STRSCPY(ImageInfoP->CameraMake, (char*)ValuePtr); + break; + + case TAG_MODEL: + STRSCPY(ImageInfoP->CameraModel, (char*)ValuePtr); + break; + + case TAG_XRESOLUTION: + ImageInfoP->XResolution = + ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_YRESOLUTION: + ImageInfoP->YResolution = + ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_DATETIME_ORIGINAL: + STRSCPY(ImageInfoP->DateTime, (char*)ValuePtr); + ImageInfoP->DatePointer = (char*)ValuePtr; + 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; + } - case TAG_THUMBNAIL_OFFSET: - *thumbnailOffsetP = (unsigned int) - convertAnyFormat(valuePtr, format, byteOrder); - 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(ImageInfoP->Comments, (char*)ValuePtr+a, + 199); + break; + } + } + + }else{ + strncpy(ImageInfoP->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) + */ + ImageInfoP->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 (ImageInfoP->ApertureFNumber == 0){ + ImageInfoP->ApertureFNumber = (float) + exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); + } + break; - case TAG_THUMBNAIL_LENGTH: - *thumbnailSizeP = (unsigned int) - convertAnyFormat(valuePtr, format, byteOrder); - break; + case TAG_FOCALLENGTH: + /* Nice digital cameras actually save the focal length + as a function of how farthey are zoomed in. + */ - case TAG_EXIF_OFFSET: - case TAG_INTEROP_OFFSET: { - unsigned int const subdirOffset = get32u(valuePtr, byteOrder); - if (subdirOffset >= exifLength) - pm_message("Illegal exif or interop offset " - "directory link. Offset is %u, " - "but Exif data is only %u bytes.", - subdirOffset, exifLength); - else - processExifDir(exifData, exifLength, subdirOffset, - imageInfoP, byteOrder, wantTagTrace, - lastExifRefdP); - } break; - } -} + ImageInfoP->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. + */ + ImageInfoP->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) + */ + ImageInfoP->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 (ImageInfoP->ExposureTime == 0){ + ImageInfoP->ExposureTime = (float) + (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); + } + break; + case TAG_FLASH: + if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){ + ImageInfoP->FlashUsed = TRUE; + }else{ + ImageInfoP->FlashUsed = FALSE; + } + break; + + case TAG_ORIENTATION: + ImageInfoP->Orientation = + (int)ConvertAnyFormat(ValuePtr, Format); + if (ImageInfoP->Orientation < 1 || + ImageInfoP->Orientation > 8){ + pm_message("Undefined rotation value %d", + ImageInfoP->Orientation); + ImageInfoP->Orientation = 0; + } + break; -static void -processExifDir(const unsigned char * const exifData, - unsigned int const exifLength, - unsigned int const dirOffset, - exif_ImageInfo * const imageInfoP, - ByteOrder const byteOrder, - bool const wantTagTrace, - const unsigned char ** const lastExifRefdP) { -/*-------------------------------------------------------------------------- - Process one of the nested EXIF directories. ---------------------------------------------------------------------------*/ - const unsigned char * const dirStart = exifData + dirOffset; - unsigned int const numDirEntries = get16u(&dirStart[0], byteOrder); - unsigned int de; - bool haveThumbnail; - unsigned int thumbnailOffset; - unsigned int thumbnailSize; + 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: + HaveXRes = TRUE; + 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; - #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) + case 3: FocalplaneUnits = 10; break; /* 1 centimeter*/ + case 4: FocalplaneUnits = 1; break; /* 1 millimeter*/ + case 5: FocalplaneUnits = .001; break; /* 1 micrometer*/ + } + break; - { - const unsigned char * const dirEnd = - DIR_ENTRY_ADDR(dirStart, numDirEntries); - if (dirEnd + 4 > (exifData + exifLength)){ - if (dirEnd + 2 == exifData + exifLength || - dirEnd == exifData + exifLength){ - /* Version 1.3 of jhead would truncate a bit too much. - This also caught later on as well. + /* Remaining cases contributed by: Volker C. Schoech + (schoech@gmx.de) */ - }else{ - /* Note: Files that had thumbnails trimmed with jhead - 1.3 or earlier might trigger this. - */ - pm_message("Illegal directory entry size"); - return; - } - } - *lastExifRefdP = MAX(*lastExifRefdP, dirEnd); - } - - if (wantTagTrace) - pm_message("Directory with %d entries", numDirEntries); - haveThumbnail = false; /* initial value */ - thumbnailOffset = 0; /* initial value */ - thumbnailSize = 0; /* initial value */ + case TAG_EXPOSURE_BIAS: + ImageInfoP->ExposureBias = + (float) ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_WHITEBALANCE: + ImageInfoP->Whitebalance = + (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_METERING_MODE: + ImageInfoP->MeteringMode = + (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXPOSURE_PROGRAM: + ImageInfoP->ExposureProgram = + (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_ISO_EQUIVALENT: + ImageInfoP->ISOequivalent = + (int)ConvertAnyFormat(ValuePtr, Format); + if ( ImageInfoP->ISOequivalent < 50 ) + ImageInfoP->ISOequivalent *= 200; + break; + + case TAG_COMPRESSION_LEVEL: + ImageInfoP->CompressionLevel = + (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_THUMBNAIL_OFFSET: + ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); + DirWithThumbnailPtrs = DirStart; + break; + + case TAG_THUMBNAIL_LENGTH: + ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXIF_OFFSET: + case TAG_INTEROP_OFFSET: + { + unsigned int const SubdirOffset = Get32u(ValuePtr); + if (SubdirOffset >= ExifLength) + pm_message("Illegal exif or interop offset " + "directory link. Offset is %u, " + "but Exif data is only %u bytes.", + SubdirOffset, ExifLength); + else + ProcessExifDir(ExifData, ExifLength, SubdirOffset, + ImageInfoP, ShowTags, LastExifRefdP); + continue; + } + } - for (de = 0; de < numDirEntries; ++de) - processDirEntry(DIR_ENTRY_ADDR(dirStart, de), exifData, exifLength, - byteOrder, wantTagTrace, imageInfoP, - &thumbnailOffset, &thumbnailSize, &haveThumbnail, - lastExifRefdP); + } - if (haveThumbnail) - DirWithThumbnailPtrs = dirStart; { /* In addition to linking to subdirectories via exif tags, @@ -791,30 +707,28 @@ processExifDir(const unsigned char * const exifData, of each directory. This has got to be the result of a committee! */ - if (DIR_ENTRY_ADDR(dirStart, numDirEntries) + 4 <= - exifData + exifLength){ - unsigned int const subdirOffset = - get32u(dirStart + 2 + 12*numDirEntries, byteOrder); - if (subdirOffset){ - const unsigned char * const subdirStart = - exifData + subdirOffset; - if (subdirStart > exifData + exifLength){ - if (subdirStart < exifData + exifLength + 20){ + if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= + ExifData+ExifLength){ + unsigned int const SubdirOffset = + Get32u(DirStart+2+12*NumDirEntries); + if (SubdirOffset){ + unsigned char * const SubdirStart = ExifData + SubdirOffset; + if (SubdirStart > ExifData+ExifLength){ + if (SubdirStart < ExifData+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. */ - if (wantTagTrace) + if (ShowTags) printf("Thumbnail removed with " "Jhead 1.3 or earlier\n"); }else{ pm_message("Illegal subdirectory link"); } }else{ - if (subdirOffset <= exifLength) - processExifDir(exifData, exifLength, subdirOffset, - imageInfoP, byteOrder, wantTagTrace, - lastExifRefdP); + if (SubdirOffset <= ExifLength) + ProcessExifDir(ExifData, ExifLength, SubdirOffset, + ImageInfoP, ShowTags, LastExifRefdP); } } }else{ @@ -822,14 +736,14 @@ processExifDir(const unsigned char * const exifData, } } - if (thumbnailSize && thumbnailOffset){ - if (thumbnailSize + thumbnailOffset <= exifLength){ + if (ThumbnailSize && ThumbnailOffset){ + if (ThumbnailSize + ThumbnailOffset <= ExifLength){ /* The thumbnail pointer appears to be valid. Store it. */ - imageInfoP->ThumbnailPointer = exifData + thumbnailOffset; - imageInfoP->ThumbnailSize = thumbnailSize; + ImageInfoP->ThumbnailPointer = ExifData + ThumbnailOffset; + ImageInfoP->ThumbnailSize = ThumbnailSize; - if (wantTagTrace){ - fprintf(stderr, "Thumbnail size: %u bytes\n", thumbnailSize); + if (ShowTags){ + fprintf(stderr, "Thumbnail size: %d bytes\n",ThumbnailSize); } } } @@ -838,46 +752,46 @@ processExifDir(const unsigned char * const exifData, void -exif_parse(const unsigned char * const exifData, - unsigned int const length, - exif_ImageInfo * const imageInfoP, - bool const wantTagTrace, - const char ** const errorP) { +process_EXIF(unsigned char * const ExifData, + unsigned int const length, + ImageInfo_t * const ImageInfoP, + int const ShowTags, + const char ** const errorP) { /*-------------------------------------------------------------------------- Interpret an EXIF APP1 marker - 'exifData' is the actual Exif data; it does not include the + 'ExifData' is the actual Exif data; it does not include the "Exif" identifier and length field that often prefix Exif data. 'length' is the length of the Exif section. --------------------------------------------------------------------------*/ - ByteOrder byteOrder; int FirstOffset; - const unsigned char * lastExifRefd; + unsigned char * LastExifRefd; *errorP = NULL; /* initial assumption */ - if (wantTagTrace) + if (ShowTags){ fprintf(stderr, "Exif header %d bytes long\n",length); + } - if (MEMEQ(exifData + 0, "II" , 2)) { - if (wantTagTrace) + if (memcmp(ExifData+0,"II",2) == 0) { + if (ShowTags) fprintf(stderr, "Exif header in Intel order\n"); - byteOrder = NORMAL; + MotorolaOrder = 0; } else { - if (MEMEQ(exifData + 0, "MM", 2)) { - if (wantTagTrace) + if (memcmp(ExifData+0, "MM", 2) == 0) { + if (ShowTags) fprintf(stderr, "Exif header in Motorola order\n"); - byteOrder = MOTOROLA; + MotorolaOrder = 1; } else { pm_asprintf(errorP, "Invalid alignment marker in Exif " "data. First two bytes are '%c%c' (0x%02x%02x) " "instead of 'II' or 'MM'.", - exifData[0], exifData[1], exifData[0], exifData[1]); + ExifData[0], ExifData[1], ExifData[0], ExifData[1]); } } if (!*errorP) { - unsigned short const start = get16u(exifData + 2, byteOrder); + unsigned short const start = Get16u(ExifData + 2); /* Check the next value for correctness. */ if (start != 0x002a){ pm_asprintf(errorP, "Invalid Exif header start. " @@ -887,7 +801,7 @@ exif_parse(const unsigned char * const exifData, } } if (!*errorP) { - FirstOffset = get32u(exifData + 4, byteOrder); + FirstOffset = Get32u(ExifData + 4); 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 @@ -896,54 +810,51 @@ exif_parse(const unsigned char * const exifData, pm_message("Suspicious offset of first IFD value in Exif header"); } - imageInfoP->Comments[0] = '\0'; /* Initial value - null string */ + ImageInfoP->Comments[0] = '\0'; /* Initial value - null string */ HaveXRes = FALSE; /* Initial assumption */ FocalplaneUnits = 0; ExifImageWidth = 0; - lastExifRefd = exifData; + LastExifRefd = ExifData; DirWithThumbnailPtrs = NULL; - processExifDir(exifData, length, FirstOffset, - imageInfoP, byteOrder, wantTagTrace, &lastExifRefd); + ProcessExifDir(ExifData, length, FirstOffset, + ImageInfoP, ShowTags, &LastExifRefd); /* Compute the CCD width, in millimeters. */ if (HaveXRes){ - imageInfoP->HaveCCDWidth = 1; - imageInfoP->CCDWidth = + ImageInfoP->HaveCCDWidth = 1; + ImageInfoP->CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); } else - imageInfoP->HaveCCDWidth = 0; + ImageInfoP->HaveCCDWidth = 0; - if (wantTagTrace){ + if (ShowTags){ fprintf(stderr, "Non-settings part of Exif header: %lu bytes\n", - (unsigned long)(exifData + length - lastExifRefd)); + (unsigned long)(ExifData+length-LastExifRefd)); } } } - - -void -exif_showImageInfo(const exif_ImageInfo * const imageInfoP, - FILE * const fileP) { /*-------------------------------------------------------------------------- Show the collected image info, displaying camera F-stop and shutter speed in a consistent and legible fashion. --------------------------------------------------------------------------*/ - if (imageInfoP->CameraMake[0]) { - fprintf(fileP, "Camera make : %s\n", imageInfoP->CameraMake); - fprintf(fileP, "Camera model : %s\n", imageInfoP->CameraModel); +void +ShowImageInfo(ImageInfo_t * const ImageInfoP) +{ + if (ImageInfoP->CameraMake[0]){ + fprintf(stderr, "Camera make : %s\n",ImageInfoP->CameraMake); + fprintf(stderr, "Camera model : %s\n",ImageInfoP->CameraModel); } - if (imageInfoP->DateTime[0]) - fprintf(fileP, "Date/Time : %s\n", imageInfoP->DateTime); - - fprintf(fileP, "Resolution : %f x %f\n", - imageInfoP->XResolution, imageInfoP->YResolution); - - if (imageInfoP->Orientation > 1) { + if (ImageInfoP->DateTime[0]){ + fprintf(stderr, "Date/Time : %s\n",ImageInfoP->DateTime); + } + fprintf(stderr, "Resolution : %f x %f\n", + ImageInfoP->XResolution, ImageInfoP->YResolution); + if (ImageInfoP->Orientation > 1){ /* Only print orientation if one was supplied, and if its not 1 (normal orientation) @@ -980,144 +891,154 @@ exif_showImageInfo(const exif_ImageInfo * const imageInfoP, "rotate 270", /* rotate 270 to right it. */ }; - fprintf(fileP, "Orientation : %s\n", - OrientTab[imageInfoP->Orientation]); + fprintf(stderr, "Orientation : %s\n", + OrientTab[ImageInfoP->Orientation]); } - if (imageInfoP->IsColor == 0) - fprintf(fileP, "Color/bw : Black and white\n"); - - if (imageInfoP->FlashUsed >= 0) - fprintf(fileP, "Flash used : %s\n", - imageInfoP->FlashUsed ? "Yes" :"No"); - - if (imageInfoP->FocalLength) { - fprintf(fileP, "Focal length : %4.1fmm", - (double)imageInfoP->FocalLength); - if (imageInfoP->HaveCCDWidth){ - fprintf(fileP, " (35mm equivalent: %dmm)", + if (ImageInfoP->IsColor == 0){ + fprintf(stderr, "Color/bw : Black and white\n"); + } + if (ImageInfoP->FlashUsed >= 0){ + fprintf(stderr, "Flash used : %s\n", + ImageInfoP->FlashUsed ? "Yes" :"No"); + } + if (ImageInfoP->FocalLength){ + fprintf(stderr, "Focal length : %4.1fmm", + (double)ImageInfoP->FocalLength); + if (ImageInfoP->HaveCCDWidth){ + fprintf(stderr, " (35mm equivalent: %dmm)", (int) - (imageInfoP->FocalLength/imageInfoP->CCDWidth*36 + 0.5)); + (ImageInfoP->FocalLength/ImageInfoP->CCDWidth*36 + 0.5)); } - fprintf(fileP, "\n"); + fprintf(stderr, "\n"); } - if (imageInfoP->HaveCCDWidth) - fprintf(fileP, "CCD width : %2.4fmm\n", - (double)imageInfoP->CCDWidth); + if (ImageInfoP->HaveCCDWidth){ + fprintf(stderr, "CCD width : %2.4fmm\n", + (double)ImageInfoP->CCDWidth); + } - if (imageInfoP->ExposureTime) { - if (imageInfoP->ExposureTime < 0.010){ - fprintf(fileP, + if (ImageInfoP->ExposureTime){ + if (ImageInfoP->ExposureTime < 0.010){ + fprintf(stderr, "Exposure time: %6.4f s ", - (double)imageInfoP->ExposureTime); + (double)ImageInfoP->ExposureTime); }else{ - fprintf(fileP, + fprintf(stderr, "Exposure time: %5.3f s ", - (double)imageInfoP->ExposureTime); + (double)ImageInfoP->ExposureTime); } - if (imageInfoP->ExposureTime <= 0.5){ - fprintf(fileP, " (1/%d)",(int)(0.5 + 1/imageInfoP->ExposureTime)); + if (ImageInfoP->ExposureTime <= 0.5){ + fprintf(stderr, " (1/%d)",(int)(0.5 + 1/ImageInfoP->ExposureTime)); } - fprintf(fileP, "\n"); + fprintf(stderr, "\n"); } - if (imageInfoP->ApertureFNumber){ - fprintf(fileP, "Aperture : f/%3.1f\n", - (double)imageInfoP->ApertureFNumber); + if (ImageInfoP->ApertureFNumber){ + fprintf(stderr, "Aperture : f/%3.1f\n", + (double)ImageInfoP->ApertureFNumber); } - if (imageInfoP->Distance){ - if (imageInfoP->Distance < 0){ - fprintf(fileP, "Focus dist. : Infinite\n"); + if (ImageInfoP->Distance){ + if (ImageInfoP->Distance < 0){ + fprintf(stderr, "Focus dist. : Infinite\n"); }else{ - fprintf(fileP, "Focus dist. :%5.2fm\n", - (double)imageInfoP->Distance); + fprintf(stderr, "Focus dist. :%5.2fm\n", + (double)ImageInfoP->Distance); } } - if (imageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */ - fprintf(fileP, "ISO equiv. : %2d\n",(int)imageInfoP->ISOequivalent); + + + + + if (ImageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */ + fprintf(stderr, "ISO equiv. : %2d\n",(int)ImageInfoP->ISOequivalent); } - if (imageInfoP->ExposureBias){ /* 05-jan-2001 vcs */ - fprintf(fileP, "Exposure bias:%4.2f\n", - (double)imageInfoP->ExposureBias); + if (ImageInfoP->ExposureBias){ /* 05-jan-2001 vcs */ + fprintf(stderr, "Exposure bias:%4.2f\n", + (double)ImageInfoP->ExposureBias); } - if (imageInfoP->Whitebalance){ /* 05-jan-2001 vcs */ - switch(imageInfoP->Whitebalance) { + if (ImageInfoP->Whitebalance){ /* 05-jan-2001 vcs */ + switch(ImageInfoP->Whitebalance) { case 1: - fprintf(fileP, "Whitebalance : sunny\n"); + fprintf(stderr, "Whitebalance : sunny\n"); break; case 2: - fprintf(fileP, "Whitebalance : fluorescent\n"); + fprintf(stderr, "Whitebalance : fluorescent\n"); break; case 3: - fprintf(fileP, "Whitebalance : incandescent\n"); + fprintf(stderr, "Whitebalance : incandescent\n"); break; default: - fprintf(fileP, "Whitebalance : cloudy\n"); + fprintf(stderr, "Whitebalance : cloudy\n"); } } - if (imageInfoP->MeteringMode){ /* 05-jan-2001 vcs */ - switch(imageInfoP->MeteringMode) { + if (ImageInfoP->MeteringMode){ /* 05-jan-2001 vcs */ + switch(ImageInfoP->MeteringMode) { case 2: - fprintf(fileP, "Metering Mode: center weight\n"); + fprintf(stderr, "Metering Mode: center weight\n"); break; case 3: - fprintf(fileP, "Metering Mode: spot\n"); + fprintf(stderr, "Metering Mode: spot\n"); break; case 5: - fprintf(fileP, "Metering Mode: matrix\n"); + fprintf(stderr, "Metering Mode: matrix\n"); break; } } - if (imageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */ - switch(imageInfoP->ExposureProgram) { + if (ImageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */ + switch(ImageInfoP->ExposureProgram) { case 2: - fprintf(fileP, "Exposure : program (auto)\n"); + fprintf(stderr, "Exposure : program (auto)\n"); break; case 3: - fprintf(fileP, "Exposure : aperture priority (semi-auto)\n"); + fprintf(stderr, "Exposure : aperture priority (semi-auto)\n"); break; case 4: - fprintf(fileP, "Exposure : shutter priority (semi-auto)\n"); + fprintf(stderr, "Exposure : shutter priority (semi-auto)\n"); break; } } - if (imageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */ - switch(imageInfoP->CompressionLevel) { + if (ImageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */ + switch(ImageInfoP->CompressionLevel) { case 1: - fprintf(fileP, "Jpeg Quality : basic\n"); + fprintf(stderr, "Jpeg Quality : basic\n"); break; case 2: - fprintf(fileP, "Jpeg Quality : normal\n"); + fprintf(stderr, "Jpeg Quality : normal\n"); break; case 4: - fprintf(fileP, "Jpeg Quality : fine\n"); + fprintf(stderr, "Jpeg Quality : fine\n"); break; } } - /* Print the comment. Print 'Comment:' for each new line of comment. */ - if (imageInfoP->Comments[0]) { - unsigned int a; - - fprintf(fileP, "Comment : "); + - for (a = 0; a < MAX_COMMENT && imageInfoP->Comments[a]; ++a) { - char const c = imageInfoP->Comments[a]; + /* Print the comment. Print 'Comment:' for each new line of comment. */ + if (ImageInfoP->Comments[0]){ + int a,c; + fprintf(stderr, "Comment : "); + for (a=0;a<MAX_COMMENT;a++){ + c = ImageInfoP->Comments[a]; + if (c == '\0') break; if (c == '\n'){ /* Do not start a new line if the string ends with a cr */ - if (imageInfoP->Comments[a+1] != '\0') - fprintf(fileP, "\nComment : "); - else - fprintf(fileP, "\n"); - } else - putc(c, fileP); + if (ImageInfoP->Comments[a+1] != '\0'){ + fprintf(stderr, "\nComment : "); + }else{ + fprintf(stderr, "\n"); + } + }else{ + putc(c, stderr); + } } - fprintf(fileP, "\n"); + fprintf(stderr, "\n"); } - fprintf(fileP, "\n"); + fprintf(stderr, "\n"); } + + |