summaryrefslogtreecommitdiff
path: root/ext/exif/exif.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/exif/exif.c')
-rw-r--r--ext/exif/exif.c1282
1 files changed, 0 insertions, 1282 deletions
diff --git a/ext/exif/exif.c b/ext/exif/exif.c
deleted file mode 100644
index e3e8e7cedf..0000000000
--- a/ext/exif/exif.c
+++ /dev/null
@@ -1,1282 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | PHP version 4.0 |
- +----------------------------------------------------------------------+
- | Copyright (c) 1997-2001 The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 2.02 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available at through the world-wide-web at |
- | http://www.php.net/license/2_02.txt. |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Rasmus Lerdorf <rasmus@php.net> |
- +----------------------------------------------------------------------+
- */
-/* Much of the code in this module was borrowed from the public domain
- jhead.c package with the author's consent. The main changes have been
- to eliminate all the global variables to make it thread safe and to wrap
- it in the PHP 4 API.
-
- Aug.3 2001 - Added support for multiple M_COM entries - Rasmus
-
- The original header from the jhead.c file was:
-
- --------------------------------------------------------------------------
- Program to pull the information out of various types of EFIF digital
- camera files and show it in a reasonably consistent way
-
- Version 0.9
-
- Compiles with MSVC on Windows, or with GCC on Linux
-
- Compileing under linux: Must include math library.
- Use: cc -lm -O3 -o jhead jhead.c
-
- Matthias Wandel, Dec 1999 - April 2000
- --------------------------------------------------------------------------
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "php.h"
-
-#if HAVE_EXIF
-
-#include "php_exif.h"
-#include <math.h>
-#include "php_ini.h"
-#include "ext/standard/php_string.h"
-#include "ext/standard/info.h"
-
-typedef unsigned char uchar;
-
-#ifndef TRUE
- #define TRUE 1
- #define FALSE 0
-#endif
-
-#define EXIF_MAX_COMMENTS 12
-
-/* {{{ structs
- This structure stores Exif header image elements in a simple manner
- Used to store camera data as extracted from the various ways that it can be
- stored in a nexif header
-*/
-typedef struct {
- char FileName [120];
- time_t FileDateTime;
- unsigned FileSize;
- char CameraMake [32];
- char CameraModel [64];
- char DateTime [20];
- int Height, Width;
- int IsColor;
- int FlashUsed;
- float FocalLength;
- float ExposureTime;
- float ApertureFNumber;
- float Distance;
- float CCDWidth;
- char *Comments[EXIF_MAX_COMMENTS];
- int numComments;
- double FocalplaneXRes;
- double FocalplaneUnits;
- int ExifImageWidth;
- int MotorolaOrder;
- int Orientation;
- char GPSinfo[48];
- int ISOspeed;
- char ExifVersion[16];
- char Copyright[32];
- char Software[32];
- 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;
-} Section_t;
-/* }}} */
-
-#define EXIT_FAILURE 1
-#define EXIT_SUCCESS 0
-
-/* {{{ exif_functions[]
- */
-function_entry exif_functions[] = {
- PHP_FE(read_exif_data, NULL)
- {NULL, NULL, NULL}
-};
-/* }}} */
-
-PHP_MINFO_FUNCTION(exif);
-
-/* {{{ exif_module_entry
- */
-zend_module_entry exif_module_entry = {
- "exif",
- exif_functions,
- NULL, NULL,
- NULL, NULL,
- PHP_MINFO(exif),
- STANDARD_MODULE_PROPERTIES
-};
-/* }}} */
-
-#ifdef COMPILE_DL_EXIF
-ZEND_GET_MODULE(exif)
-#endif
-
-/* {{{ PHP_MINFO_FUNCTION
- */
-PHP_MINFO_FUNCTION(exif)
-{
- php_info_print_table_start();
- php_info_print_table_row(2, "EXIF Support", "enabled" );
- php_info_print_table_end();
-}
-/* }}} */
-
-/* {{{ Markers
- JPEG markers consist of one or more 0xFF bytes, followed by a marker
- code byte (which is not an FF). Here are the marker codes of interest
- in this program. (See jdmarker.c for a more complete list.)
-*/
-
-#define M_SOF0 0xC0 /* Start Of Frame N */
-#define M_SOF1 0xC1 /* N indicates which compression process */
-#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */
-#define M_SOF3 0xC3
-#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */
-#define M_SOF6 0xC6
-#define M_SOF7 0xC7
-#define M_SOF9 0xC9
-#define M_SOF10 0xCA
-#define M_SOF11 0xCB
-#define M_SOF13 0xCD
-#define M_SOF14 0xCE
-#define M_SOF15 0xCF
-#define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */
-#define M_EOI 0xD9 /* End Of Image (end of datastream) */
-#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
-#define M_EXIF 0xE1
-#define M_COM 0xFE /* COMment */
-
-
-#define PSEUDO_IMAGE_MARKER 0x123; /* Extra value. */
-
-/* }}} */
-
-/* {{{ Get16m
- Get 16 bits motorola order (always) for jpeg header stuff.
-*/
-static int Get16m(void *Short)
-{
- return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
-}
-/* }}} */
-
-/* {{{ process_COM
- Process a COM marker.
- We want to print out the marker contents as legible text;
- we must guard against random junk and varying newline representations.
-*/
-static void process_COM (ImageInfoType *ImageInfo, uchar *Data, int length)
-{
- int ch;
- char Comment[250];
- int nch;
- int a;
-
- if(ImageInfo->numComments == EXIF_MAX_COMMENTS) return;
-
- nch = 0;
-
- if (length > 200) length = 200; /* Truncate if it won't fit in our structure. */
-
- for (a=2;a<length;a++) {
- ch = Data[a];
-
- if (ch == '\r' && Data[a+1] == '\n') continue; /* Remove cr followed by lf. */
-
- if (isprint(ch) || ch == '\n' || ch == '\t') {
- Comment[nch++] = (char)ch;
- } else {
- Comment[nch++] = '?';
- }
- }
-
- Comment[nch] = '\0'; /* Null terminate */
-
- a = ImageInfo->numComments;
-
- (ImageInfo->Comments)[a] = emalloc(nch+1);
- strcpy(ImageInfo->Comments[a],Comment);
- (ImageInfo->numComments)++;
-}
-/* }}} */
-
-/* {{{ process_SOFn
- * 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;
- }
-}
-/* }}} */
-
-/* {{{ format description defines
- Describes format descriptor
-*/
-static int ExifBytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
-#define NUM_FORMATS 12
-
-#define FMT_BYTE 1
-#define FMT_STRING 2
-#define FMT_USHORT 3
-#define FMT_ULONG 4
-#define FMT_URATIONAL 5
-#define FMT_SBYTE 6
-#define FMT_UNDEFINED 7
-#define FMT_SSHORT 8
-#define FMT_SLONG 9
-#define FMT_SRATIONAL 10
-#define FMT_SINGLE 11
-#define FMT_DOUBLE 12
-
-/*
- Describes tag values
-*/
-
-#define TAG_EXIF_OFFSET 0x8769
-#define TAG_INTEROP_OFFSET 0xa005
-
-#define TAG_COMPRESSION 0x0103
-
-#define TAG_MAKE 0x010F
-#define TAG_MODEL 0x0110
-#define TAG_ORIENTATION 0x0112
-
-#define TAG_SOFTWARE 0x0131
-
-/* Olympus specific tags */
-#define TAG_SPECIALMODE 0x0200
-#define TAG_JPEGQUAL 0x0201
-#define TAG_MACRO 0x0202
-#define TAG_DIGIZOOM 0x0204
-#define TAG_SOFTWARERELEASE 0x0207
-#define TAG_PICTINFO 0x0208
-#define TAG_CAMERAID 0x0209
-/* end Olympus specific tags */
-
-#define TAG_COPYRIGHT 0x8298
-
-#define TAG_EXPOSURETIME 0x829A
-#define TAG_FNUMBER 0x829D
-
-#define TAG_GPSINFO 0x8825
-#define TAG_ISOSPEED 0x8827
-#define TAG_EXIFVERSION 0x9000
-
-#define TAG_SHUTTERSPEED 0x9201
-#define TAG_APERTURE 0x9202
-#define TAG_MAXAPERTURE 0x9205
-#define TAG_FOCALLENGTH 0x920A
-
-#define TAG_DATETIME_ORIGINAL 0x9003
-#define TAG_USERCOMMENT 0x9286
-
-#define TAG_SUBJECT_DISTANCE 0x9206
-#define TAG_LIGHT_SOURCE 0x9208
-#define TAG_FLASH 0x9209
-
-#define TAG_FOCALPLANEXRES 0xa20E
-#define TAG_FOCALPLANEUNITS 0xa210
-#define TAG_IMAGEWIDTH 0xA002
-/* }}} */
-
-/* {{{ TabTable[]
- */
-static const struct {
- unsigned short Tag;
- char *Desc;
-} TagTable[] = {
- { 0x100, "ImageWidth"},
- { 0x101, "ImageLength"},
- { 0x102, "BitsPerSample"},
- { 0x103, "Compression"},
- { 0x106, "PhotometricInterpretation"},
- { 0x10A, "FillOrder"},
- { 0x10D, "DocumentName"},
- { 0x10E, "ImageDescription"},
- { 0x10F, "Make"},
- { 0x110, "Model"},
- { 0x111, "StripOffsets"},
- { 0x112, "Orientation"},
- { 0x115, "SamplesPerPixel"},
- { 0x116, "RowsPerStrip"},
- { 0x117, "StripByteCounts"},
- { 0x11A, "XResolution"},
- { 0x11B, "YResolution"},
- { 0x11C, "PlanarConfiguration"},
- { 0x128, "ResolutionUnit"},
- { 0x12D, "TransferFunction"},
- { 0x131, "Software"},
- { 0x132, "DateTime"},
- { 0x13B, "Artist"},
- { 0x13E, "WhitePoint"},
- { 0x13F, "PrimaryChromaticities"},
- { 0x156, "TransferRange"},
- { 0x200, "JPEGProc"},
- { 0x201, "JPEGInterchangeFormat"},
- { 0x202, "JPEGInterchangeFormatLength"},
- { 0x211, "YCbCrCoefficients"},
- { 0x212, "YCbCrSubSampling"},
- { 0x213, "YCbCrPositioning"},
- { 0x214, "ReferenceBlackWhite"},
- { 0x1000, "RelatedImageFileFormat"},
- { 0x828D, "CFARepeatPatternDim"},
- { 0x828E, "CFAPattern"},
- { 0x828F, "BatteryLevel"},
- { 0x8298, "Copyright"},
- { 0x829A, "ExposureTime"},
- { 0x829D, "FNumber"},
- { 0x83BB, "IPTC/NAA"},
- { 0x8769, "ExifOffset"},
- { 0x8773, "InterColorProfile"},
- { 0x8822, "ExposureProgram"},
- { 0x8824, "SpectralSensitivity"},
- { 0x8825, "GPSInfo"},
- { 0x8827, "ISOSpeedRatings"},
- { 0x8828, "OECF"},
- { 0x9000, "ExifVersion"},
- { 0x9003, "DateTimeOriginal"},
- { 0x9004, "DateTimeDigitized"},
- { 0x9101, "ComponentsConfiguration"},
- { 0x9102, "CompressedBitsPerPixel"},
- { 0x9201, "ShutterSpeedValue"},
- { 0x9202, "ApertureValue"},
- { 0x9203, "BrightnessValue"},
- { 0x9204, "ExposureBiasValue"},
- { 0x9205, "MaxApertureValue"},
- { 0x9206, "SubjectDistance"},
- { 0x9207, "MeteringMode"},
- { 0x9208, "LightSource"},
- { 0x9209, "Flash"},
- { 0x920A, "FocalLength"},
- { 0x927C, "MakerNote"},
- { 0x9286, "UserComment"},
- { 0x9290, "SubSecTime"},
- { 0x9291, "SubSecTimeOriginal"},
- { 0x9292, "SubSecTimeDigitized"},
- { 0xA000, "FlashPixVersion"},
- { 0xA001, "ColorSpace"},
- { 0xA002, "ExifImageWidth"},
- { 0xA003, "ExifImageLength"},
- { 0xA005, "InteroperabilityOffset"},
- { 0xA20B, "FlashEnergy"}, /* 0x920B in TIFF/EP */
- { 0xA20C, "SpatialFrequencyResponse"}, /* 0x920C - - */
- { 0xA20E, "FocalPlaneXResolution"}, /* 0x920E - - */
- { 0xA20F, "FocalPlaneYResolution"}, /* 0x920F - - */
- { 0xA210, "FocalPlaneResolutionUnit"}, /* 0x9210 - - */
- { 0xA214, "SubjectLocation"}, /* 0x9214 - - */
- { 0xA215, "ExposureIndex"}, /* 0x9215 - - */
- { 0xA217, "SensingMethod"}, /* 0x9217 - - */
- { 0xA300, "FileSource"},
- { 0xA301, "SceneType"},
- { 0, NULL}
-} ;
-/* }}} */
-
-/* {{{ Get16u
- * 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];
- }
-}
-/* }}} */
-
-/* {{{ Get32s
- * 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 );
- }
-}
-/* }}} */
-
-/* {{{ Get32u
- * 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;
-}
-/* }}} */
-
-/* {{{ ConvertAnyFormat
- * 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;
-}
-/* }}} */
-
-/* {{{ ExtractThumbnail
- * Grab the thumbnail - by Matt Bonneau */
-static void ExtractThumbnail(ImageInfoType *ImageInfo, char *OffsetBase, unsigned ExifLength) {
- /* according to exif2.1, the thumbnail is not supposed to be greater than 64K */
- if (ImageInfo->ThumbnailSize > 65536) {
- php_error(E_WARNING,"Illegal thumbnail size");
- return;
- }
-
- ImageInfo->Thumbnail = emalloc(ImageInfo->ThumbnailSize);
- if (!ImageInfo->Thumbnail) {
- php_error(E_WARNING,"Could not allocate memory for thumbnail");
- return;
- } else {
- /* Check to make sure we are not going to go past the ExifLength */
- if ((unsigned)(ImageInfo->ThumbnailOffset + ImageInfo->ThumbnailSize) > ExifLength) {
- php_error(E_WARNING,"Thumbnail goes beyond exif header boundary");
- return;
- } else {
- memcpy(ImageInfo->Thumbnail, OffsetBase + ImageInfo->ThumbnailOffset, ImageInfo->ThumbnailSize);
- }
- }
-}
-/* }}} */
-
-/* {{{ ProcessExifDir
- * Process one of the nested EXIF directories. */
-static void ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *OffsetBase, unsigned ExifLength, char *LastExifRefd)
-{
- int de;
- int a;
- int NumDirEntries;
- int NextDirOffset;
-
-
- NumDirEntries = Get16u(DirStart, ImageInfo->MotorolaOrder);
-
- if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) {
- php_error(E_WARNING,"Illegally sized directory");
- return;
- }
-
-
- /*
- if (ShowTags) {
- printf("Directory with %d entries\n",NumDirEntries);
- }
- */
-
- for (de=0;de<NumDirEntries;de++) {
- int Tag, Format, Components;
- char *ValuePtr;
- int ByteCount;
- char *DirEntry;
- DirEntry = DirStart+2+12*de;
-
- Tag = Get16u(DirEntry, ImageInfo->MotorolaOrder);
- 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");
- return;
- }
- ValuePtr = OffsetBase+OffsetVal;
- } else {
- /* 4 bytes or less and value is in the dir entry itself */
- ValuePtr = DirEntry+8;
- }
-
- if (LastExifRefd < ValuePtr+ByteCount) {
- /*
- Keep track of last byte in the exif header that was actually referenced.
- That way, we know where the discardable thumbnail data begins.
- */
- LastExifRefd = ValuePtr+ByteCount;
- }
-
- /* Extract useful components of tag */
- switch(Tag) {
-
- case TAG_MAKE:
- strlcpy(ImageInfo->CameraMake, ValuePtr, sizeof(ImageInfo->CameraMake));
- break;
-
- case TAG_MODEL:
- strlcpy(ImageInfo->CameraModel, ValuePtr, sizeof(ImageInfo->CameraModel));
- break;
-
- case TAG_GPSINFO:
- strlcpy(ImageInfo->GPSinfo, ValuePtr, sizeof(ImageInfo->GPSinfo));
- break;
-
- case TAG_EXIFVERSION:
- strlcpy(ImageInfo->ExifVersion, ValuePtr, sizeof(ImageInfo->ExifVersion));
- break;
-
- case TAG_COPYRIGHT:
- strlcpy(ImageInfo->Copyright, ValuePtr, sizeof(ImageInfo->Copyright));
- break;
-
- case TAG_SOFTWARE:
- strlcpy(ImageInfo->Software, ValuePtr, sizeof(ImageInfo->Software));
- 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_USERCOMMENT:
- /* Olympus has this padded with trailing spaces. Remove these first. */
- for (a=ByteCount;;) {
- a--;
- if ((ValuePtr)[a] == ' ') {
- (ValuePtr)[a] = '\0';
- } else {
- break;
- }
- if (a == 0) break;
- }
-
- /* Copy the comment */
- if (memcmp(ValuePtr, "ASCII",5) == 0) {
- for (a=5;a<10;a++) {
- int c; int l;
- c = (ValuePtr)[a];
- if (c != '\0' && c != ' ') {
- l = strlen(a+ValuePtr)+1;
- l = (l<200)?l:201;
- (ImageInfo->Comments)[ImageInfo->numComments] = emalloc(l);
- strlcpy((ImageInfo->Comments)[ImageInfo->numComments], a+ValuePtr, l);
- ImageInfo->numComments++;
- break;
- }
- }
-
- } else {
- int l;
-
- l = strlen(ValuePtr)+1;
- l = (l<200)?l:201;
- (ImageInfo->Comments)[ImageInfo->numComments] = emalloc(l);
- strlcpy((ImageInfo->Comments)[ImageInfo->numComments], ValuePtr, l);
- }
- 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) {
- 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) {
- 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);
- 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);
- }
-}
-/* }}} */
-
-/* {{{ process_EXIF
- Process an EXIF marker
- Describes all the drivel that most digital cameras include...
-*/
-static void process_EXIF (ImageInfoType *ImageInfo, char *CharBuf, unsigned int length, char *LastExifRefd)
-{
- 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) {
-/* 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);
-
- /* 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);
- }
-}
-/* }}} */
-
-/* {{{ scan_JPEG_header
- * 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 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);
- }
- 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);
- }
- 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;
-}
-/* }}} */
-
-/* {{{ DiscardData
- Discard read data.
-*/
-void DiscardData(Section_t *Sections, int *SectionsRead)
-{
- int a;
- for (a=0;a<*SectionsRead-1;a++) {
- efree(Sections[a].Data);
- }
- *SectionsRead = 0;
-}
-/* }}} */
-
-/* {{{ ReadJpegFile
- Read image data.
-*/
-int ReadJpegFile(ImageInfoType *ImageInfo, Section_t *Sections,
- int *SectionsRead, char *FileName,
- int ReadAll, char *LastExifRefd TSRMLS_DC)
-{
- FILE *infile;
- int ret;
- char *tmp;
-
- 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;
- }
-/* CurrentFile = 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, ReadAll, LastExifRefd);
- if (!ret) {
- php_error(E_WARNING,"Invalid Jpeg file: '%s'\n",FileName);
- return FALSE;
- }
-
- fclose(infile);
-
- return ret;
-}
-/* }}} */
-
-/* {{{ php_read_jpeg_exif
- */
-int php_read_jpeg_exif(ImageInfoType *ImageInfo, char *FileName, int ReadAll TSRMLS_DC)
-{
- Section_t Sections[20];
- int SectionsRead;
- char *LastExifRefd=NULL;
- int ret;
- /* int i; */
-
- ImageInfo->MotorolaOrder = 0;
-
- ret = ReadJpegFile(ImageInfo, Sections, &SectionsRead, FileName, ReadAll, LastExifRefd TSRMLS_CC);
- /*
- * Thought this might pick out the embedded thumbnail, but it doesn't work. -RL
- for (i=0;i<SectionsRead-1;i++) {
- if (Sections[i].Type == M_EXIF) {
- thumbsize = Sections[i].Size;
- if(thumbsize>0) {
- 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 */
-PHP_FUNCTION(read_exif_data)
-{
- pval **p_name, **p_readall, *tmpi;
- int ac = ZEND_NUM_ARGS(), ret, readall=1;
- ImageInfoType ImageInfo;
- char tmp[64];
-
- if ((ac < 1 || ac > 2) || zend_get_parameters_ex(ac, &p_name, &p_readall) == FAILURE) {
- WRONG_PARAM_COUNT;
- }
-
- convert_to_string_ex(p_name);
-
- if(ac == 2) {
- convert_to_long_ex(p_readall);
- readall = Z_LVAL_PP(p_readall);
- }
-
- ret = php_read_jpeg_exif(&ImageInfo, Z_STRVAL_PP(p_name), 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[0]) {
- add_assoc_string(return_value,"CameraMake",ImageInfo.CameraMake,1);
- }
- if (ImageInfo.CameraModel[0]) {
- add_assoc_string(return_value,"CameraModel",ImageInfo.CameraModel,1);
- }
- 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[0]) {
- add_assoc_string(return_value,"Copyright",ImageInfo.Copyright,1);
- }
- if (ImageInfo.Software[0]) {
- add_assoc_string(return_value,"Software",ImageInfo.Software,1);
- }
- if(ImageInfo.numComments) {
- if(ImageInfo.numComments==1) {
- add_assoc_string(return_value,"Comments",(ImageInfo.Comments)[0],0);
- } else {
- int i;
-
- MAKE_STD_ZVAL(tmpi);
- array_init(tmpi);
- for(i=0; i<ImageInfo.numComments; i++) {
- add_index_string(tmpi, i, (ImageInfo.Comments)[i], 0);
- }
- zend_hash_update(return_value->value.ht, "Comments", 9, &tmpi, sizeof(zval *), NULL);
- }
- }
- if(ImageInfo.ThumbnailSize && ImageInfo.Thumbnail) {
- add_assoc_stringl(return_value,"Thumbnail",ImageInfo.Thumbnail,ImageInfo.ThumbnailSize,1);
- add_assoc_long(return_value,"ThumbnailSize",ImageInfo.ThumbnailSize);
- efree(ImageInfo.Thumbnail);
- }
- if(ImageInfo.SpecialMode >= 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);
- }
-}
-/* }}} */
-
-#endif
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: sw=4 ts=4 tw=78 fdm=marker
- * vim<600: sw=4 ts=4 tw=78
- */