From 05b13da5251ecc6446651bab385bce9305683e2a Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Thu, 28 Feb 2002 12:20:17 +0000 Subject: [PATCH] Changed file to match CODING_STANDARDS except function names that match jhead.c project. I think it is acceptable using naming conventions of other projects when borrowing code. --- ext/exif/exif.c | 1839 +++++++++++++++++++++++------------------------ 1 file changed, 917 insertions(+), 922 deletions(-) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 7d576707eb..5db3728add 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -26,30 +26,33 @@ Aug.3 2001 - Added support for multiple M_COM entries - Rasmus + Additional changes and fixes mainly to expand functionality for image + galleries. + Feb.26 2002 - Marcus - Added UNICODE support for Comments - Added Description,Artist - Added missing memory deallocation - Corrected error with multiple comments - Corrected handling of ExifVersion, Tag has 4 ASCII - characters *WITHOUT* NUL - Corrected handling of Thumbnailsize if current source - detects size < 0 - Changed all fields to char* that do not have a maximum - length in EXIF standard - Undocumented second Parameter ReadAll frees memory to - early -> moved to third position default changed to - false -> faster - New second Parameter [true|false] to specify whether or - not to to read thumbnails -> reading is timeconsumpting - suppose default should be false -> done so + Added UNICODE support for Comments + Added Description,Artist + Added missing memory deallocation + Corrected error with multiple comments + Corrected handling of ExifVersion, Tag has 4 ASCII + characters *WITHOUT* NUL + Corrected handling of Thumbnailsize if current source + detects size < 0 + Changed all fields to char* that do not have a maximum + length in EXIF standard + Undocumented second Parameter ReadAll frees memory to + early -> moved to third position default changed to + false -> faster + New second Parameter [true|false] to specify whether or + not to to read thumbnails -> reading is timeconsumpting + suppose default should be false -> done so ToDos - Copyright tag stores " '\0' '\0'" but we - stop at first '\0' - JIS encoding for comments - See if example images from http://www.exif.org have illegal - thumbnail sizes or if code is corrupt. + stop at first '\0' + JIS encoding for comments + See if example images from http://www.exif.org have illegal + thumbnail sizes or if code is corrupt. The original header from the jhead.c file was: @@ -84,8 +87,8 @@ typedef unsigned char uchar; #ifndef TRUE - #define TRUE 1 - #define FALSE 0 + #define TRUE 1 + #define FALSE 0 #endif #define EXIF_MAX_COMMENTS 12 @@ -96,53 +99,53 @@ typedef unsigned char uchar; stored in a nexif header */ typedef struct { - char FileName [120]; - time_t FileDateTime; - unsigned FileSize; - char *CameraMake; - char *CameraModel; - char DateTime [20]; - int Height, Width; - int IsColor; - int FlashUsed; - float FocalLength; - float ExposureTime; - float ApertureFNumber; - float Distance; - float CCDWidth; - char *Description; - char *Comments[EXIF_MAX_COMMENTS]; - int numComments; - double FocalplaneXRes; - double FocalplaneUnits; - int ExifImageWidth; - int MotorolaOrder; - int Orientation; - char GPSinfo[48]; - int ISOspeed; - char ExifVersion[8]; - char *Copyright; // maybe two strings see ToDos - char *Artist; - char *Software; - char *Thumbnail; - int ThumbnailSize; - int ThumbnailOffset; - /* Olympus vars */ - int SpecialMode; - int JpegQual; - int Macro; - int DigiZoom; - char SoftwareRelease[16]; - char PictInfo[64]; - char CameraId[64]; - /* End Olympus vars */ + char FileName [120]; + time_t FileDateTime; + unsigned FileSize; + char *CameraMake; + char *CameraModel; + char DateTime [20]; + int Height, Width; + int IsColor; + int FlashUsed; + float FocalLength; + float ExposureTime; + float ApertureFNumber; + float Distance; + float CCDWidth; + char *Description; + char *Comments[EXIF_MAX_COMMENTS]; + int numComments; + double FocalplaneXRes; + double FocalplaneUnits; + int ExifImageWidth; + int MotorolaOrder; + int Orientation; + char GPSinfo[48]; + int ISOspeed; + char ExifVersion[8]; + char *Copyright; /* maybe two strings see ToDos */ + char *Artist; + char *Software; + char *Thumbnail; + int ThumbnailSize; + int ThumbnailOffset; + /* Olympus vars */ + int SpecialMode; + int JpegQual; + int Macro; + int DigiZoom; + char SoftwareRelease[16]; + char PictInfo[64]; + char CameraId[64]; + /* End Olympus vars */ } ImageInfoType; /* This structure is used to store a section of a Jpeg file. */ typedef struct { - uchar *Data; - int Type; - unsigned Size; + uchar *Data; + int Type; + unsigned Size; } Section_t; /* }}} */ @@ -152,8 +155,8 @@ typedef struct { /* {{{ exif_functions[] */ function_entry exif_functions[] = { - PHP_FE(read_exif_data, NULL) - {NULL, NULL, NULL} + PHP_FE(read_exif_data, NULL) + {NULL, NULL, NULL} }; /* }}} */ @@ -162,14 +165,14 @@ PHP_MINFO_FUNCTION(exif); /* {{{ exif_module_entry */ zend_module_entry exif_module_entry = { - STANDARD_MODULE_HEADER, - "exif", - exif_functions, - NULL, NULL, - NULL, NULL, - PHP_MINFO(exif), - NO_VERSION_YET, - STANDARD_MODULE_PROPERTIES + STANDARD_MODULE_HEADER, + "exif", + exif_functions, + NULL, NULL, + NULL, NULL, + PHP_MINFO(exif), + NO_VERSION_YET, + STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -182,8 +185,8 @@ ZEND_GET_MODULE(exif) PHP_MINFO_FUNCTION(exif) { php_info_print_table_start(); - php_info_print_table_row(2, "EXIF Support", "enabled" ); - php_info_print_table_end(); + php_info_print_table_row(2, "EXIF Support", "enabled" ); + php_info_print_table_end(); } /* }}} */ @@ -222,7 +225,7 @@ PHP_MINFO_FUNCTION(exif) */ static int Get16m(void *Short) { - return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; + return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; } /* }}} */ @@ -233,30 +236,30 @@ static int Get16m(void *Short) */ static void process_COM (ImageInfoType *ImageInfo, uchar *Data, int length) { - int ch; - char Comment[250]; - int nch; - int a; + int ch; + char Comment[250]; + int nch; + int a; if(ImageInfo->numComments == EXIF_MAX_COMMENTS) return; - nch = 0; + nch = 0; - if (length > 200) length = 200; /* Truncate if it won't fit in our structure. */ + if (length > 200) length = 200; /* Truncate if it won't fit in our structure. */ - for (a=2;anumComments; @@ -270,36 +273,36 @@ static void process_COM (ImageInfoType *ImageInfo, uchar *Data, int length) * Process a SOFn marker. This is useful for the image dimensions */ static void process_SOFn (ImageInfoType *ImageInfo, uchar *Data, int marker) { - int data_precision, num_components; - const char *process; - - data_precision = Data[2]; - ImageInfo->Height = Get16m(Data+3); - ImageInfo->Width = Get16m(Data+5); - num_components = Data[7]; - - if (num_components == 3) { - ImageInfo->IsColor = 1; - } else { - ImageInfo->IsColor = 0; - } - - switch (marker) { - case M_SOF0: process = "Baseline"; break; - case M_SOF1: process = "Extended sequential"; break; - case M_SOF2: process = "Progressive"; break; - case M_SOF3: process = "Lossless"; break; - case M_SOF5: process = "Differential sequential"; break; - case M_SOF6: process = "Differential progressive"; break; - case M_SOF7: process = "Differential lossless"; break; - case M_SOF9: process = "Extended sequential, arithmetic coding"; break; - case M_SOF10: process = "Progressive, arithmetic coding"; break; - case M_SOF11: process = "Lossless, arithmetic coding"; break; - case M_SOF13: process = "Differential sequential, arithmetic coding"; break; - case M_SOF14: process = "Differential progressive, arithmetic coding"; break; - case M_SOF15: process = "Differential lossless, arithmetic coding"; break; - default: process = "Unknown"; break; - } + int data_precision, num_components; + const char *process; + + data_precision = Data[2]; + ImageInfo->Height = Get16m(Data+3); + ImageInfo->Width = Get16m(Data+5); + num_components = Data[7]; + + if (num_components == 3) { + ImageInfo->IsColor = 1; + } else { + ImageInfo->IsColor = 0; + } + + switch (marker) { + case M_SOF0: process = "Baseline"; break; + case M_SOF1: process = "Extended sequential"; break; + case M_SOF2: process = "Progressive"; break; + case M_SOF3: process = "Lossless"; break; + case M_SOF5: process = "Differential sequential"; break; + case M_SOF6: process = "Differential progressive"; break; + case M_SOF7: process = "Differential lossless"; break; + case M_SOF9: process = "Extended sequential, arithmetic coding"; break; + case M_SOF10: process = "Progressive, arithmetic coding"; break; + case M_SOF11: process = "Lossless, arithmetic coding"; break; + case M_SOF13: process = "Differential sequential, arithmetic coding"; break; + case M_SOF14: process = "Differential progressive, arithmetic coding"; break; + case M_SOF15: process = "Differential lossless, arithmetic coding"; break; + default: process = "Unknown"; break; + } } /* }}} */ @@ -379,8 +382,8 @@ static int ExifBytesPerFormat[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; /* {{{ TabTable[] */ static const struct { - unsigned short Tag; - char *Desc; + unsigned short Tag; + char *Desc; } TagTable[] = { { 0x100, "ImageWidth"}, { 0x101, "ImageLength"}, @@ -473,11 +476,11 @@ static const struct { * Convert a 16 bit unsigned value from file's native byte order */ static int Get16u(void *Short, int MotorolaOrder) { - if (MotorolaOrder) { - return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; - } else { - return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; - } + if (MotorolaOrder) { + return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; + } else { + return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; + } } /* }}} */ @@ -485,13 +488,13 @@ static int Get16u(void *Short, int MotorolaOrder) * Convert a 32 bit signed value from file's native byte order */ static int Get32s(void *Long, int MotorolaOrder) { - if (MotorolaOrder) { - return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) - | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); - } else { - return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) - | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); - } + if (MotorolaOrder) { + return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) + | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); + } else { + return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) + | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); + } } /* }}} */ @@ -499,7 +502,7 @@ static int Get32s(void *Long, int MotorolaOrder) * Convert a 32 bit unsigned value from file's native byte order */ static unsigned Get32u(void *Long, int MotorolaOrder) { - return (unsigned)Get32s(Long, MotorolaOrder) & 0xffffffff; + return (unsigned)Get32s(Long, MotorolaOrder) & 0xffffffff; } /* }}} */ @@ -507,38 +510,38 @@ static unsigned Get32u(void *Long, int MotorolaOrder) * Evaluate number, be it int, rational, or float from directory. */ static double ConvertAnyFormat(void *ValuePtr, int Format, int MotorolaOrder) { - double Value; - Value = 0; - - switch(Format) { - case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; - case FMT_BYTE: Value = *(uchar *)ValuePtr; break; - - case FMT_USHORT: Value = Get16u(ValuePtr, MotorolaOrder); break; - case FMT_ULONG: Value = Get32u(ValuePtr, MotorolaOrder); break; - - case FMT_URATIONAL: - case FMT_SRATIONAL: - { - int Num, Den; - Num = Get32s(ValuePtr, MotorolaOrder); - Den = Get32s(4+(char *)ValuePtr, MotorolaOrder); - if (Den == 0) { - Value = 0; - } else { - Value = (double)Num/Den; - } - break; - } - - case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr, MotorolaOrder); break; - case FMT_SLONG: Value = Get32s(ValuePtr, MotorolaOrder); 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; + double Value; + Value = 0; + + switch(Format) { + case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; + case FMT_BYTE: Value = *(uchar *)ValuePtr; break; + + case FMT_USHORT: Value = Get16u(ValuePtr, MotorolaOrder); break; + case FMT_ULONG: Value = Get32u(ValuePtr, MotorolaOrder); break; + + case FMT_URATIONAL: + case FMT_SRATIONAL: + { + int Num, Den; + Num = Get32s(ValuePtr, MotorolaOrder); + Den = Get32s(4+(char *)ValuePtr, MotorolaOrder); + if (Den == 0) { + Value = 0; + } else { + Value = (double)Num/Den; + } + break; + } + + case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr, MotorolaOrder); break; + case FMT_SLONG: Value = Get32s(ValuePtr, MotorolaOrder); 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; } /* }}} */ @@ -571,69 +574,66 @@ static void ExtractThumbnail(ImageInfoType *ImageInfo, char *OffsetBase, unsigne * Process one Tag in EXIF header. */ static int ProcessExifComment(char **pszInfoPtr,char *szValuePtr,int ByteCount) { - int a,l,c; // :-) - char mbBuffer[MB_CUR_MAX]; - - /* Copy the comment */ - if ( ByteCount>8 && !memcmp(szValuePtr, "UNICODE\0", 8) || !memcmp(szValuePtr, "JIS\0\0\0\0\0", 8)) { - // treat JIS encoding as if it where UNICODE - szValuePtr = szValuePtr+8; - ByteCount -= 8; - l = 0; - a = 0; - while(((wchar_t*)szValuePtr)[a]) { - l += (int)wctomb( mbBuffer, *((wchar_t*)szValuePtr)); - if (sizeof(wchar_t)*a++ >= ByteCount) break; // avoiding problems with corrupt headers - } - if (l>1) { - *pszInfoPtr = emalloc(l+1); - wcstombs(*pszInfoPtr, (wchar_t*)(szValuePtr), l+1); - (*pszInfoPtr)[l] = '\0'; - //php_error(E_WARNING, "User comment UNICODE(%i): '%s'", l, *pszInfoPtr); - return l; - } else return 0; - } - - /* Olympus has this padded with trailing spaces. Remove these first. */ - for (a=ByteCount-1;a && szValuePtr[a]==' ';a--) (szValuePtr)[a] = '\0'; - - if ( ByteCount>5 && !memcmp(szValuePtr, "ASCII", 5)) { - // this is original code from Matthias Wandel - // from a standard point of view it is wrong but it functions - // and accepts more than needed. Especially you cannot start a - // comment with the word 'ASCII' - for (a=5;a<8;a++) { - c = (szValuePtr)[a]; - if (c != '\0' && c != ' ') { - szValuePtr = szValuePtr+a; - ByteCount -= a; - l = strlen(szValuePtr)+1; - if ( l>ByteCount) l= ByteCount; - if (l>1) { - *pszInfoPtr = emalloc(l); - strlcpy(*pszInfoPtr, szValuePtr, l); - //php_error(E_WARNING, "User comment ASCII(%i): '%s'", l, *pszInfoPtr); - return 1; - } - return 0; - } - } - return 0; - } - if ( ByteCount>8 && !memcmp(szValuePtr,"\0\0\0\0\0\0\0\0",8) && szValuePtr[8]!='\0') { - szValuePtr = szValuePtr+8; - ByteCount -= 8; - //php_error(E_WARNING, "User comment UNDEFINED: '%s'", szValuePtr); - } - // normal text without encoding - l = strlen(szValuePtr)+1; - if ( l>ByteCount) l= ByteCount; - if ( l>1) { - *pszInfoPtr = emalloc(l); - strlcpy(*pszInfoPtr, szValuePtr, l); - //php_error(E_WARNING, "User comment standard(%i): '%s'", l, *pszInfoPtr); - return 1; - } else return 0; + int a,l,c; /* :-) */ + char mbBuffer[MB_CUR_MAX]; + + /* Copy the comment */ + if ( ByteCount>8 && !memcmp(szValuePtr, "UNICODE\0", 8) || !memcmp(szValuePtr, "JIS\0\0\0\0\0", 8)) { + /* treat JIS encoding as if it where UNICODE */ + szValuePtr = szValuePtr+8; + ByteCount -= 8; + l = 0; + a = 0; + while(((wchar_t*)szValuePtr)[a]) { + l += (int)wctomb( mbBuffer, *((wchar_t*)szValuePtr)); + if (sizeof(wchar_t)*a++ >= ByteCount) break; /* avoiding problems with corrupt headers */ + } + if (l>1) { + *pszInfoPtr = emalloc(l+1); + wcstombs(*pszInfoPtr, (wchar_t*)(szValuePtr), l+1); + (*pszInfoPtr)[l] = '\0'; + return l; + } else return 0; + } + + /* Olympus has this padded with trailing spaces. Remove these first. */ + for (a=ByteCount-1;a && szValuePtr[a]==' ';a--) (szValuePtr)[a] = '\0'; + + if ( ByteCount>5 && !memcmp(szValuePtr, "ASCII", 5)) { + /* this is original code from Matthias Wandel + * from a standard point of view it is wrong but it functions + * and accepts more than needed. Especially you cannot start a + * comment with the word 'ASCII' + */ + for (a=5;a<8;a++) { + c = (szValuePtr)[a]; + if (c != '\0' && c != ' ') { + szValuePtr = szValuePtr+a; + ByteCount -= a; + l = strlen(szValuePtr)+1; + if ( l>ByteCount) l= ByteCount; + if (l>1) { + *pszInfoPtr = emalloc(l); + strlcpy(*pszInfoPtr, szValuePtr, l); + return 1; + } + return 0; + } + } + return 0; + } + if ( ByteCount>8 && !memcmp(szValuePtr,"\0\0\0\0\0\0\0\0",8) && szValuePtr[8]!='\0') { + szValuePtr = szValuePtr+8; + ByteCount -= 8; + } + /* normal text without encoding */ + l = strlen(szValuePtr)+1; + if ( l>ByteCount) l= ByteCount; + if ( l>1) { + *pszInfoPtr = emalloc(l); + strlcpy(*pszInfoPtr, szValuePtr, l); + return 1; + } else return 0; } /* }}} */ @@ -641,17 +641,16 @@ static int ProcessExifComment(char **pszInfoPtr,char *szValuePtr,int ByteCount) /* {{{ CopyExifString * Copy a string in Exif header to a character string. */ static int CopyExifString(char **pszInfoPtr,char *szValuePtr,int ByteCount) { - int l; - - l = strlen(szValuePtr)+1; - if (ByteCount1) { - (*pszInfoPtr) = emalloc(l); - strlcpy(*pszInfoPtr, szValuePtr, l); - //php_error(E_WARNING, "Image description(%i): '%s'", l, *pszInfoPtr); - return 1; - } - return 0; + int l; + + l = strlen(szValuePtr)+1; + if (ByteCount1) { + (*pszInfoPtr) = emalloc(l); + strlcpy(*pszInfoPtr, szValuePtr, l); + return 1; + } + return 0; } /* }}} */ @@ -659,267 +658,267 @@ static int CopyExifString(char **pszInfoPtr,char *szValuePtr,int ByteCount) { * Process one of the nested EXIF directories. */ static void ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *OffsetBase, unsigned ExifLength, char *LastExifRefd, int ReadThumbnail) { - int de; - int a; - int NumDirEntries; - int NextDirOffset; + int de; + int a; + int NumDirEntries; + int NextDirOffset; - NumDirEntries = Get16u(DirStart, ImageInfo->MotorolaOrder); + NumDirEntries = Get16u(DirStart, ImageInfo->MotorolaOrder); - if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) { - php_error(E_WARNING, "Illegally sized directory"); + if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) { + php_error(E_WARNING, "Illegally sized directory"); return; - } + } /* - if (ShowTags) { - printf("Directory with %d entries\n", NumDirEntries); - } + if (ShowTags) { + printf("Directory with %d entries\n", NumDirEntries); + } */ - for (de=0;deMotorolaOrder); - Format = Get16u(DirEntry+2, ImageInfo->MotorolaOrder); - Components = Get32u(DirEntry+4, ImageInfo->MotorolaOrder); - - if ((Format-1) >= NUM_FORMATS) { - /* (-1) catches illegal zero case as unsigned underflows to positive large. */ - php_error(E_WARNING, "Illegal format code in EXIF dir"); - return; - } - - ByteCount = Components * ExifBytesPerFormat[Format]; - - if (ByteCount > 4) { - unsigned OffsetVal; - OffsetVal = Get32u(DirEntry+8, ImageInfo->MotorolaOrder); - /* If its bigger than 4 bytes, the dir entry contains an offset. */ - if (OffsetVal+ByteCount > ExifLength) { - /* Bogus pointer offset and / or bytecount value */ + for (de=0;deMotorolaOrder); + Format = Get16u(DirEntry+2, ImageInfo->MotorolaOrder); + Components = Get32u(DirEntry+4, ImageInfo->MotorolaOrder); + + if ((Format-1) >= NUM_FORMATS) { + /* (-1) catches illegal zero case as unsigned underflows to positive large. */ + php_error(E_WARNING, "Illegal format code in EXIF dir"); + return; + } + + ByteCount = Components * ExifBytesPerFormat[Format]; + + if (ByteCount > 4) { + unsigned OffsetVal; + OffsetVal = Get32u(DirEntry+8, ImageInfo->MotorolaOrder); + /* 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); */ - php_error(E_WARNING, "Illegal pointer offset value in EXIF"); + php_error(E_WARNING, "Illegal pointer offset value in EXIF"); return; - } - ValuePtr = OffsetBase+OffsetVal; - } else { - /* 4 bytes or less and value is in the dir entry itself */ - ValuePtr = DirEntry+8; - } - - if (LastExifRefd < ValuePtr+ByteCount) { - /* + } + 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. + That way, we know where the discardable thumbnail data begins. */ - LastExifRefd = ValuePtr+ByteCount; - } - - /* Extract useful components of tag */ - switch(Tag) { - - case TAG_MAKE: - CopyExifString(&ImageInfo->CameraMake,ValuePtr,ByteCount); - break; - - case TAG_MODEL: - CopyExifString(&ImageInfo->CameraModel,ValuePtr,ByteCount); - break; - - case TAG_GPSINFO: - strlcpy(ImageInfo->GPSinfo, ValuePtr, sizeof(ImageInfo->GPSinfo)); - break; - - case TAG_EXIFVERSION: - strncpy(ImageInfo->ExifVersion, ValuePtr, 4); - ImageInfo->ExifVersion[4] = '\0'; - break; - - case TAG_COPYRIGHT: - CopyExifString(&ImageInfo->Copyright,ValuePtr,ByteCount); - break; - - case TAG_ARTIST: - CopyExifString(&ImageInfo->Artist,ValuePtr,ByteCount); - break; - - case TAG_SOFTWARE: - CopyExifString(&ImageInfo->Software,ValuePtr,ByteCount); - break; - - case TAG_ORIENTATION: - ImageInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); - break; - - case TAG_ISOSPEED: - ImageInfo->ISOspeed = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); - break; - - case TAG_DATETIME_ORIGINAL: - strlcpy(ImageInfo->DateTime, ValuePtr, sizeof(ImageInfo->DateTime)); - break; - - case TAG_IMAGE_DESCRIPTION: - CopyExifString(&ImageInfo->Description,ValuePtr,ByteCount); - break; - - case TAG_USERCOMMENT: - if (ImageInfo->numComments < EXIF_MAX_COMMENTS) { - if (ProcessExifComment(&((ImageInfo->Comments)[ImageInfo->numComments]),ValuePtr,ByteCount)) { - ImageInfo->numComments++; - } - } - 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, ImageInfo->MotorolaOrder); - 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, ImageInfo->MotorolaOrder)*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, ImageInfo->MotorolaOrder); - 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, ImageInfo->MotorolaOrder); - 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, ImageInfo->MotorolaOrder); - 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, ImageInfo->MotorolaOrder)*log(2))); - } - break; - - case TAG_FLASH: - if (ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder)) { - ImageInfo->FlashUsed = 1; - } - break; - - case TAG_IMAGEWIDTH: - ImageInfo->ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); - break; - - case TAG_FOCALPLANEXRES: - ImageInfo->FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); - break; - - case TAG_FOCALPLANEUNITS: - switch((int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder)) { - case 1: ImageInfo->FocalplaneUnits = 25.4; break; /* inch */ - case 2: - /* According to the information I was using, 2 measn meters. - But looking at the Cannon powershot's files, inches is the only - sensible value. */ - ImageInfo->FocalplaneUnits = 25.4; - break; - - case 3: ImageInfo->FocalplaneUnits = 10; break; /* centimeter */ - case 4: ImageInfo->FocalplaneUnits = 1; break; /* milimeter */ - case 5: ImageInfo->FocalplaneUnits = .001; break; /* micrometer */ - } - break; - - case TAG_LIGHT_SOURCE: - /* Rarely set or useful. */ - break; - - case TAG_SPECIALMODE: - ImageInfo->SpecialMode = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->SpecialMode); - break; - - case TAG_JPEGQUAL: /* I think that this is a pointer to the thumbnail - let's see */ - ImageInfo->ThumbnailOffset = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->ThumbnailOffset); - - /* see if we know the size */ - if (ImageInfo->ThumbnailSize && ReadThumbnail) { - ExtractThumbnail(ImageInfo, OffsetBase, ExifLength); - } - /*ImageInfo->JpegQual = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->JpegQual);*/ - break; - - case TAG_MACRO: /* I think this is the size of the Thumbnail */ - ImageInfo->ThumbnailSize = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->ThumbnailSize); - - /* see if we have the offset */ - if (ImageInfo->ThumbnailOffset && ReadThumbnail) { - ExtractThumbnail(ImageInfo, OffsetBase, ExifLength); - } - /*ImageInfo->Macro = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->Macro);*/ - break; - - case TAG_DIGIZOOM: - ImageInfo->DigiZoom = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->DigiZoom); - break; - - case TAG_SOFTWARERELEASE: - strlcpy(ImageInfo->SoftwareRelease, ValuePtr, sizeof(ImageInfo->SoftwareRelease)); - break; - - case TAG_PICTINFO: - strlcpy(ImageInfo->PictInfo, ValuePtr, sizeof(ImageInfo->PictInfo)); - break; - - case TAG_CAMERAID: - strlcpy(ImageInfo->CameraId, ValuePtr, sizeof(ImageInfo->CameraId)); - break; - } - - if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) { - char *SubdirStart; - SubdirStart = OffsetBase + Get32u(ValuePtr, ImageInfo->MotorolaOrder); - if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { - php_error(E_WARNING, "Illegal subdirectory link"); - return; - } - ProcessExifDir(ImageInfo, SubdirStart, OffsetBase, ExifLength, LastExifRefd, ReadThumbnail); - continue; - } - } - /* - * Hack to make it process IDF1 I hope - * There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail - */ - NextDirOffset = Get32u(DirStart+2+12*de, ImageInfo->MotorolaOrder); - if (NextDirOffset) { - if (OffsetBase + NextDirOffset < OffsetBase || OffsetBase + NextDirOffset > OffsetBase+ExifLength) { - php_error(E_WARNING, "Illegal directory offset"); - return; - } - ProcessExifDir(ImageInfo, OffsetBase + NextDirOffset, OffsetBase, ExifLength, LastExifRefd, ReadThumbnail); - } + LastExifRefd = ValuePtr+ByteCount; + } + + /* Extract useful components of tag */ + switch(Tag) { + + case TAG_MAKE: + CopyExifString(&ImageInfo->CameraMake,ValuePtr,ByteCount); + break; + + case TAG_MODEL: + CopyExifString(&ImageInfo->CameraModel,ValuePtr,ByteCount); + break; + + case TAG_GPSINFO: + strlcpy(ImageInfo->GPSinfo, ValuePtr, sizeof(ImageInfo->GPSinfo)); + break; + + case TAG_EXIFVERSION: + strncpy(ImageInfo->ExifVersion, ValuePtr, 4); + ImageInfo->ExifVersion[4] = '\0'; + break; + + case TAG_COPYRIGHT: + CopyExifString(&ImageInfo->Copyright,ValuePtr,ByteCount); + break; + + case TAG_ARTIST: + CopyExifString(&ImageInfo->Artist,ValuePtr,ByteCount); + break; + + case TAG_SOFTWARE: + CopyExifString(&ImageInfo->Software,ValuePtr,ByteCount); + break; + + case TAG_ORIENTATION: + ImageInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); + break; + + case TAG_ISOSPEED: + ImageInfo->ISOspeed = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); + break; + + case TAG_DATETIME_ORIGINAL: + strlcpy(ImageInfo->DateTime, ValuePtr, sizeof(ImageInfo->DateTime)); + break; + + case TAG_IMAGE_DESCRIPTION: + CopyExifString(&ImageInfo->Description,ValuePtr,ByteCount); + break; + + case TAG_USERCOMMENT: + if (ImageInfo->numComments < EXIF_MAX_COMMENTS) { + if (ProcessExifComment(&((ImageInfo->Comments)[ImageInfo->numComments]),ValuePtr,ByteCount)) { + ImageInfo->numComments++; + } + } + 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, ImageInfo->MotorolaOrder); + 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, ImageInfo->MotorolaOrder)*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, ImageInfo->MotorolaOrder); + 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, ImageInfo->MotorolaOrder); + 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, ImageInfo->MotorolaOrder); + 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, ImageInfo->MotorolaOrder)*log(2))); + } + break; + + case TAG_FLASH: + if (ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder)) { + ImageInfo->FlashUsed = 1; + } + break; + + case TAG_IMAGEWIDTH: + ImageInfo->ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); + break; + + case TAG_FOCALPLANEXRES: + ImageInfo->FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder); + break; + + case TAG_FOCALPLANEUNITS: + switch((int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder)) { + case 1: ImageInfo->FocalplaneUnits = 25.4; break; /* inch */ + case 2: + /* According to the information I was using, 2 measn meters. + But looking at the Cannon powershot's files, inches is the only + sensible value. */ + ImageInfo->FocalplaneUnits = 25.4; + break; + + case 3: ImageInfo->FocalplaneUnits = 10; break; /* centimeter */ + case 4: ImageInfo->FocalplaneUnits = 1; break; /* milimeter */ + case 5: ImageInfo->FocalplaneUnits = .001; break; /* micrometer */ + } + break; + + case TAG_LIGHT_SOURCE: + /* Rarely set or useful. */ + break; + + case TAG_SPECIALMODE: + ImageInfo->SpecialMode = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->SpecialMode); + break; + + case TAG_JPEGQUAL: /* I think that this is a pointer to the thumbnail - let's see */ + ImageInfo->ThumbnailOffset = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->ThumbnailOffset); + + /* see if we know the size */ + if (ImageInfo->ThumbnailSize && ReadThumbnail) { + ExtractThumbnail(ImageInfo, OffsetBase, ExifLength); + } + /*ImageInfo->JpegQual = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->JpegQual);*/ + break; + + case TAG_MACRO: /* I think this is the size of the Thumbnail */ + ImageInfo->ThumbnailSize = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->ThumbnailSize); + + /* see if we have the offset */ + if (ImageInfo->ThumbnailOffset && ReadThumbnail) { + ExtractThumbnail(ImageInfo, OffsetBase, ExifLength); + } + /*ImageInfo->Macro = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->Macro);*/ + break; + + case TAG_DIGIZOOM: + ImageInfo->DigiZoom = (int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->DigiZoom); + break; + + case TAG_SOFTWARERELEASE: + strlcpy(ImageInfo->SoftwareRelease, ValuePtr, sizeof(ImageInfo->SoftwareRelease)); + break; + + case TAG_PICTINFO: + strlcpy(ImageInfo->PictInfo, ValuePtr, sizeof(ImageInfo->PictInfo)); + break; + + case TAG_CAMERAID: + strlcpy(ImageInfo->CameraId, ValuePtr, sizeof(ImageInfo->CameraId)); + break; + } + + if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) { + char *SubdirStart; + SubdirStart = OffsetBase + Get32u(ValuePtr, ImageInfo->MotorolaOrder); + if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { + php_error(E_WARNING, "Illegal subdirectory link"); + return; + } + ProcessExifDir(ImageInfo, SubdirStart, OffsetBase, ExifLength, LastExifRefd, ReadThumbnail); + continue; + } + } + /* + * Hack to make it process IDF1 I hope + * There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail + */ + NextDirOffset = Get32u(DirStart+2+12*de, ImageInfo->MotorolaOrder); + if (NextDirOffset) { + if (OffsetBase + NextDirOffset < OffsetBase || OffsetBase + NextDirOffset > OffsetBase+ExifLength) { + php_error(E_WARNING, "Illegal directory offset"); + return; + } + ProcessExifDir(ImageInfo, OffsetBase + NextDirOffset, OffsetBase, ExifLength, LastExifRefd, ReadThumbnail); + } } /* }}} */ @@ -929,55 +928,55 @@ static void ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *Offse */ static void process_EXIF (ImageInfoType *ImageInfo, char *CharBuf, unsigned int length, char *LastExifRefd, int ReadThumbnail) { - ImageInfo->FlashUsed = 0; /* If it s from a digicam, and it used flash, it says so. */ - LastExifRefd = CharBuf; - - ImageInfo->FocalplaneXRes = 0; - ImageInfo->FocalplaneUnits = 0; - ImageInfo->ExifImageWidth = 0; - - /* set the thumbnail stuff to nothing so we can test to see if they get set up */ - ImageInfo->Thumbnail = NULL; - ImageInfo->ThumbnailSize = 0; - - { /* Check the EXIF header component */ - static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; - if (memcmp(CharBuf+2, ExifHeader, 6)) { - php_error(E_WARNING, "Incorrect Exif header"); - return; - } - } - - if (memcmp(CharBuf+8, "II", 2) == 0) { + ImageInfo->FlashUsed = 0; /* If it s from a digicam, and it used flash, it says so. */ + LastExifRefd = CharBuf; + + ImageInfo->FocalplaneXRes = 0; + ImageInfo->FocalplaneUnits = 0; + ImageInfo->ExifImageWidth = 0; + + /* set the thumbnail stuff to nothing so we can test to see if they get set up */ + ImageInfo->Thumbnail = NULL; + ImageInfo->ThumbnailSize = 0; + + { /* Check the EXIF header component */ + static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + if (memcmp(CharBuf+2, ExifHeader, 6)) { + php_error(E_WARNING, "Incorrect Exif header"); + return; + } + } + + if (memcmp(CharBuf+8, "II", 2) == 0) { /* if (ShowTags) printf("Exif section in Intel order\n"); */ - ImageInfo->MotorolaOrder = 0; - } else { - if (memcmp(CharBuf+8, "MM", 2) == 0) { + ImageInfo->MotorolaOrder = 0; + } else { + if (memcmp(CharBuf+8, "MM", 2) == 0) { /* if (ShowTags) printf("Exif section in Motorola order\n"); */ - ImageInfo->MotorolaOrder = 1; - } else { - php_error(E_WARNING, "Invalid Exif alignment marker."); - return; - } - } - - /* Check the next two values for correctness. */ - if (Get16u(CharBuf+10, ImageInfo->MotorolaOrder) != 0x2a - || Get32u(CharBuf+12, ImageInfo->MotorolaOrder) != 0x08) { - php_error(E_WARNING, "Invalid Exif start (1)"); - return; - } - - /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */ - ProcessExifDir(ImageInfo, CharBuf+16, CharBuf+8, length-6, LastExifRefd, ReadThumbnail); - - /* MB: This is where I will make my attempt to get the tumbnail */ - - - /* Compute the CCD width, in milimeters. */ - if (ImageInfo->FocalplaneXRes != 0) { - ImageInfo->CCDWidth = (float)(ImageInfo->ExifImageWidth * ImageInfo->FocalplaneUnits / ImageInfo->FocalplaneXRes); - } + ImageInfo->MotorolaOrder = 1; + } else { + php_error(E_WARNING, "Invalid Exif alignment marker."); + return; + } + } + + /* Check the next two values for correctness. */ + if (Get16u(CharBuf+10, ImageInfo->MotorolaOrder) != 0x2a + || Get32u(CharBuf+12, ImageInfo->MotorolaOrder) != 0x08) { + php_error(E_WARNING, "Invalid Exif start (1)"); + return; + } + + /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */ + ProcessExifDir(ImageInfo, CharBuf+16, CharBuf+8, length-6, LastExifRefd, ReadThumbnail); + + /* MB: This is where I will make my attempt to get the tumbnail */ + + + /* Compute the CCD width, in milimeters. */ + if (ImageInfo->FocalplaneXRes != 0) { + ImageInfo->CCDWidth = (float)(ImageInfo->ExifImageWidth * ImageInfo->FocalplaneUnits / ImageInfo->FocalplaneXRes); + } } /* }}} */ @@ -985,142 +984,142 @@ static void process_EXIF (ImageInfoType *ImageInfo, char *CharBuf, unsigned int * Parse the marker stream until SOS or EOI is seen; */ static int scan_JPEG_header (ImageInfoType *ImageInfo, FILE *infile, Section_t *Sections, int *SectionsRead, int ReadThumbnail, int ReadAll, char *LastExifRefd) { - int a; - int HaveCom = FALSE; - - a = fgetc(infile); - if (a != 0xff || fgetc(infile) != M_SOI) { - return FALSE; - } - - for(*SectionsRead=0;*SectionsRead < 19;) { - int itemlen; - int marker = 0; - int ll, lh, got; - uchar *Data; - - for (a=0;a<7;a++) { - marker = fgetc(infile); - if (marker != 0xff) break; - } - if (marker == 0xff) { - /* 0xff is legal padding, but if we get that many, something's wrong. */ - php_error(E_WARNING, "too many padding bytes!"); - return FALSE; - } - - Sections[*SectionsRead].Type = marker; - - /* Read the length of the section. */ - lh = fgetc(infile); - ll = fgetc(infile); - - itemlen = (lh << 8) | ll; - - if (itemlen < 2) { - php_error(E_WARNING, "invalid marker"); - return FALSE; - } - - Sections[*SectionsRead].Size = itemlen; - - Data = (uchar *)emalloc(itemlen+1); /* Add 1 to allow sticking a 0 at the end. */ - 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) { - php_error(E_WARNING, "reading from file"); - return FALSE; - } - *SectionsRead += 1; - - /*printf("Marker '%x' size %d\n", marker, itemlen);*/ - switch(marker) { - case M_SOS: /* stop before hitting compressed data */ - /* If reading entire image is requested, read the rest of the data. */ - if (ReadAll) { - 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 *)emalloc(size); - if (Data == NULL) { - php_error(E_WARNING, "could not allocate data for entire image"); - return FALSE; - } - - got = fread(Data, 1, size, infile); - if (got != size) { - php_error(E_WARNING, "could not read the rest of the image"); - return FALSE; - } - - Sections[*SectionsRead].Data = Data; - Sections[*SectionsRead].Size = size; - Sections[*SectionsRead].Type = PSEUDO_IMAGE_MARKER; - (*SectionsRead)++; - /* - *HaveAll = 1; - */ - // efree(Data); Done in DiscardData - } - return TRUE; - - case M_EOI: /* in case it's a tables-only JPEG stream */ - php_error(E_WARNING, "No image in jpeg!"); - return FALSE; - - case M_COM: /* Comment section */ - /* - if (HaveCom) { - (*SectionsRead) -= 1; - efree(Sections[*SectionsRead].Data); - } else { - process_COM(ImageInfo, Data, itemlen); - HaveCom = TRUE; - } - */ - process_COM(ImageInfo, Data, itemlen); - break; - - case M_EXIF: - if (*SectionsRead <= 2) { - /* Seen files from some 'U-lead' software with Vivitar scanner - that uses marker 31 later in the file (no clue what for!) */ - process_EXIF(ImageInfo, (char *)Data, itemlen, LastExifRefd, ReadThumbnail); - } - 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(ImageInfo, Data, marker); - break; - default: - /* skip any other marker silently. */ - break; - } - } - return TRUE; + int a; + int HaveCom = FALSE; + + a = fgetc(infile); + if (a != 0xff || fgetc(infile) != M_SOI) { + return FALSE; + } + + for(*SectionsRead=0;*SectionsRead < 19;) { + int itemlen; + int marker = 0; + int ll, lh, got; + uchar *Data; + + for (a=0;a<7;a++) { + marker = fgetc(infile); + if (marker != 0xff) break; + } + if (marker == 0xff) { + /* 0xff is legal padding, but if we get that many, something's wrong. */ + php_error(E_WARNING, "too many padding bytes!"); + return FALSE; + } + + Sections[*SectionsRead].Type = marker; + + /* Read the length of the section. */ + lh = fgetc(infile); + ll = fgetc(infile); + + itemlen = (lh << 8) | ll; + + if (itemlen < 2) { + php_error(E_WARNING, "invalid marker"); + return FALSE; + } + + Sections[*SectionsRead].Size = itemlen; + + Data = (uchar *)emalloc(itemlen+1); /* Add 1 to allow sticking a 0 at the end. */ + 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) { + php_error(E_WARNING, "reading from file"); + return FALSE; + } + *SectionsRead += 1; + + /*printf("Marker '%x' size %d\n", marker, itemlen);*/ + switch(marker) { + case M_SOS: /* stop before hitting compressed data */ + /* If reading entire image is requested, read the rest of the data. */ + if (ReadAll) { + 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 *)emalloc(size); + if (Data == NULL) { + php_error(E_WARNING, "could not allocate data for entire image"); + return FALSE; + } + + got = fread(Data, 1, size, infile); + if (got != size) { + php_error(E_WARNING, "could not read the rest of the image"); + return FALSE; + } + + Sections[*SectionsRead].Data = Data; + Sections[*SectionsRead].Size = size; + Sections[*SectionsRead].Type = PSEUDO_IMAGE_MARKER; + (*SectionsRead)++; + /* + *HaveAll = 1; + */ + /* efree(Data); Done in DiscardData */ + } + return TRUE; + + case M_EOI: /* in case it's a tables-only JPEG stream */ + php_error(E_WARNING, "No image in jpeg!"); + return FALSE; + + case M_COM: /* Comment section */ + /* + if (HaveCom) { + (*SectionsRead) -= 1; + efree(Sections[*SectionsRead].Data); + } else { + process_COM(ImageInfo, Data, itemlen); + HaveCom = TRUE; + } + */ + process_COM(ImageInfo, Data, itemlen); + break; + + case M_EXIF: + if (*SectionsRead <= 2) { + /* Seen files from some 'U-lead' software with Vivitar scanner + that uses marker 31 later in the file (no clue what for!) */ + process_EXIF(ImageInfo, (char *)Data, itemlen, LastExifRefd, ReadThumbnail); + } + 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(ImageInfo, Data, marker); + break; + default: + /* skip any other marker silently. */ + break; + } + } + return TRUE; } /* }}} */ @@ -1129,11 +1128,11 @@ static int scan_JPEG_header (ImageInfoType *ImageInfo, FILE *infile, Section_t * */ void DiscardData(Section_t *Sections, int *SectionsRead) { - int a; - for (a=0;a<*SectionsRead-1;a++) { - efree(Sections[a].Data); - } - *SectionsRead = 0; + int a; + for (a=0;a<*SectionsRead-1;a++) { + efree(Sections[a].Data); + } + *SectionsRead = 0; } /* }}} */ @@ -1141,62 +1140,62 @@ void DiscardData(Section_t *Sections, int *SectionsRead) Read image data. */ int ReadJpegFile(ImageInfoType *ImageInfo, Section_t *Sections, - int *SectionsRead, char *FileName, - int ReadThumbnail, int ReadAll, char *LastExifRefd TSRMLS_DC) + int *SectionsRead, char *FileName, + int ReadThumbnail, int ReadAll, char *LastExifRefd TSRMLS_DC) { - FILE *infile; - int ret; - char *tmp; + FILE *infile; + int ret; + char *tmp; - infile = VCWD_FOPEN(FileName, "rb"); /* Unix ignores 'b', windows needs it. */ + infile = VCWD_FOPEN(FileName, "rb"); /* Unix ignores 'b', windows needs it. */ - if (infile == NULL) { - php_error(E_WARNING, "Unable to open '%s'", FileName); - return FALSE; - } + if (infile == NULL) { + php_error(E_WARNING, "Unable to open '%s'", FileName); + return FALSE; + } /* CurrentFile = FileName; */ -// php_error(E_USER_NOTICE,"Process %s%s: %s", ReadThumbnail?"thumbs ":"", ReadAll?"All ":"", FileName); - /* Start with an empty image information structure. */ - memset(ImageInfo, 0, sizeof(*ImageInfo)); - memset(Sections, 0, sizeof(*Sections)); - - tmp = php_basename(FileName, strlen(FileName), NULL, 0); - strlcpy(ImageInfo->FileName, tmp, sizeof(ImageInfo->FileName)); - efree(tmp); - ImageInfo->FocalLength = 0; - ImageInfo->ExposureTime = 0; - ImageInfo->ApertureFNumber = 0; - ImageInfo->Distance = 0; - ImageInfo->CCDWidth = 0; - ImageInfo->FlashUsed = -1; - ImageInfo->SpecialMode = -1; - ImageInfo->JpegQual = -1; - ImageInfo->Macro = -1; - ImageInfo->DigiZoom = -1; - - { - /* Store file date/time. */ - struct stat st; - if (VCWD_STAT(FileName, &st) >= 0) { - ImageInfo->FileDateTime = st.st_mtime; - ImageInfo->FileSize = st.st_size; - } else { - php_error(E_WARNING, "Can't get file statitics"); - return FALSE; - } - } - - /* Scan the JPEG headers. */ - ret = scan_JPEG_header(ImageInfo, infile, Sections, SectionsRead, ReadThumbnail, ReadAll, LastExifRefd); - if (!ret) { - php_error(E_WARNING, "Invalid Jpeg file: '%s'\n", FileName); - return FALSE; - } - - fclose(infile); - - return ret; +/* php_error(E_NOTICE,"EXIF: Process %s%s: %s", ReadThumbnail?"thumbs ":"", ReadAll?"All ":"", FileName); */ + /* Start with an empty image information structure. */ + memset(ImageInfo, 0, sizeof(*ImageInfo)); + memset(Sections, 0, sizeof(*Sections)); + + tmp = php_basename(FileName, strlen(FileName), NULL, 0); + strlcpy(ImageInfo->FileName, tmp, sizeof(ImageInfo->FileName)); + efree(tmp); + ImageInfo->FocalLength = 0; + ImageInfo->ExposureTime = 0; + ImageInfo->ApertureFNumber = 0; + ImageInfo->Distance = 0; + ImageInfo->CCDWidth = 0; + ImageInfo->FlashUsed = -1; + ImageInfo->SpecialMode = -1; + ImageInfo->JpegQual = -1; + ImageInfo->Macro = -1; + ImageInfo->DigiZoom = -1; + + { + /* Store file date/time. */ + struct stat st; + if (VCWD_STAT(FileName, &st) >= 0) { + ImageInfo->FileDateTime = st.st_mtime; + ImageInfo->FileSize = st.st_size; + } else { + php_error(E_WARNING, "Can't get file statitics"); + return FALSE; + } + } + + /* Scan the JPEG headers. */ + ret = scan_JPEG_header(ImageInfo, infile, Sections, SectionsRead, ReadThumbnail, ReadAll, LastExifRefd); + if (!ret) { + php_error(E_WARNING, "Invalid Jpeg file: '%s'\n", FileName); + return FALSE; + } + + fclose(infile); + + return ret; } /* }}} */ @@ -1204,202 +1203,198 @@ int ReadJpegFile(ImageInfoType *ImageInfo, Section_t *Sections, */ int php_read_jpeg_exif(ImageInfoType *ImageInfo, char *FileName, int ReadThumbnail, int ReadAll TSRMLS_DC) { - Section_t Sections[20]; - int SectionsRead; - char *LastExifRefd=NULL; - int ret; - /* int i; */ - - ImageInfo->MotorolaOrder = 0; - - //php_error(E_WARNING, "Read EXIF from %s", FileName); - - ret = ReadJpegFile(ImageInfo, Sections, &SectionsRead, FileName, ReadThumbnail, ReadAll, LastExifRefd TSRMLS_CC); - /* - * Thought this might pick out the embedded thumbnail, but it doesn't work. -RL - for (i=0;i0) { - ImageInfo->Thumbnail = emalloc(thumbsize+5); - ImageInfo->ThumbnailSize = thumbsize; - ImageInfo->Thumbnail[0] = 0xff; - ImageInfo->Thumbnail[1] = 0xd8; - ImageInfo->Thumbnail[2] = 0xff; - memcpy(ImageInfo->Thumbnail+4, Sections[i].Data, thumbsize+4); - } - } - } - */ - if (ret != FALSE) { - DiscardData(Sections, &SectionsRead); - } - return(ret); + Section_t Sections[20]; + int SectionsRead; + char *LastExifRefd=NULL; + int ret; + /* int i; */ + + ImageInfo->MotorolaOrder = 0; + + ret = ReadJpegFile(ImageInfo, Sections, &SectionsRead, FileName, ReadThumbnail, ReadAll, LastExifRefd TSRMLS_CC); + /* + * Thought this might pick out the embedded thumbnail, but it doesn't work. -RL + for (i=0;i0) { + ImageInfo->Thumbnail = emalloc(thumbsize+5); + ImageInfo->ThumbnailSize = thumbsize; + ImageInfo->Thumbnail[0] = 0xff; + ImageInfo->Thumbnail[1] = 0xd8; + ImageInfo->Thumbnail[2] = 0xff; + memcpy(ImageInfo->Thumbnail+4, Sections[i].Data, thumbsize+4); + } + } + } + */ + if (ret != FALSE) { + DiscardData(Sections, &SectionsRead); + } + return(ret); } /* }}} */ -/* {{{ proto string read_exif_data(string filename [, int readall]) - Reads the EXIF header data from a JPEG file */ +/* {{{ proto array read_exif_data(string filename [, readthumbnails [, int readall]]) + Reads the EXIF header data from the JPEG identified by filename and optionally reads the internal thumbnails */ PHP_FUNCTION(read_exif_data) { - pval **p_name, **p_readthumbnail, **p_readall, *tmpi; - int i, ac = ZEND_NUM_ARGS(), ret, readthumbnail=0, readall=0; - ImageInfoType ImageInfo; - char tmp[64]; - - if ((ac < 1 || ac > 3) || zend_get_parameters_ex(ac, &p_name, &p_readthumbnail, &p_readall) == FAILURE) { - WRONG_PARAM_COUNT; - } - - convert_to_string_ex(p_name); - - if(ac >= 2) { - convert_to_long_ex(p_readthumbnail); - readthumbnail = Z_LVAL_PP(p_readthumbnail); - } - if(ac == 3) { - convert_to_long_ex(p_readall); - readall = Z_LVAL_PP(p_readall); - } - - ret = php_read_jpeg_exif(&ImageInfo, Z_STRVAL_PP(p_name), readthumbnail, readall TSRMLS_CC); - - if (ret==FALSE || array_init(return_value) == FAILURE) { - RETURN_FALSE; - } - add_assoc_string(return_value, "FileName", ImageInfo.FileName, 1); - add_assoc_long(return_value, "FileDateTime", ImageInfo.FileDateTime); - add_assoc_long(return_value, "FileSize", ImageInfo.FileSize); - if (ImageInfo.CameraMake) { - if (ImageInfo.CameraMake[0]) { - add_assoc_string(return_value, "CameraMake", ImageInfo.CameraMake, 1); - } - efree(ImageInfo.CameraMake); - } - if (ImageInfo.CameraModel) { - if (ImageInfo.CameraModel[0]) { - add_assoc_string(return_value, "CameraModel", ImageInfo.CameraModel, 1); - } - efree(ImageInfo.CameraModel); - } - if (ImageInfo.DateTime[0]) { - add_assoc_string(return_value, "DateTime", ImageInfo.DateTime, 1); - } - add_assoc_long(return_value, "Height", ImageInfo.Height); - add_assoc_long(return_value, "Width", ImageInfo.Width); - add_assoc_long(return_value, "IsColor", ImageInfo.IsColor); - if(ImageInfo.FlashUsed >= 0) { - add_assoc_long(return_value, "FlashUsed", ImageInfo.FlashUsed); - } - if (ImageInfo.FocalLength) { - sprintf(tmp, "%4.1fmm", ImageInfo.FocalLength); - add_assoc_string(return_value, "FocalLength", tmp, 1); - if(ImageInfo.CCDWidth) { - sprintf(tmp, "%dmm", (int)(ImageInfo.FocalLength/ImageInfo.CCDWidth*35+0.5)); - add_assoc_string(return_value, "35mmFocalLength", tmp, 1); - } - add_assoc_double(return_value, "RawFocalLength", ImageInfo.FocalLength); - } - if(ImageInfo.ExposureTime) { - if(ImageInfo.ExposureTime <= 0.5) { - sprintf(tmp, "%6.3f s (1/%d)", ImageInfo.ExposureTime, (int)(0.5 + 1/ImageInfo.ExposureTime)); - } else { - sprintf(tmp, "%6.3f s", ImageInfo.ExposureTime); - } - add_assoc_string(return_value, "ExposureTime", tmp, 1); - add_assoc_double(return_value, "RawExposureTime", ImageInfo.ExposureTime); - } - if(ImageInfo.ApertureFNumber) { - sprintf(tmp, "f/%4.1f", ImageInfo.ApertureFNumber); - add_assoc_string(return_value, "ApertureFNumber", tmp, 1); - add_assoc_double(return_value, "RawApertureFNumber", ImageInfo.ApertureFNumber); - } - if(ImageInfo.Distance) { - if(ImageInfo.Distance<0) { - add_assoc_string(return_value, "FocusDistance", "Infinite", 1); - } else { - sprintf(tmp, "%5.2fm", ImageInfo.Distance); - add_assoc_string(return_value, "FocusDistance", tmp, 1); - } - add_assoc_double(return_value, "RawFocusDistance", ImageInfo.Distance); - } - if(ImageInfo.CCDWidth) { - add_assoc_double(return_value, "CCDWidth", ImageInfo.CCDWidth); - } - if(ImageInfo.Orientation) { - add_assoc_long(return_value, "Orientation", ImageInfo.Orientation); - } - if (ImageInfo.GPSinfo[0]) { - add_assoc_string(return_value, "GPSinfo", ImageInfo.GPSinfo, 1); - } - if(ImageInfo.ISOspeed) { - add_assoc_long(return_value, "ISOspeed", ImageInfo.ISOspeed); - } - if (ImageInfo.ExifVersion[0]) { - add_assoc_string(return_value, "ExifVersion", ImageInfo.ExifVersion, 1); - } - if (ImageInfo.Copyright) { - if (ImageInfo.Copyright[0]) { - add_assoc_string(return_value, "Copyright", ImageInfo.Copyright, 1); - } - efree(ImageInfo.Copyright); - } - if (ImageInfo.Software) { - if (ImageInfo.Software[0]) { - add_assoc_string(return_value, "Software", ImageInfo.Software, 1); - } - efree(ImageInfo.Software); - } - if(ImageInfo.numComments) { - //php_error(E_WARNING, "Comments:%i", ImageInfo.numComments); - if(ImageInfo.numComments==1) { - add_assoc_string(return_value, "Comments", (ImageInfo.Comments)[0], 1); - } else { - MAKE_STD_ZVAL(tmpi); - array_init(tmpi); - for(i=0; ivalue.ht, "Comments", 9, &tmpi, sizeof(zval *), NULL); - } - for(i=0; i= 0) { - add_assoc_long(return_value, "SpecialMode", ImageInfo.SpecialMode); - } - if(ImageInfo.JpegQual >= 0) { - add_assoc_long(return_value, "JpegQual", ImageInfo.JpegQual); - } - if(ImageInfo.Macro >= 0) { - add_assoc_long(return_value, "Macro", ImageInfo.Macro); - } - if(ImageInfo.DigiZoom >= 0) { - add_assoc_long(return_value, "DigiZoom", ImageInfo.DigiZoom); - } - if (ImageInfo.SoftwareRelease[0]) { - add_assoc_string(return_value, "SoftwareRelease", ImageInfo.SoftwareRelease, 1); - } - if (ImageInfo.PictInfo[0]) { - add_assoc_string(return_value, "PictInfo", ImageInfo.PictInfo, 1); - } - if (ImageInfo.CameraId[0]) { - add_assoc_string(return_value, "CameraId", ImageInfo.CameraId, 1); - } + pval **p_name, **p_readthumbnail, **p_readall, *tmpi; + int i, ac = ZEND_NUM_ARGS(), ret, readthumbnail=0, readall=0; + ImageInfoType ImageInfo; + char tmp[64]; + + if ((ac < 1 || ac > 3) || zend_get_parameters_ex(ac, &p_name, &p_readthumbnail, &p_readall) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(p_name); + + if(ac >= 2) { + convert_to_long_ex(p_readthumbnail); + readthumbnail = Z_LVAL_PP(p_readthumbnail); + } + if(ac == 3) { + convert_to_long_ex(p_readall); + readall = Z_LVAL_PP(p_readall); + } + + ret = php_read_jpeg_exif(&ImageInfo, Z_STRVAL_PP(p_name), readthumbnail, readall TSRMLS_CC); + + if (ret==FALSE || array_init(return_value) == FAILURE) { + RETURN_FALSE; + } + add_assoc_string(return_value, "FileName", ImageInfo.FileName, 1); + add_assoc_long(return_value, "FileDateTime", ImageInfo.FileDateTime); + add_assoc_long(return_value, "FileSize", ImageInfo.FileSize); + if (ImageInfo.CameraMake) { + if (ImageInfo.CameraMake[0]) { + add_assoc_string(return_value, "CameraMake", ImageInfo.CameraMake, 1); + } + efree(ImageInfo.CameraMake); + } + if (ImageInfo.CameraModel) { + if (ImageInfo.CameraModel[0]) { + add_assoc_string(return_value, "CameraModel", ImageInfo.CameraModel, 1); + } + efree(ImageInfo.CameraModel); + } + if (ImageInfo.DateTime[0]) { + add_assoc_string(return_value, "DateTime", ImageInfo.DateTime, 1); + } + add_assoc_long(return_value, "Height", ImageInfo.Height); + add_assoc_long(return_value, "Width", ImageInfo.Width); + add_assoc_long(return_value, "IsColor", ImageInfo.IsColor); + if(ImageInfo.FlashUsed >= 0) { + add_assoc_long(return_value, "FlashUsed", ImageInfo.FlashUsed); + } + if (ImageInfo.FocalLength) { + sprintf(tmp, "%4.1fmm", ImageInfo.FocalLength); + add_assoc_string(return_value, "FocalLength", tmp, 1); + if(ImageInfo.CCDWidth) { + sprintf(tmp, "%dmm", (int)(ImageInfo.FocalLength/ImageInfo.CCDWidth*35+0.5)); + add_assoc_string(return_value, "35mmFocalLength", tmp, 1); + } + add_assoc_double(return_value, "RawFocalLength", ImageInfo.FocalLength); + } + if(ImageInfo.ExposureTime) { + if(ImageInfo.ExposureTime <= 0.5) { + sprintf(tmp, "%6.3f s (1/%d)", ImageInfo.ExposureTime, (int)(0.5 + 1/ImageInfo.ExposureTime)); + } else { + sprintf(tmp, "%6.3f s", ImageInfo.ExposureTime); + } + add_assoc_string(return_value, "ExposureTime", tmp, 1); + add_assoc_double(return_value, "RawExposureTime", ImageInfo.ExposureTime); + } + if(ImageInfo.ApertureFNumber) { + sprintf(tmp, "f/%4.1f", ImageInfo.ApertureFNumber); + add_assoc_string(return_value, "ApertureFNumber", tmp, 1); + add_assoc_double(return_value, "RawApertureFNumber", ImageInfo.ApertureFNumber); + } + if(ImageInfo.Distance) { + if(ImageInfo.Distance<0) { + add_assoc_string(return_value, "FocusDistance", "Infinite", 1); + } else { + sprintf(tmp, "%5.2fm", ImageInfo.Distance); + add_assoc_string(return_value, "FocusDistance", tmp, 1); + } + add_assoc_double(return_value, "RawFocusDistance", ImageInfo.Distance); + } + if(ImageInfo.CCDWidth) { + add_assoc_double(return_value, "CCDWidth", ImageInfo.CCDWidth); + } + if(ImageInfo.Orientation) { + add_assoc_long(return_value, "Orientation", ImageInfo.Orientation); + } + if (ImageInfo.GPSinfo[0]) { + add_assoc_string(return_value, "GPSinfo", ImageInfo.GPSinfo, 1); + } + if(ImageInfo.ISOspeed) { + add_assoc_long(return_value, "ISOspeed", ImageInfo.ISOspeed); + } + if (ImageInfo.ExifVersion[0]) { + add_assoc_string(return_value, "ExifVersion", ImageInfo.ExifVersion, 1); + } + if (ImageInfo.Copyright) { + if (ImageInfo.Copyright[0]) { + add_assoc_string(return_value, "Copyright", ImageInfo.Copyright, 1); + } + efree(ImageInfo.Copyright); + } + if (ImageInfo.Software) { + if (ImageInfo.Software[0]) { + add_assoc_string(return_value, "Software", ImageInfo.Software, 1); + } + efree(ImageInfo.Software); + } + if(ImageInfo.numComments) { + if(ImageInfo.numComments==1) { + add_assoc_string(return_value, "Comments", (ImageInfo.Comments)[0], 1); + } else { + MAKE_STD_ZVAL(tmpi); + array_init(tmpi); + for(i=0; ivalue.ht, "Comments", 9, &tmpi, sizeof(zval *), NULL); + } + for(i=0; i= 0) { + add_assoc_long(return_value, "SpecialMode", ImageInfo.SpecialMode); + } + if(ImageInfo.JpegQual >= 0) { + add_assoc_long(return_value, "JpegQual", ImageInfo.JpegQual); + } + if(ImageInfo.Macro >= 0) { + add_assoc_long(return_value, "Macro", ImageInfo.Macro); + } + if(ImageInfo.DigiZoom >= 0) { + add_assoc_long(return_value, "DigiZoom", ImageInfo.DigiZoom); + } + if (ImageInfo.SoftwareRelease[0]) { + add_assoc_string(return_value, "SoftwareRelease", ImageInfo.SoftwareRelease, 1); + } + if (ImageInfo.PictInfo[0]) { + add_assoc_string(return_value, "PictInfo", ImageInfo.PictInfo, 1); + } + if (ImageInfo.CameraId[0]) { + add_assoc_string(return_value, "CameraId", ImageInfo.CameraId, 1); + } } /* }}} */ -- 2.40.0