1//--------------------------------------------------------------------------
2// Program to pull the information out of various types of EXIF digital
3// camera files and show it in a reasonably consistent way
4//
5// This module parses the very complicated exif structures.
6//
7// Matthias Wandel
8//--------------------------------------------------------------------------
9#include "jhead.h"
10
11#include <math.h>
12#include <ctype.h>
13#include <utils/Log.h>
14
15static unsigned char * DirWithThumbnailPtrs;
16static double FocalplaneXRes;
17static double FocalplaneUnits;
18static int ExifImageWidth;
19static int MotorolaOrder = 0;
20
21// for fixing the rotation.
22static void * OrientationPtr[2];
23static int    OrientationNumFormat[2];
24int NumOrientations = 0;
25
26
27// Define the line below to turn on poor man's debugging output
28#undef SUPERDEBUG
29
30#ifdef SUPERDEBUG
31#define printf ALOGE
32#endif
33
34//--------------------------------------------------------------------------
35// Table of Jpeg encoding process names
36static const TagTable_t ProcessTable[] = {
37    { M_SOF0,   "Baseline", 0, 0},
38    { M_SOF1,   "Extended sequential", 0, 0},
39    { M_SOF2,   "Progressive", 0, 0},
40    { M_SOF3,   "Lossless", 0, 0},
41    { M_SOF5,   "Differential sequential", 0, 0},
42    { M_SOF6,   "Differential progressive", 0, 0},
43    { M_SOF7,   "Differential lossless", 0, 0},
44    { M_SOF9,   "Extended sequential, arithmetic coding", 0, 0},
45    { M_SOF10,  "Progressive, arithmetic coding", 0, 0},
46    { M_SOF11,  "Lossless, arithmetic coding", 0, 0},
47    { M_SOF13,  "Differential sequential, arithmetic coding", 0, 0},
48    { M_SOF14,  "Differential progressive, arithmetic coding", 0, 0},
49    { M_SOF15,  "Differential lossless, arithmetic coding", 0, 0},
50};
51
52#define PROCESS_TABLE_SIZE  (sizeof(ProcessTable) / sizeof(TagTable_t))
53
54// 1 - "The 0th row is at the visual top of the image,    and the 0th column is the visual left-hand side."
55// 2 - "The 0th row is at the visual top of the image,    and the 0th column is the visual right-hand side."
56// 3 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side."
57// 4 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side."
58
59// 5 - "The 0th row is the visual left-hand side of of the image,  and the 0th column is the visual top."
60// 6 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual top."
61// 7 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual bottom."
62// 8 - "The 0th row is the visual left-hand side of of the image,  and the 0th column is the visual bottom."
63
64// Note: The descriptions here are the same as the name of the command line
65// option to pass to jpegtran to right the image
66
67static const char * OrientTab[9] = {
68    "Undefined",
69    "Normal",           // 1
70    "flip horizontal",  // left right reversed mirror
71    "rotate 180",       // 3
72    "flip vertical",    // upside down mirror
73    "transpose",        // Flipped about top-left <--> bottom-right axis.
74    "rotate 90",        // rotate 90 cw to right it.
75    "transverse",       // flipped about top-right <--> bottom-left axis
76    "rotate 270",       // rotate 270 to right it.
77};
78
79const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
80
81//--------------------------------------------------------------------------
82// Describes tag values
83
84#define TAG_INTEROP_INDEX          0x0001
85#define TAG_INTEROP_VERSION        0x0002
86#define TAG_IMAGE_WIDTH            0x0100
87#define TAG_IMAGE_LENGTH           0x0101
88#define TAG_BITS_PER_SAMPLE        0x0102
89#define TAG_COMPRESSION            0x0103
90#define TAG_PHOTOMETRIC_INTERP     0x0106
91#define TAG_FILL_ORDER             0x010A
92#define TAG_DOCUMENT_NAME          0x010D
93#define TAG_IMAGE_DESCRIPTION      0x010E
94#define TAG_MAKE                   0x010F
95#define TAG_MODEL                  0x0110
96#define TAG_SRIP_OFFSET            0x0111
97#define TAG_ORIENTATION            0x0112
98#define TAG_SAMPLES_PER_PIXEL      0x0115
99#define TAG_ROWS_PER_STRIP         0x0116
100#define TAG_STRIP_BYTE_COUNTS      0x0117
101#define TAG_X_RESOLUTION           0x011A
102#define TAG_Y_RESOLUTION           0x011B
103#define TAG_PLANAR_CONFIGURATION   0x011C
104#define TAG_RESOLUTION_UNIT        0x0128
105#define TAG_TRANSFER_FUNCTION      0x012D
106#define TAG_SOFTWARE               0x0131
107#define TAG_DATETIME               0x0132
108#define TAG_ARTIST                 0x013B
109#define TAG_WHITE_POINT            0x013E
110#define TAG_PRIMARY_CHROMATICITIES 0x013F
111#define TAG_TRANSFER_RANGE         0x0156
112#define TAG_JPEG_PROC              0x0200
113#define TAG_THUMBNAIL_OFFSET       0x0201
114#define TAG_THUMBNAIL_LENGTH       0x0202
115#define TAG_Y_CB_CR_COEFFICIENTS   0x0211
116#define TAG_Y_CB_CR_SUB_SAMPLING   0x0212
117#define TAG_Y_CB_CR_POSITIONING    0x0213
118#define TAG_REFERENCE_BLACK_WHITE  0x0214
119#define TAG_RELATED_IMAGE_WIDTH    0x1001
120#define TAG_RELATED_IMAGE_LENGTH   0x1002
121#define TAG_CFA_REPEAT_PATTERN_DIM 0x828D
122#define TAG_CFA_PATTERN1           0x828E
123#define TAG_BATTERY_LEVEL          0x828F
124#define TAG_COPYRIGHT              0x8298
125#define TAG_EXPOSURETIME           0x829A
126#define TAG_FNUMBER                0x829D
127#define TAG_IPTC_NAA               0x83BB
128#define TAG_EXIF_OFFSET            0x8769
129#define TAG_INTER_COLOR_PROFILE    0x8773
130#define TAG_EXPOSURE_PROGRAM       0x8822
131#define TAG_SPECTRAL_SENSITIVITY   0x8824
132#define TAG_GPSINFO                0x8825
133#define TAG_ISO_EQUIVALENT         0x8827
134#define TAG_OECF                   0x8828
135#define TAG_EXIF_VERSION           0x9000
136#define TAG_DATETIME_ORIGINAL      0x9003
137#define TAG_DATETIME_DIGITIZED     0x9004
138#define TAG_COMPONENTS_CONFIG      0x9101
139#define TAG_CPRS_BITS_PER_PIXEL    0x9102
140#define TAG_SHUTTERSPEED           0x9201
141#define TAG_APERTURE               0x9202
142#define TAG_BRIGHTNESS_VALUE       0x9203
143#define TAG_EXPOSURE_BIAS          0x9204
144#define TAG_MAXAPERTURE            0x9205
145#define TAG_SUBJECT_DISTANCE       0x9206
146#define TAG_METERING_MODE          0x9207
147#define TAG_LIGHT_SOURCE           0x9208
148#define TAG_FLASH                  0x9209
149#define TAG_FOCALLENGTH            0x920A
150#define TAG_MAKER_NOTE             0x927C
151#define TAG_USERCOMMENT            0x9286
152#define TAG_SUBSEC_TIME            0x9290
153#define TAG_SUBSEC_TIME_ORIG       0x9291
154#define TAG_SUBSEC_TIME_DIG        0x9292
155
156#define TAG_WINXP_TITLE            0x9c9b // Windows XP - not part of exif standard.
157#define TAG_WINXP_COMMENT          0x9c9c // Windows XP - not part of exif standard.
158#define TAG_WINXP_AUTHOR           0x9c9d // Windows XP - not part of exif standard.
159#define TAG_WINXP_KEYWORDS         0x9c9e // Windows XP - not part of exif standard.
160#define TAG_WINXP_SUBJECT          0x9c9f // Windows XP - not part of exif standard.
161
162#define TAG_FLASH_PIX_VERSION      0xA000
163#define TAG_COLOR_SPACE            0xA001
164#define TAG_EXIF_IMAGEWIDTH        0xA002
165#define TAG_EXIF_IMAGELENGTH       0xA003
166#define TAG_RELATED_AUDIO_FILE     0xA004
167#define TAG_INTEROP_OFFSET         0xA005
168#define TAG_FLASH_ENERGY           0xA20B
169#define TAG_SPATIAL_FREQ_RESP      0xA20C
170#define TAG_FOCAL_PLANE_XRES       0xA20E
171#define TAG_FOCAL_PLANE_YRES       0xA20F
172#define TAG_FOCAL_PLANE_UNITS      0xA210
173#define TAG_SUBJECT_LOCATION       0xA214
174#define TAG_EXPOSURE_INDEX         0xA215
175#define TAG_SENSING_METHOD         0xA217
176#define TAG_FILE_SOURCE            0xA300
177#define TAG_SCENE_TYPE             0xA301
178#define TAG_CFA_PATTERN            0xA302
179#define TAG_CUSTOM_RENDERED        0xA401
180#define TAG_EXPOSURE_MODE          0xA402
181#define TAG_WHITEBALANCE           0xA403
182#define TAG_DIGITALZOOMRATIO       0xA404
183#define TAG_FOCALLENGTH_35MM       0xA405
184#define TAG_SCENE_CAPTURE_TYPE     0xA406
185#define TAG_GAIN_CONTROL           0xA407
186#define TAG_CONTRAST               0xA408
187#define TAG_SATURATION             0xA409
188#define TAG_SHARPNESS              0xA40A
189#define TAG_DISTANCE_RANGE         0xA40C
190
191// TODO: replace the ", 0" values in this table with the correct format, e.g. ", FMT_USHORT"
192static const TagTable_t TagTable[] = {
193  { TAG_INTEROP_INDEX,          "InteropIndex", 0, 0},
194  { TAG_INTEROP_VERSION,        "InteropVersion", 0, 0},
195  { TAG_IMAGE_WIDTH,            "ImageWidth", FMT_USHORT, 1},
196  { TAG_IMAGE_LENGTH,           "ImageLength", FMT_USHORT, 1},
197  { TAG_BITS_PER_SAMPLE,        "BitsPerSample", FMT_USHORT, 3},
198  { TAG_COMPRESSION,            "Compression", FMT_USHORT, 1},
199  { TAG_PHOTOMETRIC_INTERP,     "PhotometricInterpretation", FMT_USHORT, 1},
200  { TAG_FILL_ORDER,             "FillOrder", 0, 0},
201  { TAG_DOCUMENT_NAME,          "DocumentName", 0, 0},
202  { TAG_IMAGE_DESCRIPTION,      "ImageDescription", 0, 0 },
203  { TAG_MAKE,                   "Make", FMT_STRING, -1},
204  { TAG_MODEL,                  "Model", FMT_STRING, -1},
205  { TAG_SRIP_OFFSET,            "StripOffsets", FMT_USHORT, 1},
206  { TAG_ORIENTATION,            "Orientation", FMT_USHORT, 1},
207  { TAG_SAMPLES_PER_PIXEL,      "SamplesPerPixel", FMT_USHORT, 3},
208  { TAG_ROWS_PER_STRIP,         "RowsPerStrip", FMT_USHORT, 1},
209  { TAG_STRIP_BYTE_COUNTS,      "StripByteCounts", FMT_USHORT, 1},
210  { TAG_X_RESOLUTION,           "XResolution", FMT_URATIONAL, 1},
211  { TAG_Y_RESOLUTION,           "YResolution", FMT_URATIONAL, 1},
212  { TAG_PLANAR_CONFIGURATION,   "PlanarConfiguration", FMT_USHORT, 1},
213  { TAG_RESOLUTION_UNIT,        "ResolutionUnit", FMT_USHORT, 1},
214  { TAG_TRANSFER_FUNCTION,      "TransferFunction", FMT_USHORT, 768},
215  { TAG_SOFTWARE,               "Software", FMT_STRING, -1},
216  { TAG_DATETIME,               "DateTime", FMT_STRING, 20},
217  { TAG_ARTIST,                 "Artist", FMT_STRING, -1},
218  { TAG_WHITE_POINT,            "WhitePoint", FMT_SRATIONAL, 2},
219  { TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities", FMT_SRATIONAL, 6},
220  { TAG_TRANSFER_RANGE,         "TransferRange", 0, 0},
221  { TAG_JPEG_PROC,              "JPEGProc", 0, 0},
222  { TAG_THUMBNAIL_OFFSET,       "ThumbnailOffset", 0, 0},
223  { TAG_THUMBNAIL_LENGTH,       "ThumbnailLength", 0, 0},
224  { TAG_Y_CB_CR_COEFFICIENTS,   "YCbCrCoefficients", FMT_SRATIONAL, 3},
225  { TAG_Y_CB_CR_SUB_SAMPLING,   "YCbCrSubSampling", FMT_USHORT, 2},
226  { TAG_Y_CB_CR_POSITIONING,    "YCbCrPositioning", FMT_USHORT, 1},
227  { TAG_REFERENCE_BLACK_WHITE,  "ReferenceBlackWhite", FMT_SRATIONAL, 6},
228  { TAG_RELATED_IMAGE_WIDTH,    "RelatedImageWidth", 0, 0},
229  { TAG_RELATED_IMAGE_LENGTH,   "RelatedImageLength", 0, 0},
230  { TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim", 0, 0},
231  { TAG_CFA_PATTERN1,           "CFAPattern", 0, 0},
232  { TAG_BATTERY_LEVEL,          "BatteryLevel", 0, 0},
233  { TAG_COPYRIGHT,              "Copyright", FMT_STRING, -1},
234  { TAG_EXPOSURETIME,           "ExposureTime", FMT_SRATIONAL, 1},
235  { TAG_FNUMBER,                "FNumber", FMT_SRATIONAL, 1},
236  { TAG_IPTC_NAA,               "IPTC/NAA", 0, 0},
237  { TAG_EXIF_OFFSET,            "ExifOffset", 0, 0},
238  { TAG_INTER_COLOR_PROFILE,    "InterColorProfile", 0, 0},
239  { TAG_EXPOSURE_PROGRAM,       "ExposureProgram", FMT_SSHORT, 1},
240  { TAG_SPECTRAL_SENSITIVITY,   "SpectralSensitivity", FMT_STRING, -1},
241  { TAG_GPSINFO,                "GPS Dir offset", 0, 0},
242  { TAG_ISO_EQUIVALENT,         "ISOSpeedRatings", FMT_SSHORT, -1},
243  { TAG_OECF,                   "OECF", 0, 0},
244  { TAG_EXIF_VERSION,           "ExifVersion", FMT_BYTE, 4},
245  { TAG_DATETIME_ORIGINAL,      "DateTimeOriginal", FMT_STRING, 20},
246  { TAG_DATETIME_DIGITIZED,     "DateTimeDigitized", FMT_STRING, 20},
247  { TAG_COMPONENTS_CONFIG,      "ComponentsConfiguration", FMT_BYTE, 4},
248  { TAG_CPRS_BITS_PER_PIXEL,    "CompressedBitsPerPixel", FMT_SRATIONAL, 1},
249  { TAG_SHUTTERSPEED,           "ShutterSpeedValue", FMT_SRATIONAL, 1},
250  { TAG_APERTURE,               "ApertureValue", FMT_URATIONAL, 1},
251  { TAG_BRIGHTNESS_VALUE,       "BrightnessValue", FMT_SRATIONAL, 1},
252  { TAG_EXPOSURE_BIAS,          "ExposureBiasValue", FMT_SRATIONAL, 1},
253  { TAG_MAXAPERTURE,            "MaxApertureValue", FMT_URATIONAL, 1},
254  { TAG_SUBJECT_DISTANCE,       "SubjectDistance", FMT_URATIONAL, 1},
255  { TAG_METERING_MODE,          "MeteringMode", FMT_USHORT, 1},
256  { TAG_LIGHT_SOURCE,           "LightSource", FMT_USHORT, 1},
257  { TAG_FLASH,                  "Flash", FMT_USHORT, 1},
258  { TAG_FOCALLENGTH,            "FocalLength", FMT_URATIONAL, 1},
259  { TAG_MAKER_NOTE,             "MakerNote", FMT_STRING, -1},
260  { TAG_USERCOMMENT,            "UserComment", FMT_STRING, -1},
261  { TAG_SUBSEC_TIME,            "SubSecTime", FMT_STRING, -1},
262  { TAG_SUBSEC_TIME_ORIG,       "SubSecTimeOriginal", FMT_STRING, -1},
263  { TAG_SUBSEC_TIME_DIG,        "SubSecTimeDigitized", FMT_STRING, -1},
264  { TAG_WINXP_TITLE,            "Windows-XP Title", 0, 0},
265  { TAG_WINXP_COMMENT,          "Windows-XP comment", 0, 0},
266  { TAG_WINXP_AUTHOR,           "Windows-XP author", 0, 0},
267  { TAG_WINXP_KEYWORDS,         "Windows-XP keywords", 0, 0},
268  { TAG_WINXP_SUBJECT,          "Windows-XP subject", 0, 0},
269  { TAG_FLASH_PIX_VERSION,      "FlashPixVersion", FMT_BYTE, 4},
270  { TAG_COLOR_SPACE,            "ColorSpace", FMT_USHORT, 1},
271  { TAG_EXIF_IMAGEWIDTH,        "ExifImageWidth", 0, 0},
272  { TAG_EXIF_IMAGELENGTH,       "ExifImageLength", 0, 0},
273  { TAG_RELATED_AUDIO_FILE,     "RelatedAudioFile", 0, 0},
274  { TAG_INTEROP_OFFSET,         "InteroperabilityOffset", 0, 0},
275  { TAG_FLASH_ENERGY,           "FlashEnergy", FMT_URATIONAL, 1},
276  { TAG_SPATIAL_FREQ_RESP,      "SpatialFrequencyResponse", FMT_STRING, -1},
277  { TAG_FOCAL_PLANE_XRES,       "FocalPlaneXResolution", FMT_URATIONAL, 1},
278  { TAG_FOCAL_PLANE_YRES,       "FocalPlaneYResolution", FMT_URATIONAL, 1},
279  { TAG_FOCAL_PLANE_UNITS,      "FocalPlaneResolutionUnit", FMT_USHORT, 1},
280  { TAG_SUBJECT_LOCATION,       "SubjectLocation", FMT_USHORT, 2},
281  { TAG_EXPOSURE_INDEX,         "ExposureIndex", FMT_URATIONAL, 1},
282  { TAG_SENSING_METHOD,         "SensingMethod", FMT_USHORT, 1},
283  { TAG_FILE_SOURCE,            "FileSource", 0, 1},
284  { TAG_SCENE_TYPE,             "SceneType", 0, 1},
285  { TAG_CFA_PATTERN,            "CFA Pattern", 0, -1},
286  { TAG_CUSTOM_RENDERED,        "CustomRendered", FMT_USHORT, 1},
287  { TAG_EXPOSURE_MODE,          "ExposureMode", FMT_USHORT, 1},
288  { TAG_WHITEBALANCE,           "WhiteBalance", FMT_USHORT, 1},
289  { TAG_DIGITALZOOMRATIO,       "DigitalZoomRatio", FMT_URATIONAL, 1},
290  { TAG_FOCALLENGTH_35MM,       "FocalLengthIn35mmFilm", FMT_USHORT, 1},
291  { TAG_SCENE_CAPTURE_TYPE,     "SceneCaptureType", FMT_USHORT, 1},
292  { TAG_GAIN_CONTROL,           "GainControl", FMT_URATIONAL, 1},
293  { TAG_CONTRAST,               "Contrast", FMT_USHORT, 1},
294  { TAG_SATURATION,             "Saturation", FMT_USHORT, 1},
295  { TAG_SHARPNESS,              "Sharpness", FMT_USHORT, 1},
296  { TAG_DISTANCE_RANGE,         "SubjectDistanceRange", FMT_USHORT, 1},
297} ;
298
299#define TAG_TABLE_SIZE  (sizeof(TagTable) / sizeof(TagTable_t))
300
301int TagNameToValue(const char* tagName)
302{
303    unsigned int i;
304    for (i = 0; i < TAG_TABLE_SIZE; i++) {
305        if (strcmp(TagTable[i].Desc, tagName) == 0) {
306            printf("found tag %s val %d", TagTable[i].Desc, TagTable[i].Tag);
307            return TagTable[i].Tag;
308        }
309    }
310    printf("tag %s NOT FOUND", tagName);
311    return -1;
312}
313
314int IsDateTimeTag(unsigned short tag)
315{
316    return ((tag == TAG_DATETIME)? TRUE: FALSE);
317}
318
319//--------------------------------------------------------------------------
320// Convert a 16 bit unsigned value to file's native byte order
321//--------------------------------------------------------------------------
322static void Put16u(void * Short, unsigned short PutValue)
323{
324    if (MotorolaOrder){
325        ((uchar *)Short)[0] = (uchar)(PutValue>>8);
326        ((uchar *)Short)[1] = (uchar)PutValue;
327    }else{
328        ((uchar *)Short)[0] = (uchar)PutValue;
329        ((uchar *)Short)[1] = (uchar)(PutValue>>8);
330    }
331}
332
333//--------------------------------------------------------------------------
334// Convert a 16 bit unsigned value from file's native byte order
335//--------------------------------------------------------------------------
336int Get16u(void * Short)
337{
338    if (MotorolaOrder){
339        return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
340    }else{
341        return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
342    }
343}
344
345//--------------------------------------------------------------------------
346// Convert a 32 bit signed value from file's native byte order
347//--------------------------------------------------------------------------
348int Get32s(void * Long)
349{
350    if (MotorolaOrder){
351        return  ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
352              | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
353    }else{
354        return  ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
355              | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
356    }
357}
358
359//--------------------------------------------------------------------------
360// Convert a 32 bit unsigned value to file's native byte order
361//--------------------------------------------------------------------------
362void Put32u(void * Value, unsigned PutValue)
363{
364    if (MotorolaOrder){
365        ((uchar *)Value)[0] = (uchar)(PutValue>>24);
366        ((uchar *)Value)[1] = (uchar)(PutValue>>16);
367        ((uchar *)Value)[2] = (uchar)(PutValue>>8);
368        ((uchar *)Value)[3] = (uchar)PutValue;
369    }else{
370        ((uchar *)Value)[0] = (uchar)PutValue;
371        ((uchar *)Value)[1] = (uchar)(PutValue>>8);
372        ((uchar *)Value)[2] = (uchar)(PutValue>>16);
373        ((uchar *)Value)[3] = (uchar)(PutValue>>24);
374    }
375}
376
377//--------------------------------------------------------------------------
378// Convert a 32 bit unsigned value from file's native byte order
379//--------------------------------------------------------------------------
380unsigned Get32u(void * Long)
381{
382    return (unsigned)Get32s(Long) & 0xffffffff;
383}
384
385//--------------------------------------------------------------------------
386// Display a number as one of its many formats
387//--------------------------------------------------------------------------
388void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount)
389{
390    int s,n;
391
392    for(n=0;n<16;n++){
393        switch(Format){
394            case FMT_SBYTE:
395            case FMT_BYTE:      printf("%02x",*(uchar *)ValuePtr); s=1;  break;
396            case FMT_USHORT:    printf("%d",Get16u(ValuePtr)); s=2;      break;
397            case FMT_ULONG:
398            case FMT_SLONG:     printf("%d",Get32s(ValuePtr)); s=4;      break;
399            case FMT_SSHORT:    printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break;
400            case FMT_URATIONAL:
401            case FMT_SRATIONAL:
402               printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
403               s = 8;
404               break;
405
406            case FMT_SINGLE:    printf("%f",(double)*(float *)ValuePtr); s=8; break;
407            case FMT_DOUBLE:    printf("%f",*(double *)ValuePtr);        s=8; break;
408            default:
409                printf("Unknown format %d:", Format);
410                return;
411        }
412        ByteCount -= s;
413        if (ByteCount <= 0) break;
414        printf(", ");
415        ValuePtr = (void *)((char *)ValuePtr + s);
416
417    }
418    if (n >= 16) printf("...");
419}
420
421
422//--------------------------------------------------------------------------
423// Evaluate number, be it int, rational, or float from directory.
424//--------------------------------------------------------------------------
425double ConvertAnyFormat(void * ValuePtr, int Format)
426{
427    double Value;
428    Value = 0;
429
430    switch(Format){
431        case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
432        case FMT_BYTE:      Value = *(uchar *)ValuePtr;        break;
433
434        case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
435        case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
436
437        case FMT_URATIONAL:
438        case FMT_SRATIONAL:
439            {
440                int Num,Den;
441                Num = Get32s(ValuePtr);
442                Den = Get32s(4+(char *)ValuePtr);
443                if (Den == 0){
444                    Value = 0;
445                }else{
446                    Value = (double)Num/Den;
447                }
448                break;
449            }
450
451        case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
452        case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
453
454        // Not sure if this is correct (never seen float used in Exif format)
455        case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
456        case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
457
458        default:
459            ErrNonfatal("Illegal format code %d",Format,0);
460    }
461    return Value;
462}
463
464//--------------------------------------------------------------------------
465// Convert a double value into a signed or unsigned rational number.
466//--------------------------------------------------------------------------
467static void float2urat(double value, unsigned int max, unsigned int *numerator,
468                       unsigned int *denominator) {
469    if (value <= 0) {
470        *numerator = 0;
471        *denominator = 1;
472        return;
473    }
474
475    if (value > max) {
476        *numerator = max;
477        *denominator = 1;
478        return;
479    }
480
481    // For values less than 1e-9, scale as much as possible
482    if (value < 1e-9) {
483        unsigned int n = (unsigned int)(value * max);
484        if (n == 0) {
485            *numerator = 0;
486            *denominator = 1;
487        } else {
488            *numerator = n;
489            *denominator = max;
490        }
491        return;
492    }
493
494    // Try to use a denominator of 1e9, 1e8, ..., until the numerator fits
495    unsigned int d;
496    for (d = 1000000000; d >= 1; d /= 10) {
497        double s = value * d;
498        if (s <= max) {
499            // Remove the trailing zeros from both.
500            unsigned int n = (unsigned int)s;
501            while (n % 10 == 0 && d >= 10) {
502                n /= 10;
503                d /= 10;
504            }
505            *numerator = n;
506            *denominator = d;
507            return;
508        }
509    }
510
511    // Shouldn't reach here because the denominator 1 should work
512    // above. But just in case.
513    *numerator = 0;
514    *denominator = 1;
515}
516
517static void ConvertDoubleToURational(double value, unsigned int *numerator,
518                                     unsigned int *denominator) {
519    float2urat(value, 0xFFFFFFFFU, numerator, denominator);
520}
521
522static void ConvertDoubleToSRational(double value, int *numerator,
523                                     int *denominator) {
524    int negative = 0;
525
526    if (value < 0) {
527        value = -value;
528        negative = 1;
529    }
530
531    unsigned int n, d;
532    float2urat(value, 0x7FFFFFFFU, &n, &d);
533    *numerator = (int)n;
534    *denominator = (int)d;
535    if (negative) {
536        *numerator = -*numerator;
537    }
538}
539
540//--------------------------------------------------------------------------
541// Process one of the nested EXIF directories.
542//--------------------------------------------------------------------------
543static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,
544        unsigned ExifLength, int NestingLevel)
545{
546    int de;
547    int a;
548    int NumDirEntries;
549    unsigned ThumbnailOffset = 0;
550    unsigned ThumbnailSize = 0;
551    char IndentString[25];
552
553    printf("ProcessExifDir");
554    if (NestingLevel > 4){
555        ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
556        return;
557    }
558
559    memset(IndentString, ' ', 25);
560    IndentString[NestingLevel * 4] = '\0';
561
562
563    NumDirEntries = Get16u(DirStart);
564    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
565
566    {
567        unsigned char * DirEnd;
568        DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
569        if (DirEnd+4 > (OffsetBase+ExifLength)){
570            if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
571                // Version 1.3 of jhead would truncate a bit too much.
572                // This also caught later on as well.
573            }else{
574                ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0);
575                return;
576            }
577        }
578        if (DumpExifMap){
579            printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
580        }
581
582
583    }
584
585    if (ShowTags){
586        printf("(dir has %d entries)\n",NumDirEntries);
587    }
588
589    for (de=0;de<NumDirEntries;de++){
590        int Tag, Format, Components;
591        unsigned char * ValuePtr;
592        int ByteCount;
593        unsigned char * DirEntry;
594        DirEntry = DIR_ENTRY_ADDR(DirStart, de);
595
596        Tag = Get16u(DirEntry);
597        Format = Get16u(DirEntry+2);
598        Components = Get32u(DirEntry+4);
599
600        if ((Format-1) >= NUM_FORMATS) {
601            // (-1) catches illegal zero case as unsigned underflows to positive large.
602            ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
603            continue;
604        }
605
606        if ((unsigned)Components > 0x10000){
607            ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
608            continue;
609        }
610
611        ByteCount = Components * BytesPerFormat[Format];
612
613        if (ByteCount > 4){
614            unsigned OffsetVal;
615            OffsetVal = Get32u(DirEntry+8);
616            // If its bigger than 4 bytes, the dir entry contains an offset.
617            if (OffsetVal > UINT32_MAX - ByteCount || OffsetVal+ByteCount > ExifLength){
618                // Bogus pointer offset and / or bytecount value
619                ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
620                continue;
621            }
622            ValuePtr = OffsetBase+OffsetVal;
623
624            if (OffsetVal > ImageInfo.LargestExifOffset){
625                ImageInfo.LargestExifOffset = OffsetVal;
626            }
627
628            if (DumpExifMap){
629                printf("Map: %05d-%05d:   Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag);
630            }
631        }else{
632            // 4 bytes or less and value is in the dir entry itself
633            ValuePtr = DirEntry+8;
634        }
635
636        if (Tag == TAG_MAKER_NOTE){
637            if (ShowTags){
638                printf("%s    Maker note: ",IndentString);
639            }
640            ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
641            continue;
642        }
643
644        if (ShowTags){
645            // Show tag name
646            for (a=0;;a++){
647                if (a >= (int)TAG_TABLE_SIZE){
648                    printf("%s", IndentString);
649                    printf("    Unknown Tag %04x Value = ", Tag);
650                    break;
651                }
652                if (TagTable[a].Tag == Tag){
653                    printf("%s", IndentString);
654                    printf("    %s = ",TagTable[a].Desc);
655                    break;
656                }
657            }
658
659            // Show tag value.
660            switch(Format){
661                case FMT_BYTE:
662                    if(ByteCount>1){
663                        printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr);
664                    }else{
665                        PrintFormatNumber(ValuePtr, Format, ByteCount);
666                        printf("\n");
667                    }
668                    break;
669
670                case FMT_UNDEFINED:
671                    // Undefined is typically an ascii string.
672
673                case FMT_STRING:
674                    // String arrays printed without function call (different from int arrays)
675                    {
676                          printf("\"%s\"", ValuePtr);
677//                        int NoPrint = 0;
678//                        printf("\"");
679//                        for (a=0;a<ByteCount;a++){
680//                            if (ValuePtr[a] >= 32){
681//                                putchar(ValuePtr[a]);
682//                                NoPrint = 0;
683//                            }else{
684//                                // Avoiding indicating too many unprintable characters of proprietary
685//                                // bits of binary information this program may not know how to parse.
686//                                if (!NoPrint && a != ByteCount-1){
687//                                    putchar('?');
688//                                    NoPrint = 1;
689//                                }
690//                            }
691//                        }
692//                        printf("\"\n");
693                    }
694                    break;
695
696                default:
697                    // Handle arrays of numbers later (will there ever be?)
698                    PrintFormatNumber(ValuePtr, Format, ByteCount);
699                    printf("\n");
700            }
701        }
702
703        // Extract useful components of tag
704        switch(Tag){
705
706            case TAG_MAKE:
707                strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31);
708                break;
709
710            case TAG_MODEL:
711                strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39);
712                break;
713
714            case TAG_SUBSEC_TIME:
715                strlcpy(ImageInfo.SubSecTime, (char *)ValuePtr, sizeof(ImageInfo.SubSecTime));
716                break;
717
718            case TAG_SUBSEC_TIME_ORIG:
719                strlcpy(ImageInfo.SubSecTimeOrig, (char *)ValuePtr,
720                        sizeof(ImageInfo.SubSecTimeOrig));
721                break;
722
723            case TAG_SUBSEC_TIME_DIG:
724                strlcpy(ImageInfo.SubSecTimeDig, (char *)ValuePtr,
725                        sizeof(ImageInfo.SubSecTimeDig));
726                break;
727
728            case TAG_DATETIME_DIGITIZED:
729                strlcpy(ImageInfo.DigitizedTime, (char *)ValuePtr,
730                        sizeof(ImageInfo.DigitizedTime));
731
732                if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
733                    ErrNonfatal("More than %d date fields!  This is nuts", MAX_DATE_COPIES, 0);
734                    break;
735                }
736                ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
737                    (char *)ValuePtr - (char *)OffsetBase;
738                break;
739
740            case TAG_DATETIME_ORIGINAL:
741                // If we get a DATETIME_ORIGINAL, we use that one.
742                strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
743                // Fallthru...
744
745            case TAG_DATETIME:
746                if (!isdigit(ImageInfo.DateTime[0])){
747                    // If we don't already have a DATETIME_ORIGINAL, use whatever
748                    // time fields we may have.
749                    strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
750                }
751
752                if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
753                    ErrNonfatal("More than %d date fields!  This is nuts", MAX_DATE_COPIES, 0);
754                    break;
755                }
756                ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
757                    (char *)ValuePtr - (char *)OffsetBase;
758                break;
759
760            case TAG_WINXP_COMMENT:
761                if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
762                    // Already have a comment (probably windows comment), skip this one.
763                    if (ShowTags) printf("Windows XP commend and other comment in header\n");
764                    break; // Already have a windows comment, skip this one.
765                }
766
767                if (ByteCount > 1){
768                    if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
769                    memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
770                    ImageInfo.CommentWidchars = ByteCount/2;
771                }
772                break;
773
774            case TAG_USERCOMMENT:
775                if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
776                    // Already have a comment (probably windows comment), skip this one.
777                    if (ShowTags) printf("Multiple comments in exif header\n");
778                    break; // Already have a windows comment, skip this one.
779                }
780
781                // Comment is often padded with trailing spaces.  Remove these first.
782                for (a=ByteCount;;){
783                    a--;
784                    if ((ValuePtr)[a] == ' '){
785                        (ValuePtr)[a] = '\0';
786                    }else{
787                        break;
788                    }
789                    if (a == 0) break;
790                }
791
792                // Copy the comment
793                {
794                    // We want to set copied comment length (msize) to be the
795                    // minimum of:
796                    // (1) The space still available in Exif
797                    // (2) The given comment length (ByteCount)
798                    // (3) MAX_COMMENT_SIZE - 1
799                    int msiz = ExifLength - (ValuePtr-OffsetBase);
800                    if (msiz > ByteCount) msiz = ByteCount;
801                    if (msiz > MAX_COMMENT_SIZE - 1) msiz = MAX_COMMENT_SIZE - 1;
802                    if (msiz > 5 && memcmp(ValuePtr, "ASCII", 5) == 0) {
803                        for (a = 5; a < 10 && a < msiz; a++) {
804                            int c = (ValuePtr)[a];
805                            if (c != '\0' && c != ' ') {
806                                strncpy(ImageInfo.Comments,
807                                        (char *)ValuePtr + a, msiz - a);
808                                break;
809                            }
810                        }
811                    } else {
812                        strncpy(ImageInfo.Comments, (char *)ValuePtr, msiz);
813                    }
814                }
815                break;
816
817            case TAG_FNUMBER:
818                // Simplest way of expressing aperture, so I trust it the most.
819                // (overwrite previously computd value if there is one)
820                ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
821                break;
822
823            case TAG_APERTURE:
824            case TAG_MAXAPERTURE:
825                // More relevant info always comes earlier, so only use this field if we don't
826                // have appropriate aperture information yet.
827                if (ImageInfo.ApertureFNumber == 0){
828                    ImageInfo.ApertureFNumber
829                        = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
830                }
831                break;
832
833            case TAG_FOCALLENGTH:
834                // Nice digital cameras actually save the focal length as a function
835                // of how farthey are zoomed in.
836                ImageInfo.FocalLength.num = Get32u(ValuePtr);
837                ImageInfo.FocalLength.denom = Get32u(4+(char *)ValuePtr);
838                break;
839
840            case TAG_SUBJECT_DISTANCE:
841                // Inidcates the distacne the autofocus camera is focused to.
842                // Tends to be less accurate as distance increases.
843                ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
844                break;
845
846            case TAG_EXPOSURETIME:
847                // Simplest way of expressing exposure time, so I trust it most.
848                // (overwrite previously computd value if there is one)
849                ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
850                break;
851
852            case TAG_SHUTTERSPEED:
853                // More complicated way of expressing exposure time, so only use
854                // this value if we don't already have it from somewhere else.
855                if (ImageInfo.ExposureTime == 0){
856                    ImageInfo.ExposureTime
857                        = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
858                }
859                break;
860
861
862            case TAG_FLASH:
863                ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format);
864                break;
865
866            case TAG_ORIENTATION:
867                if (NumOrientations >= 2){
868                    // Can have another orientation tag for the thumbnail, but if there's
869                    // a third one, things are stringae.
870                    ErrNonfatal("More than two orientation tags!",0,0);
871                    break;
872                }
873                OrientationPtr[NumOrientations] = ValuePtr;
874                OrientationNumFormat[NumOrientations] = Format;
875                if (NumOrientations == 0){
876                    ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
877                }
878                if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){
879                    ErrNonfatal("Undefined rotation value %d", ImageInfo.Orientation, 0);
880                    ImageInfo.Orientation = 0;
881                }
882                NumOrientations += 1;
883                break;
884
885            case TAG_EXIF_IMAGELENGTH:
886            case TAG_EXIF_IMAGEWIDTH:
887                // Use largest of height and width to deal with images that have been
888                // rotated to portrait format.
889                a = (int)ConvertAnyFormat(ValuePtr, Format);
890                if (ExifImageWidth < a) ExifImageWidth = a;
891                break;
892
893            case TAG_FOCAL_PLANE_XRES:
894                FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
895                break;
896
897            case TAG_FOCAL_PLANE_UNITS:
898                switch((int)ConvertAnyFormat(ValuePtr, Format)){
899                    case 1: FocalplaneUnits = 25.4; break; // inch
900                    case 2:
901                        // According to the information I was using, 2 means meters.
902                        // But looking at the Cannon powershot's files, inches is the only
903                        // sensible value.
904                        FocalplaneUnits = 25.4;
905                        break;
906
907                    case 3: FocalplaneUnits = 10;   break;  // centimeter
908                    case 4: FocalplaneUnits = 1;    break;  // millimeter
909                    case 5: FocalplaneUnits = .001; break;  // micrometer
910                }
911                break;
912
913            case TAG_EXPOSURE_BIAS:
914                ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
915                break;
916
917            case TAG_WHITEBALANCE:
918                ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
919                break;
920
921            case TAG_LIGHT_SOURCE:
922                ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
923                break;
924
925            case TAG_METERING_MODE:
926                ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
927                break;
928
929            case TAG_EXPOSURE_PROGRAM:
930                ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
931                break;
932
933            case TAG_EXPOSURE_INDEX:
934                if (ImageInfo.ISOequivalent == 0){
935                    // Exposure index and ISO equivalent are often used interchangeably,
936                    // so we will do the same in jhead.
937                    // http://photography.about.com/library/glossary/bldef_ei.htm
938                    ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
939                }
940                break;
941
942            case TAG_EXPOSURE_MODE:
943                ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
944                break;
945
946            case TAG_ISO_EQUIVALENT:
947                ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
948                break;
949
950            case TAG_DIGITALZOOMRATIO:
951                ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
952                break;
953
954            case TAG_THUMBNAIL_OFFSET:
955                ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
956                DirWithThumbnailPtrs = DirStart;
957                break;
958
959            case TAG_THUMBNAIL_LENGTH:
960                ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
961                ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase;
962                break;
963
964            case TAG_EXIF_OFFSET:
965                if (ShowTags) printf("%s    Exif Dir:",IndentString);
966
967            case TAG_INTEROP_OFFSET:
968                if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s    Interop Dir:",IndentString);
969                {
970                    unsigned char * SubdirStart;
971                    SubdirStart = OffsetBase + Get32u(ValuePtr);
972                    if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
973                        ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
974                    }else{
975                        ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
976                    }
977                    continue;
978                }
979                break;
980
981            case TAG_GPSINFO:
982                if (ShowTags) printf("%s    GPS info dir:",IndentString);
983                {
984                    unsigned char * SubdirStart;
985                    SubdirStart = OffsetBase + Get32u(ValuePtr);
986                    if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
987                        ErrNonfatal("Illegal GPS directory link",0,0);
988                    }else{
989                        ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
990                    }
991                    continue;
992                }
993                break;
994
995            case TAG_FOCALLENGTH_35MM:
996                // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
997                // if its present, use it to compute equivalent focal length instead of
998                // computing it from sensor geometry and actual focal length.
999                ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
1000                break;
1001
1002            case TAG_DISTANCE_RANGE:
1003                // Three possible standard values:
1004                //   1 = macro, 2 = close, 3 = distant
1005                ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
1006                break;
1007        }
1008    }
1009
1010
1011    {
1012        // In addition to linking to subdirectories via exif tags,
1013        // there's also a potential link to another directory at the end of each
1014        // directory.  this has got to be the result of a committee!
1015        unsigned char * SubdirStart;
1016        unsigned Offset;
1017
1018        if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
1019            printf("DirStart %p offset from dirstart %d", DirStart, 2+12*NumDirEntries);
1020            Offset = Get32u(DirStart+2+12*NumDirEntries);
1021            if (Offset){
1022                SubdirStart = OffsetBase + Offset;
1023                if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){
1024                    printf("SubdirStart %p OffsetBase %p ExifLength %d Offset %d",
1025                        SubdirStart, OffsetBase, ExifLength, Offset);
1026                    if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){
1027                        // Jhead 1.3 or earlier would crop the whole directory!
1028                        // As Jhead produces this form of format incorrectness,
1029                        // I'll just let it pass silently
1030                        if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n");
1031                    }else{
1032                        ErrNonfatal("Illegal subdirectory link",0,0);
1033                    }
1034                }else{
1035                    if (SubdirStart <= OffsetBase+ExifLength){
1036                        if (ShowTags) printf("%s    Continued directory ",IndentString);
1037                        ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
1038                    }
1039                }
1040                if (Offset > ImageInfo.LargestExifOffset){
1041                    ImageInfo.LargestExifOffset = Offset;
1042                }
1043            }
1044        }else{
1045            // The exif header ends before the last next directory pointer.
1046        }
1047    }
1048
1049    if (ThumbnailOffset){
1050        ImageInfo.ThumbnailAtEnd = FALSE;
1051
1052        if (DumpExifMap){
1053            printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize);
1054        }
1055
1056        if (ThumbnailOffset <= ExifLength){
1057            if (ThumbnailSize > ExifLength-ThumbnailOffset){
1058                // If thumbnail extends past exif header, only save the part that
1059                // actually exists.  Canon's EOS viewer utility will do this - the
1060                // thumbnail extracts ok with this hack.
1061                ThumbnailSize = ExifLength-ThumbnailOffset;
1062                if (ShowTags) printf("Thumbnail incorrectly placed in header\n");
1063
1064            }
1065            // The thumbnail pointer appears to be valid.  Store it.
1066            ImageInfo.ThumbnailOffset = ThumbnailOffset;
1067            ImageInfo.ThumbnailSize = ThumbnailSize;
1068
1069            if (ShowTags){
1070                printf("Thumbnail size: %d bytes\n",ThumbnailSize);
1071            }
1072        }
1073    }
1074    printf("returning from ProcessExifDir");
1075}
1076
1077
1078//--------------------------------------------------------------------------
1079// Process a EXIF marker
1080// Describes all the drivel that most digital cameras include...
1081//--------------------------------------------------------------------------
1082void process_EXIF (unsigned char * ExifSection, unsigned int length)
1083{
1084    unsigned FirstOffset;
1085
1086    FocalplaneXRes = 0;
1087    FocalplaneUnits = 0;
1088    ExifImageWidth = 0;
1089    NumOrientations = 0;
1090
1091    if (ShowTags){
1092        printf("Exif header %d bytes long\n",length);
1093    }
1094
1095    {   // Check the EXIF header component
1096        static uchar ExifHeader[] = "Exif\0\0";
1097        if (memcmp(ExifSection+2, ExifHeader,6)){
1098            ErrNonfatal("Incorrect Exif header",0,0);
1099            return;
1100        }
1101    }
1102
1103    if (memcmp(ExifSection+8,"II",2) == 0){
1104        if (ShowTags) printf("Exif section in Intel order\n");
1105        MotorolaOrder = 0;
1106    }else{
1107        if (memcmp(ExifSection+8,"MM",2) == 0){
1108            if (ShowTags) printf("Exif section in Motorola order\n");
1109            MotorolaOrder = 1;
1110        }else{
1111            ErrNonfatal("Invalid Exif alignment marker.",0,0);
1112            return;
1113        }
1114    }
1115
1116    // Check the next value for correctness.
1117    if (Get16u(ExifSection+10) != 0x2a){
1118        ErrNonfatal("Invalid Exif start (1)",0,0);
1119        return;
1120    }
1121
1122    FirstOffset = Get32u(ExifSection+12);
1123    if (FirstOffset < 8 || FirstOffset+8 >= length) {
1124        ErrNonfatal("Invalid offset of first IFD value: %u", FirstOffset, 0);
1125        return;
1126    }
1127
1128    DirWithThumbnailPtrs = NULL;
1129
1130
1131    // First directory starts 16 bytes in.  All offset are relative to 8 bytes in.
1132    ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
1133
1134    ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
1135#ifdef SUPERDEBUG
1136    printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at"));
1137#endif
1138    if (DumpExifMap){
1139        unsigned a,b;
1140        printf("Map: %05d- End of exif\n",length-8);
1141//        for (a=0;a<length-8;a+= 10){
1142//            printf("Map: %05d ",a);
1143//            for (b=0;b<10;b++) printf(" %02x",*(ExifSection+8+a+b));
1144//            printf("\n");
1145//        }
1146        for (a = 0; a < length - 8; ++a) {
1147            unsigned char c = *(ExifSection+8+a);
1148            unsigned pc = isprint(c) ? c : ' ';
1149            printf("Map: %4d %02x %c", a, c, pc);
1150        }
1151    }
1152
1153
1154    // Compute the CCD width, in millimeters.
1155    if (FocalplaneXRes != 0){
1156        // Note: With some cameras, its not possible to compute this correctly because
1157        // they don't adjust the indicated focal plane resolution units when using less
1158        // than maximum resolution, so the CCDWidth value comes out too small.  Nothing
1159        // that Jhad can do about it - its a camera problem.
1160        ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
1161
1162        if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0
1163            && ImageInfo.FocalLength35mmEquiv == 0){
1164            // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
1165            // already got it explicitly from a tag.
1166            ImageInfo.FocalLength35mmEquiv = (int)(
1167                (double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom
1168                / ImageInfo.CCDWidth * 36 + 0.5);
1169        }
1170    }
1171}
1172
1173static const TagTable_t* TagToTagTableEntry(unsigned short tag)
1174{
1175    unsigned int i;
1176    for (i = 0; i < TAG_TABLE_SIZE; i++) {
1177        if (TagTable[i].Tag == tag) {
1178            printf("found tag %d", tag);
1179            int format = TagTable[i].Format;
1180            if (format == 0) {
1181                printf("tag %s format not defined ***** YOU MUST ADD THE FORMAT TO THE TagTable in exif.c!!!!", TagTable[i].Desc);
1182                return NULL;
1183            }
1184            return &TagTable[i];
1185        }
1186    }
1187    printf("tag %d NOT FOUND", tag);
1188    return NULL;
1189}
1190
1191static void writeExifTagAndData(int tag,
1192                                int format,
1193                                long components,
1194                                long value,
1195                                int valueInString,
1196                                char* Buffer,
1197                                int* DirIndex,
1198                                int* DataWriteIndex) {
1199    void* componentsPosition = NULL; // for saving component position
1200
1201    Put16u(Buffer+ (*DirIndex), tag);                    // Tag
1202    Put16u(Buffer+(*DirIndex) + 2, format);              // Format
1203    if (format == FMT_STRING && components == -1) {
1204        components = strlen((char*)value) + 1;                 // account for null terminator
1205        if (components & 1) ++components;               // no odd lengths
1206    } else if (format == FMT_SSHORT && components == -1) {
1207        // jhead only supports reading one SSHORT anyway
1208        components = 1;
1209    }
1210    if (format == FMT_UNDEFINED && components == -1) {
1211        // check if this UNDEFINED format is actually ASCII (as it usually is)
1212        // if so, we can calculate the size
1213        if(memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0) {
1214            components = sizeof(ExifAsciiPrefix) +
1215                         strlen((char*)value + sizeof(ExifAsciiPrefix)) + 1;
1216            if (components & 1) ++components;               // no odd lengths
1217        }
1218    }
1219    Put32u(Buffer+(*DirIndex) + 4, components);         // Components
1220    componentsPosition = Buffer+(*DirIndex) + 4; // components # can change for lists
1221    printf("# components: %ld", components);
1222    if (format == FMT_STRING) {
1223        // short strings can fit right in the long, otherwise have to
1224        // go in the data area
1225        if (components <= 4) {
1226            strcpy(Buffer+(*DirIndex) + 8, (char*)value);
1227        } else {
1228            Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
1229            printf("copying value %s to %d", (char*)value, (*DataWriteIndex));
1230            strncpy(Buffer+(*DataWriteIndex), (char*)value, components);
1231            (*DataWriteIndex) += components;
1232        }
1233    } else if ((format == FMT_UNDEFINED) &&
1234               (memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0)) {
1235        // short strings can fit right in the long, otherwise have to
1236        // go in the data area
1237        if (components <= 4) {
1238            memcpy(Buffer+(*DirIndex) + 8, (char*)value, components);
1239        } else {
1240            Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
1241            printf("copying %s to %d", (char*)value + sizeof(ExifAsciiPrefix), (*DataWriteIndex));
1242            memcpy(Buffer+(*DataWriteIndex), (char*)value, components);
1243            (*DataWriteIndex) += components;
1244        }
1245    } else if (!valueInString) {
1246        Put32u(Buffer+(*DirIndex) + 8, value);   // Value
1247    } else {
1248        Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
1249        // Usually the separator is ',', but sometimes ':' is used, like
1250        // TAG_GPS_TIMESTAMP.
1251        char* curElement = strtok((char*)value, ",:");
1252        int i;
1253
1254        // (components == -1) Need to handle lists with unknown length too
1255        for (i = 0; ((i < components) || (components == -1)) && curElement != NULL; i++) {
1256#ifdef SUPERDEBUG
1257            printf("processing component %s format %s", curElement, formatStr(format));
1258#endif
1259            // elements are separated by commas
1260            if (format == FMT_URATIONAL) {
1261                unsigned int numerator;
1262                unsigned int denominator;
1263                char* separator = strchr(curElement, '/');
1264                if (separator) {
1265                    numerator = atoi(curElement);
1266                    denominator = atoi(separator + 1);
1267                } else {
1268                    double value = atof(curElement);
1269                    ConvertDoubleToURational(value, &numerator, &denominator);
1270                }
1271                Put32u(Buffer+(*DataWriteIndex), numerator);
1272                Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1273                (*DataWriteIndex) += 8;
1274            } else if (format == FMT_SRATIONAL) {
1275                int numerator;
1276                int denominator;
1277                char* separator = strchr(curElement, '/');
1278                if (separator) {
1279                    numerator = atoi(curElement);
1280                    denominator = atoi(separator + 1);
1281                } else {
1282                    double value = atof(curElement);
1283                    ConvertDoubleToSRational(value, &numerator, &denominator);
1284                }
1285                Put32u(Buffer+(*DataWriteIndex), numerator);
1286                Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1287                (*DataWriteIndex) += 8;
1288            } else if ((components == -1) && ((format == FMT_USHORT) || (format == FMT_SSHORT))) {
1289                // variable components need to go into data write area
1290                value = atoi(curElement);
1291                Put16u(Buffer+(*DataWriteIndex), value);
1292                (*DataWriteIndex) += 4;
1293            } else {
1294                // TODO: doesn't handle multiple components yet -- if more than one, have to put in data write area.
1295                value = atoi(curElement);
1296                Put32u(Buffer+(*DirIndex) + 8, value);   // Value
1297            }
1298            curElement = strtok(NULL, ",:");
1299        }
1300        if (components == -1) Put32u(componentsPosition, i); // update component # for unknowns
1301    }
1302    (*DirIndex) += 12;
1303}
1304
1305#ifdef SUPERDEBUG
1306char* formatStr(int format) {
1307    switch (format) {
1308        case FMT_BYTE: return "FMT_BYTE"; break;
1309        case FMT_STRING: return "FMT_STRING"; break;
1310        case FMT_USHORT: return "FMT_USHORT"; break;
1311        case FMT_ULONG: return "FMT_ULONG"; break;
1312        case FMT_URATIONAL: return "FMT_URATIONAL"; break;
1313        case FMT_SBYTE: return "FMT_SBYTE"; break;
1314        case FMT_UNDEFINED: return "FMT_UNDEFINED"; break;
1315        case FMT_SSHORT: return "FMT_SSHORT"; break;
1316        case FMT_SLONG: return "FMT_SLONG"; break;
1317        case FMT_SRATIONAL: return "FMT_SRATIONAL"; break;
1318        case FMT_SINGLE: return "FMT_SINGLE"; break;
1319        case FMT_DOUBLE: return "FMT_SINGLE"; break;
1320        default: return "UNKNOWN";
1321    }
1322}
1323#endif
1324
1325//--------------------------------------------------------------------------
1326// Create minimal exif header - just date and thumbnail pointers,
1327// so that date and thumbnail may be filled later.
1328//--------------------------------------------------------------------------
1329static void create_EXIF_internal(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag, char* Buffer)
1330{
1331    unsigned short NumEntries;
1332    int DataWriteIndex;
1333    int DirIndex;
1334    int DirExifLink = 0;
1335
1336#ifdef SUPERDEBUG
1337    ALOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
1338#endif
1339
1340    MotorolaOrder = 0;
1341
1342    memcpy(Buffer+2, "Exif\0\0II",8);
1343    Put16u(Buffer+10, 0x2a);
1344
1345    DataWriteIndex = 16;
1346    Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset.  Means start 16 bytes in.
1347
1348    {
1349        DirIndex = DataWriteIndex;
1350        NumEntries = 1 + exifTagCount;  // the extra is the thumbnail
1351        if (gpsTagCount) {
1352            ++NumEntries;       // allow for the GPS info tag
1353        }
1354        if (!hasDateTimeTag) {
1355            // We have to write extra date time tag. The entry number should be
1356            // adjusted.
1357            ++NumEntries;
1358        }
1359        DataWriteIndex += 2 + NumEntries*12 + 4;
1360
1361        Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1362        DirIndex += 2;
1363
1364        // Entries go here...
1365        if (!hasDateTimeTag) {
1366            // Date/time entry
1367            char* dateTime = NULL;
1368            char dateBuf[20];
1369            if (ImageInfo.numDateTimeTags) {
1370                // If we had a pre-existing exif header, use time from that.
1371                dateTime = ImageInfo.DateTime;
1372            } else {
1373                // Oterwise, use the file's timestamp.
1374                FileTimeAsString(dateBuf);
1375                dateTime = dateBuf;
1376            }
1377            writeExifTagAndData(TAG_DATETIME,
1378                                FMT_STRING,
1379                                20,
1380                                (long)(char*)dateBuf,
1381                                FALSE,
1382                                Buffer,
1383                                &DirIndex,
1384                                &DataWriteIndex);
1385
1386        }
1387        if (exifTagCount > 0) {
1388            int i;
1389            for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1390                if (elements[i].GpsTag) {
1391                    continue;
1392                }
1393                const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag);
1394                if (entry == NULL) {
1395                    continue;
1396                }
1397#ifdef SUPERDEBUG
1398                ALOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
1399#endif
1400                writeExifTagAndData(elements[i].Tag,
1401                                    entry->Format,
1402                                    entry->DataLength,
1403                                    (long)elements[i].Value,
1404                                    TRUE,
1405                                    Buffer,
1406                                    &DirIndex,
1407                                    &DataWriteIndex);
1408            }
1409
1410            if (gpsTagCount) {
1411                // Link to gps dir entry
1412                writeExifTagAndData(TAG_GPSINFO,
1413                                    FMT_ULONG,
1414                                    1,
1415                                    DataWriteIndex-8,
1416                                    FALSE,
1417                                    Buffer,
1418                                    &DirIndex,
1419                                    &DataWriteIndex);
1420            }
1421
1422            // Link to exif dir entry
1423            int exifDirPtr = DataWriteIndex-8;
1424            if (gpsTagCount) {
1425                exifDirPtr += 2 + gpsTagCount*12 + 4;
1426            }
1427            DirExifLink = DirIndex;
1428            writeExifTagAndData(TAG_EXIF_OFFSET,
1429                                FMT_ULONG,
1430                                1,
1431                                exifDirPtr,
1432                                FALSE,
1433                                Buffer,
1434                                &DirIndex,
1435                                &DataWriteIndex);
1436        }
1437
1438        // End of directory - contains optional link to continued directory.
1439        Put32u(Buffer+DirIndex, 0);
1440        printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1441    }
1442
1443
1444    // GPS Section
1445    if (gpsTagCount) {
1446        DirIndex = DataWriteIndex;
1447        printf("Starting GPS section DirIndex = %d", DirIndex);
1448        NumEntries = gpsTagCount;
1449        DataWriteIndex += 2 + NumEntries*12 + 4;
1450
1451        Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1452        DirIndex += 2;
1453        {
1454            int i;
1455            for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1456                if (!elements[i].GpsTag) {
1457                    continue;
1458                }
1459                const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag);
1460                if (entry == NULL) {
1461                    continue;
1462                }
1463#ifdef SUPERDEBUG
1464                ALOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
1465#endif
1466                writeExifTagAndData(elements[i].Tag,
1467                                    entry->Format,
1468                                    entry->DataLength,
1469                                    (long)elements[i].Value,
1470                                    TRUE,
1471                                    Buffer,
1472                                    &DirIndex,
1473                                    &DataWriteIndex);
1474            }
1475        }
1476
1477        // End of directory - contains optional link to continued directory.
1478        Put32u(Buffer+DirIndex, 0);
1479        printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1480    }
1481
1482    {
1483        // Overwriting TAG_EXIF_OFFSET which links to this directory
1484        Put32u(Buffer+DirExifLink+8, DataWriteIndex-8);
1485
1486        printf("Starting Thumbnail section DirIndex = %d", DirIndex);
1487        DirIndex = DataWriteIndex;
1488        NumEntries = 2;
1489        DataWriteIndex += 2 + NumEntries*12 + 4;
1490
1491        Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1492        DirIndex += 2;
1493        {
1494            // Link to exif dir entry
1495            writeExifTagAndData(TAG_THUMBNAIL_OFFSET,
1496                                FMT_ULONG,
1497                                1,
1498                                DataWriteIndex-8,
1499                                FALSE,
1500                                Buffer,
1501                                &DirIndex,
1502                                &DataWriteIndex);
1503        }
1504
1505        {
1506            // Link to exif dir entry
1507            writeExifTagAndData(TAG_THUMBNAIL_LENGTH,
1508                                FMT_ULONG,
1509                                1,
1510                                0,
1511                                FALSE,
1512                                Buffer,
1513                                &DirIndex,
1514                                &DataWriteIndex);
1515        }
1516
1517        // End of directory - contains optional link to continued directory.
1518        Put32u(Buffer+DirIndex, 0);
1519        printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1520    }
1521
1522
1523    Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
1524    Buffer[1] = (unsigned char)DataWriteIndex;
1525
1526    // Remove old exif section, if there was one.
1527    RemoveSectionType(M_EXIF);
1528
1529    {
1530        // Sections need malloced buffers, so do that now, especially because
1531        // we now know how big it needs to be allocated.
1532        unsigned char * NewBuf = malloc(DataWriteIndex);
1533        if (NewBuf == NULL){
1534            ErrFatal("Could not allocate memory");
1535        }
1536        memcpy(NewBuf, Buffer, DataWriteIndex);
1537
1538        CreateSection(M_EXIF, NewBuf, DataWriteIndex);
1539
1540        // Re-parse new exif section, now that its in place
1541        // otherwise, we risk touching data that has already been freed.
1542        process_EXIF(NewBuf, DataWriteIndex);
1543    }
1544}
1545
1546void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag)
1547{
1548    // It is hard to calculate exact necessary size for editing the exif
1549    // header dynamically, so we are using the maximum size of EXIF, 64K
1550    const int EXIF_MAX_SIZE = 1024*64;
1551    char* Buffer = malloc(EXIF_MAX_SIZE);
1552
1553    if (Buffer != NULL) {
1554        create_EXIF_internal(elements, exifTagCount, gpsTagCount, hasDateTimeTag, Buffer);
1555        free(Buffer);
1556    } else {
1557        ErrFatal("Could not allocate memory");
1558    }
1559}
1560
1561//--------------------------------------------------------------------------
1562// Cler the rotation tag in the exif header to 1.
1563//--------------------------------------------------------------------------
1564const char * ClearOrientation(void)
1565{
1566    int a;
1567    if (NumOrientations == 0) return NULL;
1568
1569    for (a=0;a<NumOrientations;a++){
1570        switch(OrientationNumFormat[a]){
1571            case FMT_SBYTE:
1572            case FMT_BYTE:
1573                *(uchar *)(OrientationPtr[a]) = 1;
1574                break;
1575
1576            case FMT_USHORT:
1577                Put16u(OrientationPtr[a], 1);
1578                break;
1579
1580            case FMT_ULONG:
1581            case FMT_SLONG:
1582                memset(OrientationPtr, 0, 4);
1583                // Can't be bothered to write  generic Put32 if I only use it once.
1584                if (MotorolaOrder){
1585                    ((uchar *)OrientationPtr[a])[3] = 1;
1586                }else{
1587                    ((uchar *)OrientationPtr[a])[0] = 1;
1588                }
1589                break;
1590
1591            default:
1592                return NULL;
1593        }
1594    }
1595
1596    return OrientTab[ImageInfo.Orientation];
1597}
1598
1599
1600
1601//--------------------------------------------------------------------------
1602// Remove thumbnail out of the exif image.
1603//--------------------------------------------------------------------------
1604int RemoveThumbnail(unsigned char * ExifSection)
1605{
1606    if (!DirWithThumbnailPtrs ||
1607        ImageInfo.ThumbnailOffset == 0 ||
1608        ImageInfo.ThumbnailSize == 0){
1609        // No thumbnail, or already deleted it.
1610        return 0;
1611    }
1612    if (ImageInfo.ThumbnailAtEnd == FALSE){
1613        ErrNonfatal("Thumbnail is not at end of header, can't chop it off", 0, 0);
1614        return 0;
1615    }
1616
1617    {
1618        int de;
1619        int NumDirEntries;
1620        NumDirEntries = Get16u(DirWithThumbnailPtrs);
1621
1622        for (de=0;de<NumDirEntries;de++){
1623            int Tag;
1624            unsigned char * DirEntry;
1625            DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de);
1626            Tag = Get16u(DirEntry);
1627            if (Tag == TAG_THUMBNAIL_LENGTH){
1628                // Set length to zero.
1629                if (Get16u(DirEntry+2) != FMT_ULONG){
1630                    // non standard format encoding.  Can't do it.
1631                    ErrNonfatal("Can't remove thumbnail", 0, 0);
1632                    return 0;
1633                }
1634                Put32u(DirEntry+8, 0);
1635            }
1636        }
1637    }
1638
1639    // This is how far the non thumbnail data went.
1640    return ImageInfo.ThumbnailOffset+8;
1641
1642}
1643
1644
1645//--------------------------------------------------------------------------
1646// Convert exif time to Unix time structure
1647//--------------------------------------------------------------------------
1648int Exif2tm(struct tm * timeptr, char * ExifTime)
1649{
1650    int a;
1651
1652    timeptr->tm_wday = -1;
1653
1654    // Check for format: YYYY:MM:DD HH:MM:SS format.
1655    // Date and time normally separated by a space, but also seen a ':' there, so
1656    // skip the middle space with '%*c' so it can be any character.
1657    a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
1658            &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
1659            &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
1660
1661
1662    if (a == 6){
1663        timeptr->tm_isdst = -1;
1664        timeptr->tm_mon -= 1;      // Adjust for unix zero-based months
1665        timeptr->tm_year -= 1900;  // Adjust for year starting at 1900
1666        return TRUE; // worked.
1667    }
1668
1669    return FALSE; // Wasn't in Exif date format.
1670}
1671
1672
1673//--------------------------------------------------------------------------
1674// Show the collected image info, displaying camera F-stop and shutter speed
1675// in a consistent and legible fashion.
1676//--------------------------------------------------------------------------
1677void ShowImageInfo(int ShowFileInfo)
1678{
1679    if (ShowFileInfo){
1680        printf("File name    : %s\n",ImageInfo.FileName);
1681        printf("File size    : %d bytes\n",ImageInfo.FileSize);
1682
1683        {
1684            char Temp[20];
1685            FileTimeAsString(Temp);
1686            printf("File date    : %s\n",Temp);
1687        }
1688    }
1689
1690    if (ImageInfo.CameraMake[0]){
1691        printf("Camera make  : %s\n",ImageInfo.CameraMake);
1692        printf("Camera model : %s\n",ImageInfo.CameraModel);
1693    }
1694    if (ImageInfo.DateTime[0]){
1695        printf("Date/Time    : %s\n",ImageInfo.DateTime);
1696    }
1697    printf("Resolution   : %d x %d\n",ImageInfo.Width, ImageInfo.Height);
1698
1699    if (ImageInfo.Orientation > 1){
1700        // Only print orientation if one was supplied, and if its not 1 (normal orientation)
1701        printf("Orientation  : %s\n", OrientTab[ImageInfo.Orientation]);
1702    }
1703
1704    if (ImageInfo.IsColor == 0){
1705        printf("Color/bw     : Black and white\n");
1706    }
1707
1708    if (ImageInfo.FlashUsed >= 0){
1709        if (ImageInfo.FlashUsed & 1){
1710            printf("Flash used   : Yes");
1711            switch (ImageInfo.FlashUsed){
1712	            case 0x5: printf(" (Strobe light not detected)"); break;
1713	            case 0x7: printf(" (Strobe light detected) "); break;
1714	            case 0x9: printf(" (manual)"); break;
1715	            case 0xd: printf(" (manual, return light not detected)"); break;
1716	            case 0xf: printf(" (manual, return light  detected)"); break;
1717	            case 0x19:printf(" (auto)"); break;
1718	            case 0x1d:printf(" (auto, return light not detected)"); break;
1719	            case 0x1f:printf(" (auto, return light detected)"); break;
1720	            case 0x41:printf(" (red eye reduction mode)"); break;
1721	            case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
1722	            case 0x47:printf(" (red eye reduction mode return light  detected)"); break;
1723	            case 0x49:printf(" (manual, red eye reduction mode)"); break;
1724	            case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
1725	            case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
1726	            case 0x59:printf(" (auto, red eye reduction mode)"); break;
1727	            case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
1728	            case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
1729            }
1730        }else{
1731            printf("Flash used   : No");
1732            switch (ImageInfo.FlashUsed){
1733	            case 0x18:printf(" (auto)"); break;
1734            }
1735        }
1736        printf("\n");
1737    }
1738
1739
1740    if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
1741        printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom);
1742        if (ImageInfo.FocalLength35mmEquiv){
1743            printf("  (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
1744        }
1745        printf("\n");
1746    }
1747
1748    if (ImageInfo.DigitalZoomRatio > 1){
1749        // Digital zoom used.  Shame on you!
1750        printf("Digital Zoom : %5.3fx\n", (double)ImageInfo.DigitalZoomRatio);
1751    }
1752
1753    if (ImageInfo.CCDWidth){
1754        printf("CCD width    : %4.2fmm\n",(double)ImageInfo.CCDWidth);
1755    }
1756
1757    if (ImageInfo.ExposureTime){
1758        if (ImageInfo.ExposureTime < 0.010){
1759            printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
1760        }else{
1761            printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
1762        }
1763        if (ImageInfo.ExposureTime <= 0.5){
1764            printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1765        }
1766        printf("\n");
1767    }
1768    if (ImageInfo.ApertureFNumber){
1769        printf("Aperture     : f/%5.3f\n",(double)ImageInfo.ApertureFNumber);
1770    }
1771    if (ImageInfo.Distance){
1772        if (ImageInfo.Distance < 0){
1773            printf("Focus dist.  : Infinite\n");
1774        }else{
1775            printf("Focus dist.  : %4.2fm\n",(double)ImageInfo.Distance);
1776        }
1777    }
1778
1779    if (ImageInfo.ISOequivalent){
1780        printf("ISO equiv.   : %2d\n",(int)ImageInfo.ISOequivalent);
1781    }
1782
1783    if (ImageInfo.ExposureBias){
1784        // If exposure bias was specified, but set to zero, presumably its no bias at all,
1785        // so only show it if its nonzero.
1786        printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
1787    }
1788
1789    switch(ImageInfo.Whitebalance) {
1790        case 1:
1791            printf("Whitebalance : Manual\n");
1792            break;
1793        case 0:
1794            printf("Whitebalance : Auto\n");
1795            break;
1796    }
1797
1798    //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
1799    switch(ImageInfo.LightSource) {
1800        case 1:
1801            printf("Light Source : Daylight\n");
1802            break;
1803        case 2:
1804            printf("Light Source : Fluorescent\n");
1805            break;
1806        case 3:
1807            printf("Light Source : Incandescent\n");
1808            break;
1809        case 4:
1810            printf("Light Source : Flash\n");
1811            break;
1812        case 9:
1813            printf("Light Source : Fine weather\n");
1814            break;
1815        case 11:
1816            printf("Light Source : Shade\n");
1817            break;
1818        default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
1819            // If it just says 'unknown' or we don't know it, then
1820            // don't bother showing it - it doesn't add any useful information.
1821    }
1822
1823    if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs
1824        switch(ImageInfo.MeteringMode) {
1825        case 2:
1826            printf("Metering Mode: center weight\n");
1827            break;
1828        case 3:
1829            printf("Metering Mode: spot\n");
1830            break;
1831        case 5:
1832            printf("Metering Mode: matrix\n");
1833            break;
1834        }
1835    }
1836
1837    if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
1838        switch(ImageInfo.ExposureProgram) {
1839        case 1:
1840            printf("Exposure     : Manual\n");
1841            break;
1842        case 2:
1843            printf("Exposure     : program (auto)\n");
1844            break;
1845        case 3:
1846            printf("Exposure     : aperture priority (semi-auto)\n");
1847            break;
1848        case 4:
1849            printf("Exposure     : shutter priority (semi-auto)\n");
1850            break;
1851        case 5:
1852            printf("Exposure     : Creative Program (based towards depth of field)\n");
1853            break;
1854        case 6:
1855            printf("Exposure     : Action program (based towards fast shutter speed)\n");
1856            break;
1857        case 7:
1858            printf("Exposure     : Portrait Mode\n");
1859            break;
1860        case 8:
1861            printf("Exposure     : LandscapeMode \n");
1862            break;
1863        default:
1864            break;
1865        }
1866    }
1867    switch(ImageInfo.ExposureMode){
1868        case 0: // Automatic (not worth cluttering up output for)
1869            break;
1870        case 1: printf("Exposure Mode: Manual\n");
1871            break;
1872        case 2: printf("Exposure Mode: Auto bracketing\n");
1873            break;
1874    }
1875
1876    if (ImageInfo.DistanceRange) {
1877        printf("Focus range  : ");
1878        switch(ImageInfo.DistanceRange) {
1879            case 1:
1880                printf("macro");
1881                break;
1882            case 2:
1883                printf("close");
1884                break;
1885            case 3:
1886                printf("distant");
1887                break;
1888        }
1889        printf("\n");
1890    }
1891
1892
1893
1894    if (ImageInfo.Process != M_SOF0){
1895        // don't show it if its the plain old boring 'baseline' process, but do
1896        // show it if its something else, like 'progressive' (used on web sometimes)
1897        int a;
1898        for (a=0;;a++){
1899            if (a >= (int)PROCESS_TABLE_SIZE){
1900                // ran off the end of the table.
1901                printf("Jpeg process : Unknown\n");
1902                break;
1903            }
1904            if (ProcessTable[a].Tag == ImageInfo.Process){
1905                printf("Jpeg process : %s\n",ProcessTable[a].Desc);
1906                break;
1907            }
1908        }
1909    }
1910
1911    if (ImageInfo.GpsInfoPresent){
1912        printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
1913        printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
1914        if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
1915    }
1916
1917    // Print the comment. Print 'Comment:' for each new line of comment.
1918    if (ImageInfo.Comments[0]){
1919        int a,c;
1920        printf("Comment      : ");
1921        if (!ImageInfo.CommentWidchars){
1922            for (a=0;a<MAX_COMMENT_SIZE;a++){
1923                c = ImageInfo.Comments[a];
1924                if (c == '\0') break;
1925                if (c == '\n'){
1926                    // Do not start a new line if the string ends with a carriage return.
1927                    if (ImageInfo.Comments[a+1] != '\0'){
1928                        printf("\nComment      : ");
1929                    }else{
1930                        printf("\n");
1931                    }
1932                }else{
1933                    putchar(c);
1934                }
1935            }
1936            printf("\n");
1937        }else{
1938            printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
1939        }
1940    }
1941    if (ImageInfo.ThumbnailOffset){
1942        printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
1943    } else {
1944        printf("NO thumbnail");
1945    }
1946}
1947
1948
1949//--------------------------------------------------------------------------
1950// Summarize highlights of image info on one line (suitable for grep-ing)
1951//--------------------------------------------------------------------------
1952void ShowConciseImageInfo(void)
1953{
1954    printf("\"%s\"",ImageInfo.FileName);
1955
1956    printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);
1957
1958    if (ImageInfo.ExposureTime){
1959        if (ImageInfo.ExposureTime <= 0.5){
1960            printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1961        }else{
1962            printf(" (%3.1f)",ImageInfo.ExposureTime);
1963        }
1964    }
1965
1966    if (ImageInfo.ApertureFNumber){
1967        printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
1968    }
1969
1970    if (ImageInfo.FocalLength35mmEquiv){
1971        printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
1972    }
1973
1974    if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
1975        printf(" (flash)");
1976    }
1977
1978    if (ImageInfo.IsColor == 0){
1979        printf(" (bw)");
1980    }
1981
1982    printf("\n");
1983}
1984