• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 static unsigned char * DirWithThumbnailPtrs;
16 static double FocalplaneXRes;
17 static double FocalplaneUnits;
18 static int ExifImageWidth;
19 static int MotorolaOrder = 0;
20 
21 // for fixing the rotation.
22 static void * OrientationPtr[2];
23 static int    OrientationNumFormat[2];
24 int 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 LOGE
32 #endif
33 
34 //--------------------------------------------------------------------------
35 // Table of Jpeg encoding process names
36 static 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 
67 static 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 
79 const 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"
192 static 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 
TagNameToValue(const char * tagName)301 int 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 
314 //--------------------------------------------------------------------------
315 // Convert a 16 bit unsigned value to file's native byte order
316 //--------------------------------------------------------------------------
Put16u(void * Short,unsigned short PutValue)317 static void Put16u(void * Short, unsigned short PutValue)
318 {
319     if (MotorolaOrder){
320         ((uchar *)Short)[0] = (uchar)(PutValue>>8);
321         ((uchar *)Short)[1] = (uchar)PutValue;
322     }else{
323         ((uchar *)Short)[0] = (uchar)PutValue;
324         ((uchar *)Short)[1] = (uchar)(PutValue>>8);
325     }
326 }
327 
328 //--------------------------------------------------------------------------
329 // Convert a 16 bit unsigned value from file's native byte order
330 //--------------------------------------------------------------------------
Get16u(void * Short)331 int Get16u(void * Short)
332 {
333     if (MotorolaOrder){
334         return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
335     }else{
336         return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
337     }
338 }
339 
340 //--------------------------------------------------------------------------
341 // Convert a 32 bit signed value from file's native byte order
342 //--------------------------------------------------------------------------
Get32s(void * Long)343 int Get32s(void * Long)
344 {
345     if (MotorolaOrder){
346         return  ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
347               | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
348     }else{
349         return  ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
350               | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
351     }
352 }
353 
354 //--------------------------------------------------------------------------
355 // Convert a 32 bit unsigned value to file's native byte order
356 //--------------------------------------------------------------------------
Put32u(void * Value,unsigned PutValue)357 void Put32u(void * Value, unsigned PutValue)
358 {
359     if (MotorolaOrder){
360         ((uchar *)Value)[0] = (uchar)(PutValue>>24);
361         ((uchar *)Value)[1] = (uchar)(PutValue>>16);
362         ((uchar *)Value)[2] = (uchar)(PutValue>>8);
363         ((uchar *)Value)[3] = (uchar)PutValue;
364     }else{
365         ((uchar *)Value)[0] = (uchar)PutValue;
366         ((uchar *)Value)[1] = (uchar)(PutValue>>8);
367         ((uchar *)Value)[2] = (uchar)(PutValue>>16);
368         ((uchar *)Value)[3] = (uchar)(PutValue>>24);
369     }
370 }
371 
372 //--------------------------------------------------------------------------
373 // Convert a 32 bit unsigned value from file's native byte order
374 //--------------------------------------------------------------------------
Get32u(void * Long)375 unsigned Get32u(void * Long)
376 {
377     return (unsigned)Get32s(Long) & 0xffffffff;
378 }
379 
380 //--------------------------------------------------------------------------
381 // Display a number as one of its many formats
382 //--------------------------------------------------------------------------
PrintFormatNumber(void * ValuePtr,int Format,int ByteCount)383 void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount)
384 {
385     int s,n;
386 
387     for(n=0;n<16;n++){
388         switch(Format){
389             case FMT_SBYTE:
390             case FMT_BYTE:      printf("%02x",*(uchar *)ValuePtr); s=1;  break;
391             case FMT_USHORT:    printf("%d",Get16u(ValuePtr)); s=2;      break;
392             case FMT_ULONG:
393             case FMT_SLONG:     printf("%d",Get32s(ValuePtr)); s=4;      break;
394             case FMT_SSHORT:    printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break;
395             case FMT_URATIONAL:
396             case FMT_SRATIONAL:
397                printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
398                s = 8;
399                break;
400 
401             case FMT_SINGLE:    printf("%f",(double)*(float *)ValuePtr); s=8; break;
402             case FMT_DOUBLE:    printf("%f",*(double *)ValuePtr);        s=8; break;
403             default:
404                 printf("Unknown format %d:", Format);
405                 return;
406         }
407         ByteCount -= s;
408         if (ByteCount <= 0) break;
409         printf(", ");
410         ValuePtr = (void *)((char *)ValuePtr + s);
411 
412     }
413     if (n >= 16) printf("...");
414 }
415 
416 
417 //--------------------------------------------------------------------------
418 // Evaluate number, be it int, rational, or float from directory.
419 //--------------------------------------------------------------------------
ConvertAnyFormat(void * ValuePtr,int Format)420 double ConvertAnyFormat(void * ValuePtr, int Format)
421 {
422     double Value;
423     Value = 0;
424 
425     switch(Format){
426         case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
427         case FMT_BYTE:      Value = *(uchar *)ValuePtr;        break;
428 
429         case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
430         case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
431 
432         case FMT_URATIONAL:
433         case FMT_SRATIONAL:
434             {
435                 int Num,Den;
436                 Num = Get32s(ValuePtr);
437                 Den = Get32s(4+(char *)ValuePtr);
438                 if (Den == 0){
439                     Value = 0;
440                 }else{
441                     Value = (double)Num/Den;
442                 }
443                 break;
444             }
445 
446         case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
447         case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
448 
449         // Not sure if this is correct (never seen float used in Exif format)
450         case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
451         case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
452 
453         default:
454             ErrNonfatal("Illegal format code %d",Format,0);
455     }
456     return Value;
457 }
458 
459 //--------------------------------------------------------------------------
460 // Process one of the nested EXIF directories.
461 //--------------------------------------------------------------------------
ProcessExifDir(unsigned char * DirStart,unsigned char * OffsetBase,unsigned ExifLength,int NestingLevel)462 static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,
463         unsigned ExifLength, int NestingLevel)
464 {
465     int de;
466     int a;
467     int NumDirEntries;
468     unsigned ThumbnailOffset = 0;
469     unsigned ThumbnailSize = 0;
470     char IndentString[25];
471 
472     printf("ProcessExifDir");
473     if (NestingLevel > 4){
474         ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
475         return;
476     }
477 
478     memset(IndentString, ' ', 25);
479     IndentString[NestingLevel * 4] = '\0';
480 
481 
482     NumDirEntries = Get16u(DirStart);
483     #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
484 
485     {
486         unsigned char * DirEnd;
487         DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
488         if (DirEnd+4 > (OffsetBase+ExifLength)){
489             if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
490                 // Version 1.3 of jhead would truncate a bit too much.
491                 // This also caught later on as well.
492             }else{
493                 ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0);
494                 return;
495             }
496         }
497         if (DumpExifMap){
498             printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
499         }
500 
501 
502     }
503 
504     if (ShowTags){
505         printf("(dir has %d entries)\n",NumDirEntries);
506     }
507 
508     for (de=0;de<NumDirEntries;de++){
509         int Tag, Format, Components;
510         unsigned char * ValuePtr;
511         int ByteCount;
512         unsigned char * DirEntry;
513         DirEntry = DIR_ENTRY_ADDR(DirStart, de);
514 
515         Tag = Get16u(DirEntry);
516         Format = Get16u(DirEntry+2);
517         Components = Get32u(DirEntry+4);
518 
519         if ((Format-1) >= NUM_FORMATS) {
520             // (-1) catches illegal zero case as unsigned underflows to positive large.
521             ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
522             continue;
523         }
524 
525         if ((unsigned)Components > 0x10000){
526             ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
527             continue;
528         }
529 
530         ByteCount = Components * BytesPerFormat[Format];
531 
532         if (ByteCount > 4){
533             unsigned OffsetVal;
534             OffsetVal = Get32u(DirEntry+8);
535             // If its bigger than 4 bytes, the dir entry contains an offset.
536             if (OffsetVal+ByteCount > ExifLength){
537                 // Bogus pointer offset and / or bytecount value
538                 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
539                 continue;
540             }
541             ValuePtr = OffsetBase+OffsetVal;
542 
543             if (OffsetVal > ImageInfo.LargestExifOffset){
544                 ImageInfo.LargestExifOffset = OffsetVal;
545             }
546 
547             if (DumpExifMap){
548                 printf("Map: %05d-%05d:   Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag);
549             }
550         }else{
551             // 4 bytes or less and value is in the dir entry itself
552             ValuePtr = DirEntry+8;
553         }
554 
555         if (Tag == TAG_MAKER_NOTE){
556             if (ShowTags){
557                 printf("%s    Maker note: ",IndentString);
558             }
559             ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
560             continue;
561         }
562 
563         if (ShowTags){
564             // Show tag name
565             for (a=0;;a++){
566                 if (a >= (int)TAG_TABLE_SIZE){
567                     printf("%s", IndentString);
568                     printf("    Unknown Tag %04x Value = ", Tag);
569                     break;
570                 }
571                 if (TagTable[a].Tag == Tag){
572                     printf("%s", IndentString);
573                     printf("    %s = ",TagTable[a].Desc);
574                     break;
575                 }
576             }
577 
578             // Show tag value.
579             switch(Format){
580                 case FMT_BYTE:
581                     if(ByteCount>1){
582                         printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr);
583                     }else{
584                         PrintFormatNumber(ValuePtr, Format, ByteCount);
585                         printf("\n");
586                     }
587                     break;
588 
589                 case FMT_UNDEFINED:
590                     // Undefined is typically an ascii string.
591 
592                 case FMT_STRING:
593                     // String arrays printed without function call (different from int arrays)
594                     {
595                           printf("\"%s\"", ValuePtr);
596 //                        int NoPrint = 0;
597 //                        printf("\"");
598 //                        for (a=0;a<ByteCount;a++){
599 //                            if (ValuePtr[a] >= 32){
600 //                                putchar(ValuePtr[a]);
601 //                                NoPrint = 0;
602 //                            }else{
603 //                                // Avoiding indicating too many unprintable characters of proprietary
604 //                                // bits of binary information this program may not know how to parse.
605 //                                if (!NoPrint && a != ByteCount-1){
606 //                                    putchar('?');
607 //                                    NoPrint = 1;
608 //                                }
609 //                            }
610 //                        }
611 //                        printf("\"\n");
612                     }
613                     break;
614 
615                 default:
616                     // Handle arrays of numbers later (will there ever be?)
617                     PrintFormatNumber(ValuePtr, Format, ByteCount);
618                     printf("\n");
619             }
620         }
621 
622         // Extract useful components of tag
623         switch(Tag){
624 
625             case TAG_MAKE:
626                 strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31);
627                 break;
628 
629             case TAG_MODEL:
630                 strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39);
631                 break;
632 
633             case TAG_DATETIME_ORIGINAL:
634                 // If we get a DATETIME_ORIGINAL, we use that one.
635                 strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
636                 // Fallthru...
637 
638             case TAG_DATETIME_DIGITIZED:
639             case TAG_DATETIME:
640                 if (!isdigit(ImageInfo.DateTime[0])){
641                     // If we don't already have a DATETIME_ORIGINAL, use whatever
642                     // time fields we may have.
643                     strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
644                 }
645 
646                 if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
647                     ErrNonfatal("More than %d date fields!  This is nuts", MAX_DATE_COPIES, 0);
648                     break;
649                 }
650                 ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
651                     (char *)ValuePtr - (char *)OffsetBase;
652                 break;
653 
654             case TAG_WINXP_COMMENT:
655                 if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
656                     // Already have a comment (probably windows comment), skip this one.
657                     if (ShowTags) printf("Windows XP commend and other comment in header\n");
658                     break; // Already have a windows comment, skip this one.
659                 }
660 
661                 if (ByteCount > 1){
662                     if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
663                     memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
664                     ImageInfo.CommentWidchars = ByteCount/2;
665                 }
666                 break;
667 
668             case TAG_USERCOMMENT:
669                 if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
670                     // Already have a comment (probably windows comment), skip this one.
671                     if (ShowTags) printf("Multiple comments in exif header\n");
672                     break; // Already have a windows comment, skip this one.
673                 }
674 
675                 // Comment is often padded with trailing spaces.  Remove these first.
676                 for (a=ByteCount;;){
677                     a--;
678                     if ((ValuePtr)[a] == ' '){
679                         (ValuePtr)[a] = '\0';
680                     }else{
681                         break;
682                     }
683                     if (a == 0) break;
684                 }
685 
686                 // Copy the comment
687                 if (memcmp(ValuePtr, "ASCII",5) == 0){
688                     for (a=5;a<10;a++){
689                         int c;
690                         c = (ValuePtr)[a];
691                         if (c != '\0' && c != ' '){
692                             strncpy(ImageInfo.Comments, (char *)ValuePtr+a, 199);
693                             break;
694                         }
695                     }
696                 }else{
697                     strncpy(ImageInfo.Comments, (char *)ValuePtr, MAX_COMMENT_SIZE-1);
698                 }
699                 break;
700 
701             case TAG_FNUMBER:
702                 // Simplest way of expressing aperture, so I trust it the most.
703                 // (overwrite previously computd value if there is one)
704                 ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
705                 break;
706 
707             case TAG_APERTURE:
708             case TAG_MAXAPERTURE:
709                 // More relevant info always comes earlier, so only use this field if we don't
710                 // have appropriate aperture information yet.
711                 if (ImageInfo.ApertureFNumber == 0){
712                     ImageInfo.ApertureFNumber
713                         = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
714                 }
715                 break;
716 
717             case TAG_FOCALLENGTH:
718                 // Nice digital cameras actually save the focal length as a function
719                 // of how farthey are zoomed in.
720                 ImageInfo.FocalLength.num = Get32u(ValuePtr);
721                 ImageInfo.FocalLength.denom = Get32u(4+(char *)ValuePtr);
722                 break;
723 
724             case TAG_SUBJECT_DISTANCE:
725                 // Inidcates the distacne the autofocus camera is focused to.
726                 // Tends to be less accurate as distance increases.
727                 ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
728                 break;
729 
730             case TAG_EXPOSURETIME:
731                 // Simplest way of expressing exposure time, so I trust it most.
732                 // (overwrite previously computd value if there is one)
733                 ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
734                 break;
735 
736             case TAG_SHUTTERSPEED:
737                 // More complicated way of expressing exposure time, so only use
738                 // this value if we don't already have it from somewhere else.
739                 if (ImageInfo.ExposureTime == 0){
740                     ImageInfo.ExposureTime
741                         = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
742                 }
743                 break;
744 
745 
746             case TAG_FLASH:
747                 ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format);
748                 break;
749 
750             case TAG_ORIENTATION:
751                 if (NumOrientations >= 2){
752                     // Can have another orientation tag for the thumbnail, but if there's
753                     // a third one, things are stringae.
754                     ErrNonfatal("More than two orientation tags!",0,0);
755                     break;
756                 }
757                 OrientationPtr[NumOrientations] = ValuePtr;
758                 OrientationNumFormat[NumOrientations] = Format;
759                 if (NumOrientations == 0){
760                     ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
761                 }
762                 if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){
763                     ErrNonfatal("Undefined rotation value %d", ImageInfo.Orientation, 0);
764                     ImageInfo.Orientation = 0;
765                 }
766                 NumOrientations += 1;
767                 break;
768 
769             case TAG_EXIF_IMAGELENGTH:
770             case TAG_EXIF_IMAGEWIDTH:
771                 // Use largest of height and width to deal with images that have been
772                 // rotated to portrait format.
773                 a = (int)ConvertAnyFormat(ValuePtr, Format);
774                 if (ExifImageWidth < a) ExifImageWidth = a;
775                 break;
776 
777             case TAG_FOCAL_PLANE_XRES:
778                 FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
779                 break;
780 
781             case TAG_FOCAL_PLANE_UNITS:
782                 switch((int)ConvertAnyFormat(ValuePtr, Format)){
783                     case 1: FocalplaneUnits = 25.4; break; // inch
784                     case 2:
785                         // According to the information I was using, 2 means meters.
786                         // But looking at the Cannon powershot's files, inches is the only
787                         // sensible value.
788                         FocalplaneUnits = 25.4;
789                         break;
790 
791                     case 3: FocalplaneUnits = 10;   break;  // centimeter
792                     case 4: FocalplaneUnits = 1;    break;  // millimeter
793                     case 5: FocalplaneUnits = .001; break;  // micrometer
794                 }
795                 break;
796 
797             case TAG_EXPOSURE_BIAS:
798                 ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
799                 break;
800 
801             case TAG_WHITEBALANCE:
802                 ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
803                 break;
804 
805             case TAG_LIGHT_SOURCE:
806                 ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
807                 break;
808 
809             case TAG_METERING_MODE:
810                 ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
811                 break;
812 
813             case TAG_EXPOSURE_PROGRAM:
814                 ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
815                 break;
816 
817             case TAG_EXPOSURE_INDEX:
818                 if (ImageInfo.ISOequivalent == 0){
819                     // Exposure index and ISO equivalent are often used interchangeably,
820                     // so we will do the same in jhead.
821                     // http://photography.about.com/library/glossary/bldef_ei.htm
822                     ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
823                 }
824                 break;
825 
826             case TAG_EXPOSURE_MODE:
827                 ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
828                 break;
829 
830             case TAG_ISO_EQUIVALENT:
831                 ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
832                 if ( ImageInfo.ISOequivalent < 50 ){
833                     // Fixes strange encoding on some older digicams.
834                     ImageInfo.ISOequivalent *= 200;
835                 }
836                 break;
837 
838             case TAG_DIGITALZOOMRATIO:
839                 ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
840                 break;
841 
842             case TAG_THUMBNAIL_OFFSET:
843                 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
844                 DirWithThumbnailPtrs = DirStart;
845                 break;
846 
847             case TAG_THUMBNAIL_LENGTH:
848                 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
849                 ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase;
850                 break;
851 
852             case TAG_EXIF_OFFSET:
853                 if (ShowTags) printf("%s    Exif Dir:",IndentString);
854 
855             case TAG_INTEROP_OFFSET:
856                 if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s    Interop Dir:",IndentString);
857                 {
858                     unsigned char * SubdirStart;
859                     SubdirStart = OffsetBase + Get32u(ValuePtr);
860                     if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
861                         ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
862                     }else{
863                         ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
864                     }
865                     continue;
866                 }
867                 break;
868 
869             case TAG_GPSINFO:
870                 if (ShowTags) printf("%s    GPS info dir:",IndentString);
871                 {
872                     unsigned char * SubdirStart;
873                     SubdirStart = OffsetBase + Get32u(ValuePtr);
874                     if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
875                         ErrNonfatal("Illegal GPS directory link",0,0);
876                     }else{
877                         ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
878                     }
879                     continue;
880                 }
881                 break;
882 
883             case TAG_FOCALLENGTH_35MM:
884                 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
885                 // if its present, use it to compute equivalent focal length instead of
886                 // computing it from sensor geometry and actual focal length.
887                 ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
888                 break;
889 
890             case TAG_DISTANCE_RANGE:
891                 // Three possible standard values:
892                 //   1 = macro, 2 = close, 3 = distant
893                 ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
894                 break;
895         }
896     }
897 
898 
899     {
900         // In addition to linking to subdirectories via exif tags,
901         // there's also a potential link to another directory at the end of each
902         // directory.  this has got to be the result of a committee!
903         unsigned char * SubdirStart;
904         unsigned Offset;
905 
906         if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
907             printf("DirStart %d offset from dirstart %d", (int)DirStart, 2+12*NumDirEntries);
908             Offset = Get32u(DirStart+2+12*NumDirEntries);
909             if (Offset){
910                 SubdirStart = OffsetBase + Offset;
911                 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){
912                     printf("SubdirStart %d OffsetBase %d ExifLength %d Offset %d",
913                         (int)SubdirStart, (int)OffsetBase, ExifLength, Offset);
914                     if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){
915                         // Jhead 1.3 or earlier would crop the whole directory!
916                         // As Jhead produces this form of format incorrectness,
917                         // I'll just let it pass silently
918                         if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n");
919                     }else{
920                         ErrNonfatal("Illegal subdirectory link",0,0);
921                     }
922                 }else{
923                     if (SubdirStart <= OffsetBase+ExifLength){
924                         if (ShowTags) printf("%s    Continued directory ",IndentString);
925                         ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
926                     }
927                 }
928                 if (Offset > ImageInfo.LargestExifOffset){
929                     ImageInfo.LargestExifOffset = Offset;
930                 }
931             }
932         }else{
933             // The exif header ends before the last next directory pointer.
934         }
935     }
936 
937     if (ThumbnailOffset){
938         ImageInfo.ThumbnailAtEnd = FALSE;
939 
940         if (DumpExifMap){
941             printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize);
942         }
943 
944         if (ThumbnailOffset <= ExifLength){
945             if (ThumbnailSize > ExifLength-ThumbnailOffset){
946                 // If thumbnail extends past exif header, only save the part that
947                 // actually exists.  Canon's EOS viewer utility will do this - the
948                 // thumbnail extracts ok with this hack.
949                 ThumbnailSize = ExifLength-ThumbnailOffset;
950                 if (ShowTags) printf("Thumbnail incorrectly placed in header\n");
951 
952             }
953             // The thumbnail pointer appears to be valid.  Store it.
954             ImageInfo.ThumbnailOffset = ThumbnailOffset;
955             ImageInfo.ThumbnailSize = ThumbnailSize;
956 
957             if (ShowTags){
958                 printf("Thumbnail size: %d bytes\n",ThumbnailSize);
959             }
960         }
961     }
962     printf("returning from ProcessExifDir");
963 }
964 
965 
966 //--------------------------------------------------------------------------
967 // Process a EXIF marker
968 // Describes all the drivel that most digital cameras include...
969 //--------------------------------------------------------------------------
process_EXIF(unsigned char * ExifSection,unsigned int length)970 void process_EXIF (unsigned char * ExifSection, unsigned int length)
971 {
972     int FirstOffset;
973 
974     FocalplaneXRes = 0;
975     FocalplaneUnits = 0;
976     ExifImageWidth = 0;
977     NumOrientations = 0;
978 
979     if (ShowTags){
980         printf("Exif header %d bytes long\n",length);
981     }
982 
983     {   // Check the EXIF header component
984         static uchar ExifHeader[] = "Exif\0\0";
985         if (memcmp(ExifSection+2, ExifHeader,6)){
986             ErrNonfatal("Incorrect Exif header",0,0);
987             return;
988         }
989     }
990 
991     if (memcmp(ExifSection+8,"II",2) == 0){
992         if (ShowTags) printf("Exif section in Intel order\n");
993         MotorolaOrder = 0;
994     }else{
995         if (memcmp(ExifSection+8,"MM",2) == 0){
996             if (ShowTags) printf("Exif section in Motorola order\n");
997             MotorolaOrder = 1;
998         }else{
999             ErrNonfatal("Invalid Exif alignment marker.",0,0);
1000             return;
1001         }
1002     }
1003 
1004     // Check the next value for correctness.
1005     if (Get16u(ExifSection+10) != 0x2a){
1006         ErrNonfatal("Invalid Exif start (1)",0,0);
1007         return;
1008     }
1009 
1010     FirstOffset = Get32u(ExifSection+12);
1011     if (FirstOffset < 8 || FirstOffset > 16){
1012         // Usually set to 8, but other values valid too.
1013         ErrNonfatal("Suspicious offset of first IFD value",0,0);
1014         return;
1015     }
1016 
1017     DirWithThumbnailPtrs = NULL;
1018 
1019 
1020     // First directory starts 16 bytes in.  All offset are relative to 8 bytes in.
1021     ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
1022 
1023     ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
1024 #ifdef SUPERDEBUG
1025     printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at"));
1026 #endif
1027     if (DumpExifMap){
1028         unsigned a,b;
1029         printf("Map: %05d- End of exif\n",length-8);
1030 //        for (a=0;a<length-8;a+= 10){
1031 //            printf("Map: %05d ",a);
1032 //            for (b=0;b<10;b++) printf(" %02x",*(ExifSection+8+a+b));
1033 //            printf("\n");
1034 //        }
1035         for (a = 0; a < length - 8; ++a) {
1036             unsigned char c = *(ExifSection+8+a);
1037             unsigned pc = isprint(c) ? c : ' ';
1038             printf("Map: %4d %02x %c", a, c, pc);
1039         }
1040     }
1041 
1042 
1043     // Compute the CCD width, in millimeters.
1044     if (FocalplaneXRes != 0){
1045         // Note: With some cameras, its not possible to compute this correctly because
1046         // they don't adjust the indicated focal plane resolution units when using less
1047         // than maximum resolution, so the CCDWidth value comes out too small.  Nothing
1048         // that Jhad can do about it - its a camera problem.
1049         ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
1050 
1051         if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0
1052             && ImageInfo.FocalLength35mmEquiv == 0){
1053             // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
1054             // already got it explicitly from a tag.
1055             ImageInfo.FocalLength35mmEquiv = (int)(
1056                 (double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom
1057                 / ImageInfo.CCDWidth * 36 + 0.5);
1058         }
1059     }
1060 }
1061 
TagToTagTableEntry(unsigned short tag)1062 static const TagTable_t* TagToTagTableEntry(unsigned short tag)
1063 {
1064     unsigned int i;
1065     for (i = 0; i < TAG_TABLE_SIZE; i++) {
1066         if (TagTable[i].Tag == tag) {
1067             printf("found tag %d", tag);
1068             int format = TagTable[i].Format;
1069             if (format == 0) {
1070                 printf("tag %s format not defined ***** YOU MUST ADD THE FORMAT TO THE TagTable in exif.c!!!!", TagTable[i].Desc);
1071                 return NULL;
1072             }
1073             return &TagTable[i];
1074         }
1075     }
1076     printf("tag %d NOT FOUND", tag);
1077     return NULL;
1078 }
1079 
writeExifTagAndData(int tag,int format,long components,long value,int valueInString,char * Buffer,int * DirIndex,int * DataWriteIndex)1080 static void writeExifTagAndData(int tag,
1081                                 int format,
1082                                 long components,
1083                                 long value,
1084                                 int valueInString,
1085                                 char* Buffer,
1086                                 int* DirIndex,
1087                                 int* DataWriteIndex) {
1088     void* componentsPosition = NULL; // for saving component position
1089 
1090     Put16u(Buffer+ (*DirIndex), tag);                    // Tag
1091     Put16u(Buffer+(*DirIndex) + 2, format);              // Format
1092     if (format == FMT_STRING && components == -1) {
1093         components = strlen((char*)value) + 1;                 // account for null terminator
1094         if (components & 1) ++components;               // no odd lengths
1095     }
1096     if (format == FMT_UNDEFINED && components == -1) {
1097         // check if this UNDEFINED format is actually ASCII (as it usually is)
1098         // if so, we can calculate the size
1099         if(memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0) {
1100             components = sizeof(ExifAsciiPrefix) +
1101                          strlen((char*)value + sizeof(ExifAsciiPrefix)) + 1;
1102             if (components & 1) ++components;               // no odd lengths
1103         }
1104     }
1105     Put32u(Buffer+(*DirIndex) + 4, components);         // Components
1106     componentsPosition = Buffer+(*DirIndex) + 4; // components # can change for lists
1107     printf("# components: %ld", components);
1108     if (format == FMT_STRING) {
1109         // short strings can fit right in the long, otherwise have to
1110         // go in the data area
1111         if (components <= 4) {
1112             strcpy(Buffer+(*DirIndex) + 8, (char*)value);
1113         } else {
1114             Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
1115             printf("copying value %s to %d", (char*)value, (*DataWriteIndex));
1116             strncpy(Buffer+(*DataWriteIndex), (char*)value, components);
1117             (*DataWriteIndex) += components;
1118         }
1119     } else if ((format == FMT_UNDEFINED) &&
1120                (memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0)) {
1121         // short strings can fit right in the long, otherwise have to
1122         // go in the data area
1123         if (components <= 4) {
1124             memcpy(Buffer+(*DirIndex) + 8, (char*)value, components);
1125         } else {
1126             Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
1127             printf("copying %s to %d", (char*)value + sizeof(ExifAsciiPrefix), (*DataWriteIndex));
1128             memcpy(Buffer+(*DataWriteIndex), (char*)value, components);
1129             (*DataWriteIndex) += components;
1130         }
1131     } else if (!valueInString) {
1132         Put32u(Buffer+(*DirIndex) + 8, value);   // Value
1133     } else {
1134         Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
1135         char* curElement = strtok((char*)value, ",");
1136         int i;
1137         for (i = 0; i < components && curElement != NULL; i++) {
1138 #ifdef SUPERDEBUG
1139             printf("processing component %s format %s", curElement, formatStr(format));
1140 #endif
1141             // elements are separated by commas
1142             if (format == FMT_URATIONAL) {
1143                 char* separator = strchr(curElement, '/');
1144                 if (separator) {
1145                     unsigned int numerator = atoi(curElement);
1146                     unsigned int denominator = atoi(separator + 1);
1147                     Put32u(Buffer+(*DataWriteIndex), numerator);
1148                     Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1149                     (*DataWriteIndex) += 8;
1150                 }
1151             } else if (format == FMT_SRATIONAL) {
1152                 char* separator = strchr(curElement, '/');
1153                 if (separator) {
1154                     int numerator = atoi(curElement);
1155                     int denominator = atoi(separator + 1);
1156                     Put32u(Buffer+(*DataWriteIndex), numerator);
1157                     Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1158                     (*DataWriteIndex) += 8;
1159                 }
1160             } else {
1161                 // TODO: doesn't handle multiple components yet -- if more than one, have to put in data write area.
1162                 value = atoi(curElement);
1163                 Put32u(Buffer+(*DirIndex) + 8, value);   // Value
1164             }
1165             curElement = strtok(NULL, ",");
1166         }
1167         if (components == -1) Put32u(componentsPosition, i); // update component # for unknowns
1168     }
1169     (*DirIndex) += 12;
1170 }
1171 
1172 #ifdef SUPERDEBUG
formatStr(int format)1173 char* formatStr(int format) {
1174     switch (format) {
1175         case FMT_BYTE: return "FMT_BYTE"; break;
1176         case FMT_STRING: return "FMT_STRING"; break;
1177         case FMT_USHORT: return "FMT_USHORT"; break;
1178         case FMT_ULONG: return "FMT_ULONG"; break;
1179         case FMT_URATIONAL: return "FMT_URATIONAL"; break;
1180         case FMT_SBYTE: return "FMT_SBYTE"; break;
1181         case FMT_UNDEFINED: return "FMT_UNDEFINED"; break;
1182         case FMT_SSHORT: return "FMT_SSHORT"; break;
1183         case FMT_SLONG: return "FMT_SLONG"; break;
1184         case FMT_SRATIONAL: return "FMT_SRATIONAL"; break;
1185         case FMT_SINGLE: return "FMT_SINGLE"; break;
1186         case FMT_DOUBLE: return "FMT_SINGLE"; break;
1187         default: return "UNKNOWN";
1188     }
1189 }
1190 #endif
1191 
1192 //--------------------------------------------------------------------------
1193 // Create minimal exif header - just date and thumbnail pointers,
1194 // so that date and thumbnail may be filled later.
1195 //--------------------------------------------------------------------------
create_EXIF_internal(ExifElement_t * elements,int exifTagCount,int gpsTagCount,char * Buffer)1196 static void create_EXIF_internal(ExifElement_t* elements, int exifTagCount, int gpsTagCount, char* Buffer)
1197 {
1198     unsigned short NumEntries;
1199     int DataWriteIndex;
1200     int DirIndex;
1201     int DirExifLink = 0;
1202 
1203 #ifdef SUPERDEBUG
1204     LOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
1205 #endif
1206 
1207     MotorolaOrder = 0;
1208 
1209     memcpy(Buffer+2, "Exif\0\0II",8);
1210     Put16u(Buffer+10, 0x2a);
1211 
1212     DataWriteIndex = 16;
1213     Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset.  Means start 16 bytes in.
1214 
1215     {
1216         DirIndex = DataWriteIndex;
1217         NumEntries = 2 + exifTagCount;  // the two extra are the datetime and the thumbnail
1218         if (gpsTagCount) {
1219             ++NumEntries;       // allow for the GPS info tag
1220         }
1221         DataWriteIndex += 2 + NumEntries*12 + 4;
1222 
1223         Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1224         DirIndex += 2;
1225 
1226         // Entries go here...
1227         {
1228             // Date/time entry
1229             char* dateTime = NULL;
1230             char dateBuf[20];
1231             if (ImageInfo.numDateTimeTags){
1232                 // If we had a pre-existing exif header, use time from that.
1233                 dateTime = ImageInfo.DateTime;
1234             } else {
1235                 // Oterwise, use the file's timestamp.
1236                 FileTimeAsString(dateBuf);
1237                 dateTime = dateBuf;
1238             }
1239             writeExifTagAndData(TAG_DATETIME,
1240                                 FMT_STRING,
1241                                 20,
1242                                 (long)(char*)dateBuf,
1243                                 FALSE,
1244                                 Buffer,
1245                                 &DirIndex,
1246                                 &DataWriteIndex);
1247 
1248         }
1249         if (exifTagCount > 0) {
1250             int i;
1251             for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1252                 if (elements[i].GpsTag) {
1253                     continue;
1254                 }
1255                 const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag);
1256                 if (entry == NULL) {
1257                     continue;
1258                 }
1259 #ifdef SUPERDEBUG
1260                 LOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
1261 #endif
1262                 writeExifTagAndData(elements[i].Tag,
1263                                     entry->Format,
1264                                     entry->DataLength,
1265                                     (long)elements[i].Value,
1266                                     TRUE,
1267                                     Buffer,
1268                                     &DirIndex,
1269                                     &DataWriteIndex);
1270             }
1271 
1272             if (gpsTagCount) {
1273                 // Link to gps dir entry
1274                 writeExifTagAndData(TAG_GPSINFO,
1275                                     FMT_ULONG,
1276                                     1,
1277                                     DataWriteIndex-8,
1278                                     FALSE,
1279                                     Buffer,
1280                                     &DirIndex,
1281                                     &DataWriteIndex);
1282             }
1283 
1284             // Link to exif dir entry
1285             int exifDirPtr = DataWriteIndex-8;
1286             if (gpsTagCount) {
1287                 exifDirPtr += 2 + gpsTagCount*12 + 4;
1288             }
1289             DirExifLink = DirIndex;
1290             writeExifTagAndData(TAG_EXIF_OFFSET,
1291                                 FMT_ULONG,
1292                                 1,
1293                                 exifDirPtr,
1294                                 FALSE,
1295                                 Buffer,
1296                                 &DirIndex,
1297                                 &DataWriteIndex);
1298         }
1299 
1300         // End of directory - contains optional link to continued directory.
1301         Put32u(Buffer+DirIndex, 0);
1302         printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1303     }
1304 
1305 
1306     // GPS Section
1307     if (gpsTagCount) {
1308         DirIndex = DataWriteIndex;
1309         printf("Starting GPS section DirIndex = %d", DirIndex);
1310         NumEntries = gpsTagCount;
1311         DataWriteIndex += 2 + NumEntries*12 + 4;
1312 
1313         Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1314         DirIndex += 2;
1315         {
1316             int i;
1317             for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1318                 if (!elements[i].GpsTag) {
1319                     continue;
1320                 }
1321                 const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag);
1322                 if (entry == NULL) {
1323                     continue;
1324                 }
1325 #ifdef SUPERDEBUG
1326                 LOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
1327 #endif
1328                 writeExifTagAndData(elements[i].Tag,
1329                                     entry->Format,
1330                                     entry->DataLength,
1331                                     (long)elements[i].Value,
1332                                     TRUE,
1333                                     Buffer,
1334                                     &DirIndex,
1335                                     &DataWriteIndex);
1336             }
1337         }
1338 
1339         // End of directory - contains optional link to continued directory.
1340         Put32u(Buffer+DirIndex, 0);
1341         printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1342     }
1343 
1344     {
1345         // Overwriting TAG_EXIF_OFFSET which links to this directory
1346         Put32u(Buffer+DirExifLink+8, DataWriteIndex-8);
1347 
1348         printf("Starting Thumbnail section DirIndex = %d", DirIndex);
1349         DirIndex = DataWriteIndex;
1350         NumEntries = 2;
1351         DataWriteIndex += 2 + NumEntries*12 + 4;
1352 
1353         Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1354         DirIndex += 2;
1355         {
1356             // Link to exif dir entry
1357             writeExifTagAndData(TAG_THUMBNAIL_OFFSET,
1358                                 FMT_ULONG,
1359                                 1,
1360                                 DataWriteIndex-8,
1361                                 FALSE,
1362                                 Buffer,
1363                                 &DirIndex,
1364                                 &DataWriteIndex);
1365         }
1366 
1367         {
1368             // Link to exif dir entry
1369             writeExifTagAndData(TAG_THUMBNAIL_LENGTH,
1370                                 FMT_ULONG,
1371                                 1,
1372                                 0,
1373                                 FALSE,
1374                                 Buffer,
1375                                 &DirIndex,
1376                                 &DataWriteIndex);
1377         }
1378 
1379         // End of directory - contains optional link to continued directory.
1380         Put32u(Buffer+DirIndex, 0);
1381         printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1382     }
1383 
1384 
1385     Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
1386     Buffer[1] = (unsigned char)DataWriteIndex;
1387 
1388     // Remove old exif section, if there was one.
1389     RemoveSectionType(M_EXIF);
1390 
1391     {
1392         // Sections need malloced buffers, so do that now, especially because
1393         // we now know how big it needs to be allocated.
1394         unsigned char * NewBuf = malloc(DataWriteIndex);
1395         if (NewBuf == NULL){
1396             ErrFatal("Could not allocate memory");
1397         }
1398         memcpy(NewBuf, Buffer, DataWriteIndex);
1399 
1400         CreateSection(M_EXIF, NewBuf, DataWriteIndex);
1401 
1402         // Re-parse new exif section, now that its in place
1403         // otherwise, we risk touching data that has already been freed.
1404         process_EXIF(NewBuf, DataWriteIndex);
1405     }
1406 }
1407 
create_EXIF(ExifElement_t * elements,int exifTagCount,int gpsTagCount)1408 void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount)
1409 {
1410     // It is hard to calculate exact necessary size for editing the exif
1411     // header dynamically, so we are using the maximum size of EXIF, 64K
1412     const int EXIF_MAX_SIZE = 1024*64;
1413     char* Buffer = malloc(EXIF_MAX_SIZE);
1414 
1415     if (Buffer != NULL) {
1416         create_EXIF_internal(elements, exifTagCount, gpsTagCount, Buffer);
1417         free(Buffer);
1418     } else {
1419         ErrFatal("Could not allocate memory");
1420     }
1421 }
1422 
1423 //--------------------------------------------------------------------------
1424 // Cler the rotation tag in the exif header to 1.
1425 //--------------------------------------------------------------------------
ClearOrientation(void)1426 const char * ClearOrientation(void)
1427 {
1428     int a;
1429     if (NumOrientations == 0) return NULL;
1430 
1431     for (a=0;a<NumOrientations;a++){
1432         switch(OrientationNumFormat[a]){
1433             case FMT_SBYTE:
1434             case FMT_BYTE:
1435                 *(uchar *)(OrientationPtr[a]) = 1;
1436                 break;
1437 
1438             case FMT_USHORT:
1439                 Put16u(OrientationPtr[a], 1);
1440                 break;
1441 
1442             case FMT_ULONG:
1443             case FMT_SLONG:
1444                 memset(OrientationPtr, 0, 4);
1445                 // Can't be bothered to write  generic Put32 if I only use it once.
1446                 if (MotorolaOrder){
1447                     ((uchar *)OrientationPtr[a])[3] = 1;
1448                 }else{
1449                     ((uchar *)OrientationPtr[a])[0] = 1;
1450                 }
1451                 break;
1452 
1453             default:
1454                 return NULL;
1455         }
1456     }
1457 
1458     return OrientTab[ImageInfo.Orientation];
1459 }
1460 
1461 
1462 
1463 //--------------------------------------------------------------------------
1464 // Remove thumbnail out of the exif image.
1465 //--------------------------------------------------------------------------
RemoveThumbnail(unsigned char * ExifSection)1466 int RemoveThumbnail(unsigned char * ExifSection)
1467 {
1468     if (!DirWithThumbnailPtrs ||
1469         ImageInfo.ThumbnailOffset == 0 ||
1470         ImageInfo.ThumbnailSize == 0){
1471         // No thumbnail, or already deleted it.
1472         return 0;
1473     }
1474     if (ImageInfo.ThumbnailAtEnd == FALSE){
1475         ErrNonfatal("Thumbnail is not at end of header, can't chop it off", 0, 0);
1476         return 0;
1477     }
1478 
1479     {
1480         int de;
1481         int NumDirEntries;
1482         NumDirEntries = Get16u(DirWithThumbnailPtrs);
1483 
1484         for (de=0;de<NumDirEntries;de++){
1485             int Tag;
1486             unsigned char * DirEntry;
1487             DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de);
1488             Tag = Get16u(DirEntry);
1489             if (Tag == TAG_THUMBNAIL_LENGTH){
1490                 // Set length to zero.
1491                 if (Get16u(DirEntry+2) != FMT_ULONG){
1492                     // non standard format encoding.  Can't do it.
1493                     ErrNonfatal("Can't remove thumbnail", 0, 0);
1494                     return 0;
1495                 }
1496                 Put32u(DirEntry+8, 0);
1497             }
1498         }
1499     }
1500 
1501     // This is how far the non thumbnail data went.
1502     return ImageInfo.ThumbnailOffset+8;
1503 
1504 }
1505 
1506 
1507 //--------------------------------------------------------------------------
1508 // Convert exif time to Unix time structure
1509 //--------------------------------------------------------------------------
Exif2tm(struct tm * timeptr,char * ExifTime)1510 int Exif2tm(struct tm * timeptr, char * ExifTime)
1511 {
1512     int a;
1513 
1514     timeptr->tm_wday = -1;
1515 
1516     // Check for format: YYYY:MM:DD HH:MM:SS format.
1517     // Date and time normally separated by a space, but also seen a ':' there, so
1518     // skip the middle space with '%*c' so it can be any character.
1519     a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
1520             &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
1521             &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
1522 
1523 
1524     if (a == 6){
1525         timeptr->tm_isdst = -1;
1526         timeptr->tm_mon -= 1;      // Adjust for unix zero-based months
1527         timeptr->tm_year -= 1900;  // Adjust for year starting at 1900
1528         return TRUE; // worked.
1529     }
1530 
1531     return FALSE; // Wasn't in Exif date format.
1532 }
1533 
1534 
1535 //--------------------------------------------------------------------------
1536 // Show the collected image info, displaying camera F-stop and shutter speed
1537 // in a consistent and legible fashion.
1538 //--------------------------------------------------------------------------
ShowImageInfo(int ShowFileInfo)1539 void ShowImageInfo(int ShowFileInfo)
1540 {
1541     if (ShowFileInfo){
1542         printf("File name    : %s\n",ImageInfo.FileName);
1543         printf("File size    : %d bytes\n",ImageInfo.FileSize);
1544 
1545         {
1546             char Temp[20];
1547             FileTimeAsString(Temp);
1548             printf("File date    : %s\n",Temp);
1549         }
1550     }
1551 
1552     if (ImageInfo.CameraMake[0]){
1553         printf("Camera make  : %s\n",ImageInfo.CameraMake);
1554         printf("Camera model : %s\n",ImageInfo.CameraModel);
1555     }
1556     if (ImageInfo.DateTime[0]){
1557         printf("Date/Time    : %s\n",ImageInfo.DateTime);
1558     }
1559     printf("Resolution   : %d x %d\n",ImageInfo.Width, ImageInfo.Height);
1560 
1561     if (ImageInfo.Orientation > 1){
1562         // Only print orientation if one was supplied, and if its not 1 (normal orientation)
1563         printf("Orientation  : %s\n", OrientTab[ImageInfo.Orientation]);
1564     }
1565 
1566     if (ImageInfo.IsColor == 0){
1567         printf("Color/bw     : Black and white\n");
1568     }
1569 
1570     if (ImageInfo.FlashUsed >= 0){
1571         if (ImageInfo.FlashUsed & 1){
1572             printf("Flash used   : Yes");
1573             switch (ImageInfo.FlashUsed){
1574 	            case 0x5: printf(" (Strobe light not detected)"); break;
1575 	            case 0x7: printf(" (Strobe light detected) "); break;
1576 	            case 0x9: printf(" (manual)"); break;
1577 	            case 0xd: printf(" (manual, return light not detected)"); break;
1578 	            case 0xf: printf(" (manual, return light  detected)"); break;
1579 	            case 0x19:printf(" (auto)"); break;
1580 	            case 0x1d:printf(" (auto, return light not detected)"); break;
1581 	            case 0x1f:printf(" (auto, return light detected)"); break;
1582 	            case 0x41:printf(" (red eye reduction mode)"); break;
1583 	            case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
1584 	            case 0x47:printf(" (red eye reduction mode return light  detected)"); break;
1585 	            case 0x49:printf(" (manual, red eye reduction mode)"); break;
1586 	            case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
1587 	            case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
1588 	            case 0x59:printf(" (auto, red eye reduction mode)"); break;
1589 	            case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
1590 	            case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
1591             }
1592         }else{
1593             printf("Flash used   : No");
1594             switch (ImageInfo.FlashUsed){
1595 	            case 0x18:printf(" (auto)"); break;
1596             }
1597         }
1598         printf("\n");
1599     }
1600 
1601 
1602     if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
1603         printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom);
1604         if (ImageInfo.FocalLength35mmEquiv){
1605             printf("  (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
1606         }
1607         printf("\n");
1608     }
1609 
1610     if (ImageInfo.DigitalZoomRatio > 1){
1611         // Digital zoom used.  Shame on you!
1612         printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio);
1613     }
1614 
1615     if (ImageInfo.CCDWidth){
1616         printf("CCD width    : %4.2fmm\n",(double)ImageInfo.CCDWidth);
1617     }
1618 
1619     if (ImageInfo.ExposureTime){
1620         if (ImageInfo.ExposureTime < 0.010){
1621             printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
1622         }else{
1623             printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
1624         }
1625         if (ImageInfo.ExposureTime <= 0.5){
1626             printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1627         }
1628         printf("\n");
1629     }
1630     if (ImageInfo.ApertureFNumber){
1631         printf("Aperture     : f/%3.1f\n",(double)ImageInfo.ApertureFNumber);
1632     }
1633     if (ImageInfo.Distance){
1634         if (ImageInfo.Distance < 0){
1635             printf("Focus dist.  : Infinite\n");
1636         }else{
1637             printf("Focus dist.  : %4.2fm\n",(double)ImageInfo.Distance);
1638         }
1639     }
1640 
1641     if (ImageInfo.ISOequivalent){
1642         printf("ISO equiv.   : %2d\n",(int)ImageInfo.ISOequivalent);
1643     }
1644 
1645     if (ImageInfo.ExposureBias){
1646         // If exposure bias was specified, but set to zero, presumably its no bias at all,
1647         // so only show it if its nonzero.
1648         printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
1649     }
1650 
1651     switch(ImageInfo.Whitebalance) {
1652         case 1:
1653             printf("Whitebalance : Manual\n");
1654             break;
1655         case 0:
1656             printf("Whitebalance : Auto\n");
1657             break;
1658     }
1659 
1660     //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
1661     switch(ImageInfo.LightSource) {
1662         case 1:
1663             printf("Light Source : Daylight\n");
1664             break;
1665         case 2:
1666             printf("Light Source : Fluorescent\n");
1667             break;
1668         case 3:
1669             printf("Light Source : Incandescent\n");
1670             break;
1671         case 4:
1672             printf("Light Source : Flash\n");
1673             break;
1674         case 9:
1675             printf("Light Source : Fine weather\n");
1676             break;
1677         case 11:
1678             printf("Light Source : Shade\n");
1679             break;
1680         default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
1681             // If it just says 'unknown' or we don't know it, then
1682             // don't bother showing it - it doesn't add any useful information.
1683     }
1684 
1685     if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs
1686         switch(ImageInfo.MeteringMode) {
1687         case 2:
1688             printf("Metering Mode: center weight\n");
1689             break;
1690         case 3:
1691             printf("Metering Mode: spot\n");
1692             break;
1693         case 5:
1694             printf("Metering Mode: matrix\n");
1695             break;
1696         }
1697     }
1698 
1699     if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
1700         switch(ImageInfo.ExposureProgram) {
1701         case 1:
1702             printf("Exposure     : Manual\n");
1703             break;
1704         case 2:
1705             printf("Exposure     : program (auto)\n");
1706             break;
1707         case 3:
1708             printf("Exposure     : aperture priority (semi-auto)\n");
1709             break;
1710         case 4:
1711             printf("Exposure     : shutter priority (semi-auto)\n");
1712             break;
1713         case 5:
1714             printf("Exposure     : Creative Program (based towards depth of field)\n");
1715             break;
1716         case 6:
1717             printf("Exposure     : Action program (based towards fast shutter speed)\n");
1718             break;
1719         case 7:
1720             printf("Exposure     : Portrait Mode\n");
1721             break;
1722         case 8:
1723             printf("Exposure     : LandscapeMode \n");
1724             break;
1725         default:
1726             break;
1727         }
1728     }
1729     switch(ImageInfo.ExposureMode){
1730         case 0: // Automatic (not worth cluttering up output for)
1731             break;
1732         case 1: printf("Exposure Mode: Manual\n");
1733             break;
1734         case 2: printf("Exposure Mode: Auto bracketing\n");
1735             break;
1736     }
1737 
1738     if (ImageInfo.DistanceRange) {
1739         printf("Focus range  : ");
1740         switch(ImageInfo.DistanceRange) {
1741             case 1:
1742                 printf("macro");
1743                 break;
1744             case 2:
1745                 printf("close");
1746                 break;
1747             case 3:
1748                 printf("distant");
1749                 break;
1750         }
1751         printf("\n");
1752     }
1753 
1754 
1755 
1756     if (ImageInfo.Process != M_SOF0){
1757         // don't show it if its the plain old boring 'baseline' process, but do
1758         // show it if its something else, like 'progressive' (used on web sometimes)
1759         int a;
1760         for (a=0;;a++){
1761             if (a >= (int)PROCESS_TABLE_SIZE){
1762                 // ran off the end of the table.
1763                 printf("Jpeg process : Unknown\n");
1764                 break;
1765             }
1766             if (ProcessTable[a].Tag == ImageInfo.Process){
1767                 printf("Jpeg process : %s\n",ProcessTable[a].Desc);
1768                 break;
1769             }
1770         }
1771     }
1772 
1773     if (ImageInfo.GpsInfoPresent){
1774         printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
1775         printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
1776         if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
1777     }
1778 
1779     // Print the comment. Print 'Comment:' for each new line of comment.
1780     if (ImageInfo.Comments[0]){
1781         int a,c;
1782         printf("Comment      : ");
1783         if (!ImageInfo.CommentWidchars){
1784             for (a=0;a<MAX_COMMENT_SIZE;a++){
1785                 c = ImageInfo.Comments[a];
1786                 if (c == '\0') break;
1787                 if (c == '\n'){
1788                     // Do not start a new line if the string ends with a carriage return.
1789                     if (ImageInfo.Comments[a+1] != '\0'){
1790                         printf("\nComment      : ");
1791                     }else{
1792                         printf("\n");
1793                     }
1794                 }else{
1795                     putchar(c);
1796                 }
1797             }
1798             printf("\n");
1799         }else{
1800             printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
1801         }
1802     }
1803     if (ImageInfo.ThumbnailOffset){
1804         printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
1805     } else {
1806         printf("NO thumbnail");
1807     }
1808 }
1809 
1810 
1811 //--------------------------------------------------------------------------
1812 // Summarize highlights of image info on one line (suitable for grep-ing)
1813 //--------------------------------------------------------------------------
ShowConciseImageInfo(void)1814 void ShowConciseImageInfo(void)
1815 {
1816     printf("\"%s\"",ImageInfo.FileName);
1817 
1818     printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);
1819 
1820     if (ImageInfo.ExposureTime){
1821         if (ImageInfo.ExposureTime <= 0.5){
1822             printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1823         }else{
1824             printf(" (%1.1f)",ImageInfo.ExposureTime);
1825         }
1826     }
1827 
1828     if (ImageInfo.ApertureFNumber){
1829         printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
1830     }
1831 
1832     if (ImageInfo.FocalLength35mmEquiv){
1833         printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
1834     }
1835 
1836     if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
1837         printf(" (flash)");
1838     }
1839 
1840     if (ImageInfo.IsColor == 0){
1841         printf(" (bw)");
1842     }
1843 
1844     printf("\n");
1845 }
1846