diff options
Diffstat (limited to 'src/libexif/exif.c')
-rw-r--r-- | src/libexif/exif.c | 1274 |
1 files changed, 0 insertions, 1274 deletions
diff --git a/src/libexif/exif.c b/src/libexif/exif.c deleted file mode 100644 index 3be72a3..0000000 --- a/src/libexif/exif.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* - -Copyright © 2000 Matthias Wandel, The PHP Group, Curtis Galloway - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -#include <sys/time.h> -#include <malloc.h> -#include <stdio.h> -#include <string.h> - -#include <math.h> -#include <sys/stat.h> -#include <stdarg.h> -#include <fcntl.h> - -#include "exif.h" - -typedef unsigned char uchar; - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -/* - This structure stores global state for an EXIF image file. -*/ -typedef struct { - exif_data_t *d; - int MotorolaOrder; - const char *filename; - - char *Thumbnail; - int ThumbnailSize; -} ImageInfoType; - -void *(*exif_malloc_fn)(int); -void *(*exif_realloc_fn)(void *, int); -void (*exif_free_fn)(void *); - -static char * -exif_strndup(char *str, int len) -{ - char *rval = (*exif_malloc_fn)(len+1); - strncpy(rval, str, len); - rval[len] = '\0'; - return rval; -} - -struct exif_data * -exif_alloc(void) -{ - exif_data_t *d; - - d = (*exif_malloc_fn)(sizeof(exif_data_t)); - bzero(d, sizeof(*d)); - return d; -} - -static void -exif_error(char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); -} - - -/* 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 - - -/* - 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. */ - -#define EXIF_ALLOC_SIZE 16 - -/* - * The name gets copied, so you can pass a static string; - * the data is not copied, so if it is a string, - * you must allocate it yourself. - */ -static int -exif_append_data(exif_data_t **d_p, - char *name, - char rec_type, - int exif_format, - exif_rec_data_t *data) -{ - exif_data_t *d = *d_p; - - if (rec_type == '\0') - return EXIT_FAILURE; - - if (d->n_alloc <= d->n_recs) { - d->n_alloc += EXIF_ALLOC_SIZE; - d = (*exif_realloc_fn)(d, sizeof(exif_data_t) + - sizeof(exif_record_t) * d->n_alloc); - *d_p = d; - } - d->recs[d->n_recs].rec_type = rec_type; - bcopy(data, &d->recs[d->n_recs].rec_data, sizeof(exif_rec_data_t)); - d->recs[d->n_recs].rec_name = (char*)strdup(name); - d->n_recs++; - return EXIT_SUCCESS; -} - -/* - Get 16 bits motorola order (always) for jpeg header stuff. -*/ -static int -Get16m(void *Short) -{ - return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; -} - - -/* - 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; - int nch; - int a; - exif_rec_data_t rd; - - nch = 0; - Comment = (*exif_malloc_fn)(length+1); - - 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 */ - - rd.s = Comment; - exif_append_data(&ImageInfo->d, "Comment", 's', EXIF_FMT_COMPUTED, &rd); -} - -/* 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; - exif_rec_data_t rd; - - data_precision = Data[2]; - rd.l = Get16m(Data+3); - exif_append_data(&ImageInfo->d, - "Height", - 'l', - EXIF_FMT_COMPUTED, - &rd); - rd.l = Get16m(Data+5); - exif_append_data(&ImageInfo->d, - "Width", - 'l', - EXIF_FMT_COMPUTED, - &rd); - num_components = Data[7]; - - if (num_components == 3) { - rd.l = 1; - } else { - rd.l = 0; - } - exif_append_data(&ImageInfo->d, "IsColor", 'l', EXIF_FMT_COMPUTED, &rd); - - 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; - } -} - -/* - Describes format descriptor -*/ -static int ExifBytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; -#define NUM_FORMATS EXIF_FMT_DOUBLE - -/* - 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 - -struct ExifTag { - unsigned short Tag; - char *Desc; - void (*Func)(); -}; - - - -/* 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]; - } -} - -/* 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 ); - } -} - -/* 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; -} - - -/* 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 EXIF_FMT_SBYTE: Value = *(signed char *)ValuePtr; break; - case EXIF_FMT_BYTE: Value = *(uchar *)ValuePtr; break; - - case EXIF_FMT_USHORT: Value = Get16u(ValuePtr,MotorolaOrder); break; - case EXIF_FMT_ULONG: Value = Get32u(ValuePtr,MotorolaOrder); break; - - case EXIF_FMT_URATIONAL: - case EXIF_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 EXIF_FMT_SSHORT: Value = (signed short)Get16u(ValuePtr,MotorolaOrder); break; - case EXIF_FMT_SLONG: Value = Get32s(ValuePtr,MotorolaOrder); break; - - /* Not sure if this is correct (never seen float used in Exif format) */ - case EXIF_FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; - case EXIF_FMT_DOUBLE: Value = *(double *)ValuePtr; break; - } - return Value; -} - -/* Evaluate number, be it int, rational, or float from directory. */ -static char -ConvertAnyFormat2(void *ValuePtr, int ByteCount, int Format, int MotorolaOrder, exif_rec_data_t *data_p) -{ - char *str, *p; - char r_type; - unsigned char c; - static char hexdigits[] = "0123456789ABCDEF"; - - switch(Format) { - case EXIF_FMT_STRING: - data_p->s = exif_strndup(ValuePtr, ByteCount); - r_type = 's'; - break; - - case EXIF_FMT_SBYTE: - data_p->l = (long)*(signed char *)ValuePtr; - r_type = 'l'; - break; - - case EXIF_FMT_BYTE: - data_p->l = (long)*(uchar *)ValuePtr; - r_type = 'l'; - break; - - case EXIF_FMT_USHORT: - data_p->l = (long)Get16u(ValuePtr,MotorolaOrder); - r_type = 'l'; - break; - case EXIF_FMT_ULONG: - data_p->l = (long)Get32u(ValuePtr,MotorolaOrder); - r_type = 'l'; - break; - - case EXIF_FMT_URATIONAL: - case EXIF_FMT_SRATIONAL: - { - int Num,Den; - data_p->r.num = Get32s(ValuePtr,MotorolaOrder); - data_p->r.denom = Get32s(4+(char *)ValuePtr,MotorolaOrder); - r_type = 'r'; - break; - } - - case EXIF_FMT_SSHORT: - data_p->l = (signed short)Get16u(ValuePtr,MotorolaOrder); - r_type = 'l'; - break; - case EXIF_FMT_SLONG: - data_p->l = (long)Get32s(ValuePtr,MotorolaOrder); - r_type = 'l'; - break; - - /* Not sure if this is correct (never seen float used in Exif format) */ - case EXIF_FMT_SINGLE: - data_p->f = *(float *)ValuePtr; - r_type = 'f'; - break; - - case EXIF_FMT_DOUBLE: - data_p->g = *(double *)ValuePtr; - r_type = 'f'; - break; - - default: - /* unknown type */ - p = str = (*exif_malloc_fn)(ByteCount*2 + 1); - while (ByteCount--) { - c = *(unsigned char *)ValuePtr++; - *p++ = hexdigits[c / 16]; - *p++ = hexdigits[c % 16]; - } - *p++ = '\0'; - data_p->s = str; - r_type = 's'; - break; - } - return r_type; -} - - -static void -ProcessFocalPlaneUnits(ImageInfoType *ImageInfo, - void *ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p) -{ - exif_rec_data_t rd; - float FocalPlaneUnits; - - switch((int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder)) { - case 1: - FocalPlaneUnits = 25.4; - break; /* inch */ - case 2: - /* According to the information I was using, 2 means meters. - But looking at the Canon PowerShot's files, inches is the only - sensible value. */ - FocalPlaneUnits = 25.4; - break; - - case 3: - FocalPlaneUnits = 10; - break; /* centimeter */ - case 4: - FocalPlaneUnits = 1; - break; /* milimeter */ - case 5: - FocalPlaneUnits = .001; - break; /* micrometer */ - } - - rd.f = FocalPlaneUnits; - exif_append_data(&ImageInfo->d, - "FocalPlaneUnits", - 'f', - Format, - &rd); -} - -static void -ProcessVersion(ImageInfoType *ImageInfo, - void *ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p) -{ - exif_rec_data_t rd; - rd.s = exif_strndup(ValuePtr, ByteCount); - exif_append_data(&ImageInfo->d, - tag_p->Desc, - 's', - Format, - &rd); -} - -static void -ProcessUserComment(ImageInfoType *ImageInfo, - void *_ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p) -{ - char *ValuePtr = (char *)_ValuePtr; - exif_rec_data_t rd; - int a; - - /* Olympus has this padded with trailing spaces. Remove these first. */ - for (a=ByteCount;;) { - a--; - if ((ValuePtr)[a] == ' ') { - (ValuePtr)[a] = '\0'; - } else { - break; - } - if (a == 0) break; - } - - /* Copy the comment */ - if (memcmp(ValuePtr, "ASCII",5) == 0) { - for (a=5;a<10;a++) { - int c; - c = (ValuePtr)[a]; - if (c != '\0' && c != ' ') { - rd.s = exif_strndup(a+ValuePtr, ByteCount - a); - exif_append_data(&ImageInfo->d, - "UserComment", - 's', - Format, - &rd); - break; - } - } - - } else { - rd.s = exif_strndup(ValuePtr, ByteCount); - exif_append_data(&ImageInfo->d, - "UserComment", - 's', - Format, - &rd); - } -} - -static void -ProcessShutterSpeed(ImageInfoType *ImageInfo, - void *ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p) -{ - exif_rec_data_t rd; - char rec_type; - - rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format, - ImageInfo->MotorolaOrder, - &rd); - exif_append_data(&ImageInfo->d, - tag_p->Desc, - rec_type, - Format, - &rd); - - /* Convert shutter speed value to shutter speed; - * shutter speed is 1/(2**ShutterSpeedValue) - */ - rd.r.denom = (int)pow(2.0, ((double)rd.r.num)/((double)rd.r.denom)); - rd.r.num = 1; - exif_append_data(&ImageInfo->d, - "ShutterSpeed", - 'r', - EXIF_FMT_COMPUTED, - &rd); - -} - -static void -ProcessAperture(ImageInfoType *ImageInfo, - void *ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p) -{ - exif_rec_data_t rd; - char rec_type; - double fstop; - char label[32]; - - rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format, - ImageInfo->MotorolaOrder, - &rd); - exif_append_data(&ImageInfo->d, - tag_p->Desc, - rec_type, - Format, - &rd); - - if (exif_find_record(ImageInfo->d, "FNumber") == NULL) { - /* Convert aperture to F-stop. */ - fstop = pow(sqrt(2), ((double)rd.r.num)/((double)rd.r.denom)); - sprintf(label, "f%.1g", fstop); - rd.s = (char*)strdup(label); - exif_append_data(&ImageInfo->d, - "FNumber", - 's', - EXIF_FMT_COMPUTED, - &rd); - } -} - -static void -ProcessCanonMakerNote(ImageInfoType *ImageInfo, - void *ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p, - char *OffsetBase) -{ - - /* This is for the Canon MakerNote. */ - /* XXX - go by value of Maker tag. */ - exif_rec_data_t rd; - char rec_type; - unsigned long n_dir, tag, format, components, offset; - char label[32]; - void *OffsetPtr; - - n_dir = Get16u(ValuePtr, ImageInfo->MotorolaOrder); - ValuePtr += 2; - while (n_dir--) { - tag = Get16u(ValuePtr, ImageInfo->MotorolaOrder); - ValuePtr += 2; - format = Get16u(ValuePtr, ImageInfo->MotorolaOrder); - ValuePtr += 2; - components = Get32u(ValuePtr, ImageInfo->MotorolaOrder); - ValuePtr += 4; - offset = Get32u(ValuePtr, ImageInfo->MotorolaOrder); - ByteCount = components * ExifBytesPerFormat[format]; - if (ByteCount > 4) { - OffsetPtr = OffsetBase + offset; - } else { - OffsetPtr = ValuePtr; - } - ValuePtr += 4; - rec_type = ConvertAnyFormat2(OffsetPtr, ByteCount, format, - ImageInfo->MotorolaOrder, - &rd); - sprintf(label, "MakerNote%04x", tag); - exif_append_data(&ImageInfo->d, - label, - rec_type, - format, - &rd); - - } -} - - -struct MakerNote { - char *Make; - void (*Func)(); -}; - -static struct MakerNote -MakerProcessors[] = { - {"Canon", ProcessCanonMakerNote}, - {NULL, NULL} -}; - -static void -ProcessMakerNote(ImageInfoType *ImageInfo, - void *ValuePtr, - int ByteCount, - int Format, - struct ExifTag *tag_p, - char *OffsetBase) -{ - struct MakerNote *mn_p; - exif_record_t *rec_p; - - rec_p = exif_find_record(ImageInfo->d, "Make"); - if (rec_p == NULL) { - return; - } - - for(mn_p = &MakerProcessors[0]; mn_p->Make != NULL; mn_p++) { - if (strcmp(mn_p->Make, rec_p->rec_data.s) == 0) { - (*mn_p->Func)(ImageInfo, ValuePtr, ByteCount, Format, tag_p, OffsetBase); - break; - } - } -} - -static struct ExifTag -TagTable[] = { - { 0x0001, "InteroperabilityIndex"}, - { 0x0002, "InteroperabilityVersion", ProcessVersion}, - { 0x0100, "ImageWidth"}, - { 0x0101, "ImageLength"}, - { 0x0102, "BitsPerSample"}, - { 0x0103, "Compression"}, - { 0x0106, "PhotometricInterpretation"}, - { 0x010A, "FillOrder"}, - { 0x010D, "DocumentName"}, - { 0x010E, "ImageDescription"}, - { 0x010F, "Make"}, - { 0x0110, "Model"}, - { 0x0111, "StripOffsets"}, - { 0x0112, "Orientation"}, - { 0x0115, "SamplesPerPixel"}, - { 0x0116, "RowsPerStrip"}, - { 0x0117, "StripByteCounts"}, - { 0x011A, "XResolution"}, - { 0x011B, "YResolution"}, - { 0x011C, "PlanarConfiguration"}, - { 0x0128, "ResolutionUnit"}, - { 0x012D, "TransferFunction"}, - { 0x0131, "Software"}, - { 0x0132, "DateTime"}, - { 0x013B, "Artist"}, - { 0x013E, "WhitePoint"}, - { 0x013F, "PrimaryChromaticities"}, - { 0x0156, "TransferRange"}, - { 0x0200, "JPEGProc"}, - { 0x0201, "JPEGInterchangeFormat"}, - { 0x0202, "JPEGInterchangeFormatLength"}, - { 0x0211, "YCbCrCoefficients"}, - { 0x0212, "YCbCrSubSampling"}, - { 0x0213, "YCbCrPositioning"}, - { 0x0214, "ReferenceBlackWhite"}, - { 0x1000, "RelatedImageFileFormat"}, - { 0x1001, "RelatedImageWidth"}, - { 0x1002, "RelatedImageLength"}, - { 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", ProcessVersion}, - { 0x9003, "DateTimeOriginal"}, - { 0x9004, "DateTimeDigitized"}, - { 0x9101, "ComponentsConfiguration"}, - { 0x9102, "CompressedBitsPerPixel"}, - { 0x9201, "ShutterSpeedValue", ProcessShutterSpeed}, - { 0x9202, "ApertureValue", ProcessAperture}, - { 0x9203, "BrightnessValue"}, - { 0x9204, "ExposureBiasValue"}, - { 0x9205, "MaxApertureValue", ProcessAperture}, - { 0x9206, "SubjectDistance"}, - { 0x9207, "MeteringMode"}, - { 0x9208, "LightSource"}, - { 0x9209, "Flash"}, - { 0x920A, "FocalLength"}, - { 0x927C, "MakerNote", ProcessMakerNote}, - { 0x9286, "UserComment", ProcessUserComment}, - { 0x9290, "SubSecTime"}, - { 0x9291, "SubSecTimeOriginal"}, - { 0x9292, "SubSecTimeDigitized"}, - { 0xA000, "FlashPixVersion", ProcessVersion}, - { 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", ProcessFocalPlaneUnits}, - /* 0x9210 - - */ - { 0xA214, "SubjectLocation"}, /* 0x9214 - - */ - { 0xA215, "ExposureIndex"}, /* 0x9215 - - */ - { 0xA217, "SensingMethod"}, /* 0x9217 - - */ - { 0xA300, "FileSource"}, - { 0xA301, "SceneType"}, - { 0, NULL} -} ; - - - -/* Process one of the nested EXIF directories. */ -static int -ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *OffsetBase, unsigned ExifLength, char *LastExifRefd) -{ - int de; - int a; - int NumDirEntries; - exif_rec_data_t rd; - char rec_type; - char label[32]; - - NumDirEntries = Get16u(DirStart, ImageInfo->MotorolaOrder); - - if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) { - exif_error("Illegally sized directory"); - return FALSE; - } - - - for (de=0;de<NumDirEntries;de++) { - int Tag, Format, Components; - char *ValuePtr; - int ByteCount; - char *DirEntry; - struct ExifTag *tag_p; - - 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. */ - exif_error("Illegal format code in EXIF dir"); - return FALSE; - } - - 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); */ - - exif_error("Illegal pointer offset value in EXIF"); - return FALSE; - } - ValuePtr = OffsetBase+OffsetVal; - } else { - /* 4 bytes or less and value is in the dir entry itself */ - ValuePtr = DirEntry+8; - } - - if (LastExifRefd < ValuePtr+ByteCount) { - /* - Keep track of last byte in the exif header that was actually referenced. - That way, we know where the discardable thumbnail data begins. - */ - LastExifRefd = ValuePtr+ByteCount; - } - - if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) { - char *SubdirStart; - SubdirStart = OffsetBase + Get32u(ValuePtr, ImageInfo->MotorolaOrder); - if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) { - exif_error("Illegal subdirectory link"); - return FALSE; - } - ProcessExifDir(ImageInfo, SubdirStart, OffsetBase, ExifLength, LastExifRefd); - continue; - } - - /* Search through tag table */ - for (tag_p = &TagTable[0]; tag_p->Desc != NULL; tag_p++) { - if (tag_p->Tag == Tag) { - if (tag_p->Func != NULL) { - (*tag_p->Func)(ImageInfo, ValuePtr, ByteCount, Format, tag_p, OffsetBase); - } else { - rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format, - ImageInfo->MotorolaOrder, - &rd); - exif_append_data(&ImageInfo->d, - tag_p->Desc, - rec_type, - Format, - &rd); - } - break; - } - } - if (tag_p->Desc == NULL) { - rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format, - ImageInfo->MotorolaOrder, - &rd); - sprintf(label, "0x%04x", Tag); - exif_append_data(&ImageInfo->d, - label, - rec_type, - Format, - &rd); - } - } - return TRUE; -} - -/* - Process an EXIF marker - Describes all the drivel that most digital cameras include... -*/ -static int -process_EXIF (ImageInfoType *ImageInfo, char *CharBuf, unsigned int length, char *LastExifRefd) -{ - int cc; - exif_rec_data_t rd; - LastExifRefd = CharBuf; - - { /* Check the EXIF header component */ - static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; - if (memcmp(CharBuf+2, ExifHeader,6)) { - exif_error("Incorrect Exif header"); - return FALSE; - } - } - - if (memcmp(CharBuf+8,"II",2) == 0) { - ImageInfo->MotorolaOrder = 0; - } else { - if (memcmp(CharBuf+8,"MM",2) == 0) { - ImageInfo->MotorolaOrder = 1; - } else { - exif_error("Invalid Exif alignment marker."); - return FALSE; - } - } - - /* Check the next two values for correctness. */ - if (Get16u(CharBuf+10,ImageInfo->MotorolaOrder) != 0x2a - || Get32u(CharBuf+12,ImageInfo->MotorolaOrder) != 0x08) { - exif_error("Invalid Exif start (1, NULL)"); - return FALSE; - } - - /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */ - cc = ProcessExifDir(ImageInfo, CharBuf+16, CharBuf+8, length-6, LastExifRefd); - if (cc != TRUE) { - return cc; - } - return TRUE; -} - -/* 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. */ - exif_error("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) { - exif_error("invalid marker"); - return FALSE; - } - - Sections[*SectionsRead].Size = itemlen; - - Data = (uchar *)(*exif_malloc_fn)(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) { - exif_error("reading from file"); - return FALSE; - } - *SectionsRead += 1; - - switch(marker) { - case M_SOS: /* stop before hitting compressed data */ - /* If reading entire image is requested, read the rest of the data. */ - if (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 *)(*exif_malloc_fn)(size); - if (Data == NULL) { - exif_error("could not allocate data for entire image"); - return FALSE; - } - - got = fread(Data, 1, size, infile); - if (got != size) { - exif_error("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; - */ - } - return TRUE; - - case M_EOI: /* in case it's a tables-only JPEG stream */ - exif_error("No image in jpeg!"); - return FALSE; - - case M_COM: /* Comment section */ - if (HaveCom) { - (*SectionsRead) -= 1; - (*exif_free_fn)(Sections[*SectionsRead].Data); - } else { - process_COM(ImageInfo, Data, itemlen); - HaveCom = TRUE; - } - 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; -} - -/* - Discard read data. -*/ -static void -DiscardData(Section_t *Sections, int *SectionsRead) -{ - int a; - for (a=0;a<*SectionsRead-1;a++) { - (*exif_free_fn)(Sections[a].Data); - } - *SectionsRead = 0; -} - -/* - Read image data. -*/ -static int -ReadJpegFile(ImageInfoType *ImageInfo, Section_t *Sections, - int *SectionsRead, int fd, - int ReadAll, char *LastExifRefd) -{ - FILE *infile; - int ret; - char *tmp; - char **p_argv; - int p_argc; - - infile = fdopen(fd, "rb"); /* Unix ignores 'b', windows needs it. */ - - if (infile == NULL) { - exif_error("Unable to open '%s'", ImageInfo->filename); - return FALSE; - } - - /* Start with an empty image information structure. */ - memset(ImageInfo, 0, sizeof(*ImageInfo)); - memset(Sections, 0, sizeof(*Sections)); - - ImageInfo->d = exif_alloc(); - - /* Scan the JPEG headers. */ - ret = scan_JPEG_header(ImageInfo, infile, Sections, SectionsRead, ReadAll, LastExifRefd); - if (!ret) { - exif_error("Invalid Jpeg file: '%s'",ImageInfo->filename); - return FALSE; - } - - fclose(infile); - - return ret; -} - -static int -read_jpeg_exif(ImageInfoType *ImageInfo, int fd, int ReadAll) -{ - Section_t Sections[20]; - int SectionsRead; - char *LastExifRefd=NULL; - int ret; - int thumbsize=0; - - ret = ReadJpegFile(ImageInfo, Sections, &SectionsRead, fd, ReadAll, LastExifRefd); -#if 0 - /* - * 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 = (*exif_malloc_fn)(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); - } - } - } -#endif - if (ret != FALSE) { - DiscardData(Sections, &SectionsRead); - } - return(ret); -} - -exif_data_t * -exif_parse_fd(int fd) -{ - ImageInfoType ImageInfo; - - ImageInfo.filename = "<file stream>"; - if (read_jpeg_exif(&ImageInfo, fd, 1) != TRUE) { - return NULL; - } - return ImageInfo.d; -} - -exif_data_t * -exif_parse_file(const char *filename) -{ - ImageInfoType ImageInfo; - int fd; - - ImageInfo.filename = filename; - fd = open(filename, O_RDONLY); - if (fd < 0) { - return NULL; - } - - if (read_jpeg_exif(&ImageInfo, fd, 1) != TRUE) { - return NULL; - } - return ImageInfo.d; -} - -void -exif_free_data(struct exif_data *d) -{ - int i; - for (i=0; i<d->n_recs; i++) { - (*exif_free_fn)(d->recs[i].rec_name); - if (d->recs[i].rec_type == 's') { - (*exif_free_fn)(d->recs[i].rec_data.s); - } - } - (*exif_free_fn)(d); -} - -void -exif_init(void *(*malloc_fn)(int), - void (*free_fn)(void *), - void *(*realloc_fn)(void *, int)) -{ - if (malloc_fn == NULL) { - malloc_fn = (void *(*)(int))malloc; - } - exif_malloc_fn = malloc_fn; - if (free_fn == NULL) { - free_fn = (void (*)(void *))free; - } - exif_free_fn = free_fn; - if (realloc_fn == NULL) { - realloc_fn = (void *(*)(void *, int))realloc; - } - exif_realloc_fn = realloc_fn; -} - -extern exif_record_t * -exif_find_record(exif_data_t *d, const char *rec_name) -{ - int i; - for (i=0; i<d->n_recs; i++) { - if (strcmp(d->recs[i].rec_name, rec_name) == 0) { - return &d->recs[i]; - } - } - return NULL; -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - */ - - |