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_USHORT, 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 Put16u(Buffer+ (*DirIndex), tag); // Tag
1089 Put16u(Buffer+(*DirIndex) + 2, format); // Format
1090 if (format == FMT_STRING && components == -1) {
1091 components = strlen((char*)value) + 1; // account for null terminator
1092 if (components & 1) ++components; // no odd lengths
1093 }
1094 Put32u(Buffer+(*DirIndex) + 4, components); // Components
1095 printf("# components: %ld", components);
1096 if (format == FMT_STRING) {
1097 // short strings can fit right in the long, otherwise have to
1098 // go in the data area
1099 if (components <= 4) {
1100 strcpy(Buffer+(*DirIndex) + 8, (char*)value);
1101 } else {
1102 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
1103 printf("copying value %s to %d", (char*)value, (*DataWriteIndex));
1104 strncpy(Buffer+(*DataWriteIndex), (char*)value, components);
1105 (*DataWriteIndex) += components;
1106 }
1107 } else if (!valueInString) {
1108 Put32u(Buffer+(*DirIndex) + 8, value); // Value
1109 } else {
1110 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer
1111 char* curElement = strtok((char*)value, ",");
1112 int i;
1113 for (i = 0; i < components && curElement != NULL; i++) {
1114 #ifdef SUPERDEBUG
1115 printf("processing component %s format %s", curElement, formatStr(format));
1116 #endif
1117 // elements are separated by commas
1118 if (format == FMT_URATIONAL) {
1119 char* separator = strchr(curElement, '/');
1120 if (separator) {
1121 unsigned int numerator = atoi(curElement);
1122 unsigned int denominator = atoi(separator + 1);
1123 Put32u(Buffer+(*DataWriteIndex), numerator);
1124 Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1125 (*DataWriteIndex) += 8;
1126 }
1127 } else if (format == FMT_SRATIONAL) {
1128 char* separator = strchr(curElement, '/');
1129 if (separator) {
1130 int numerator = atoi(curElement);
1131 int denominator = atoi(separator + 1);
1132 Put32u(Buffer+(*DataWriteIndex), numerator);
1133 Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
1134 (*DataWriteIndex) += 8;
1135 }
1136 } else {
1137 // TODO: doesn't handle multiple components yet -- if more than one, have to put in data write area.
1138 value = atoi(curElement);
1139 Put32u(Buffer+(*DirIndex) + 8, value); // Value
1140 }
1141 curElement = strtok(NULL, ",");
1142 }
1143 }
1144 (*DirIndex) += 12;
1145 }
1146
1147 #ifdef SUPERDEBUG
formatStr(int format)1148 char* formatStr(int format) {
1149 switch (format) {
1150 case FMT_BYTE: return "FMT_BYTE"; break;
1151 case FMT_STRING: return "FMT_STRING"; break;
1152 case FMT_USHORT: return "FMT_USHORT"; break;
1153 case FMT_ULONG: return "FMT_ULONG"; break;
1154 case FMT_URATIONAL: return "FMT_URATIONAL"; break;
1155 case FMT_SBYTE: return "FMT_SBYTE"; break;
1156 case FMT_UNDEFINED: return "FMT_UNDEFINED"; break;
1157 case FMT_SSHORT: return "FMT_SSHORT"; break;
1158 case FMT_SLONG: return "FMT_SLONG"; break;
1159 case FMT_SRATIONAL: return "FMT_SRATIONAL"; break;
1160 case FMT_SINGLE: return "FMT_SINGLE"; break;
1161 case FMT_DOUBLE: return "FMT_SINGLE"; break;
1162 default: return "UNKNOWN";
1163 }
1164 }
1165 #endif
1166
1167 //--------------------------------------------------------------------------
1168 // Create minimal exif header - just date and thumbnail pointers,
1169 // so that date and thumbnail may be filled later.
1170 //--------------------------------------------------------------------------
create_EXIF(ExifElement_t * elements,int exifTagCount,int gpsTagCount)1171 void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount)
1172 {
1173 // TODO: We need to dynamically allocate this buffer and resize it when
1174 // necessary while writing so we don't do a buffer overflow.
1175 char Buffer[1024];
1176
1177 unsigned short NumEntries;
1178 int DataWriteIndex;
1179 int DirIndex;
1180 int DirContinuation = 0;
1181
1182 #ifdef SUPERDEBUG
1183 LOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
1184 #endif
1185
1186 MotorolaOrder = 0;
1187
1188 memcpy(Buffer+2, "Exif\0\0II",8);
1189 Put16u(Buffer+10, 0x2a);
1190
1191 DataWriteIndex = 16;
1192 Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset. Means start 16 bytes in.
1193
1194 {
1195 DirIndex = DataWriteIndex;
1196 NumEntries = 2 + exifTagCount; // the two extra are the datetime and the thumbnail
1197 if (gpsTagCount) {
1198 ++NumEntries; // allow for the GPS info tag
1199 }
1200 DataWriteIndex += 2 + NumEntries*12 + 4;
1201
1202 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1203 DirIndex += 2;
1204
1205 // Entries go here...
1206 {
1207 // Date/time entry
1208 char* dateTime = NULL;
1209 char dateBuf[20];
1210 if (ImageInfo.numDateTimeTags){
1211 // If we had a pre-existing exif header, use time from that.
1212 dateTime = ImageInfo.DateTime;
1213 } else {
1214 // Oterwise, use the file's timestamp.
1215 FileTimeAsString(dateBuf);
1216 dateTime = dateBuf;
1217 }
1218 writeExifTagAndData(TAG_DATETIME,
1219 FMT_STRING,
1220 20,
1221 (long)(char*)dateBuf,
1222 FALSE,
1223 Buffer,
1224 &DirIndex,
1225 &DataWriteIndex);
1226
1227 }
1228 if (exifTagCount > 0) {
1229 int i;
1230 for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1231 if (elements[i].GpsTag) {
1232 continue;
1233 }
1234 const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag);
1235 if (entry == NULL) {
1236 continue;
1237 }
1238 #ifdef SUPERDEBUG
1239 LOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
1240 #endif
1241 writeExifTagAndData(elements[i].Tag,
1242 entry->Format,
1243 entry->DataLength,
1244 (long)elements[i].Value,
1245 TRUE,
1246 Buffer,
1247 &DirIndex,
1248 &DataWriteIndex);
1249 }
1250
1251 if (gpsTagCount) {
1252 // Link to gps dir entry
1253 writeExifTagAndData(TAG_GPSINFO,
1254 FMT_ULONG,
1255 1,
1256 DataWriteIndex-8,
1257 FALSE,
1258 Buffer,
1259 &DirIndex,
1260 &DataWriteIndex);
1261 }
1262
1263 // Link to exif dir entry
1264 int exifDirPtr = DataWriteIndex-8;
1265 if (gpsTagCount) {
1266 exifDirPtr += 2 + gpsTagCount*12 + 4;
1267 }
1268 DirContinuation = DirIndex;
1269 writeExifTagAndData(TAG_EXIF_OFFSET,
1270 FMT_ULONG,
1271 1,
1272 exifDirPtr,
1273 FALSE,
1274 Buffer,
1275 &DirIndex,
1276 &DataWriteIndex);
1277 }
1278
1279 // End of directory - contains optional link to continued directory.
1280 DirContinuation = DirIndex;
1281 printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1282 }
1283
1284
1285 // GPS Section
1286 if (gpsTagCount) {
1287 DirIndex = DataWriteIndex;
1288 printf("Starting GPS section DirIndex = %d", DirIndex);
1289 NumEntries = gpsTagCount;
1290 DataWriteIndex += 2 + NumEntries*12 + 4;
1291
1292 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1293 DirIndex += 2;
1294 {
1295 int i;
1296 for (i = 0; i < exifTagCount + gpsTagCount; i++) {
1297 if (!elements[i].GpsTag) {
1298 continue;
1299 }
1300 const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag);
1301 if (entry == NULL) {
1302 continue;
1303 }
1304 #ifdef SUPERDEBUG
1305 LOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
1306 #endif
1307 writeExifTagAndData(elements[i].Tag,
1308 entry->Format,
1309 entry->DataLength,
1310 (long)elements[i].Value,
1311 TRUE,
1312 Buffer,
1313 &DirIndex,
1314 &DataWriteIndex);
1315 }
1316 }
1317
1318 // End of directory - contains optional link to continued directory.
1319 Put32u(Buffer+DirIndex, 0);
1320 printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1321 }
1322
1323 {
1324 //Continuation which links to this directory;
1325 Put32u(Buffer+DirContinuation, DataWriteIndex-8);
1326
1327 printf("Starting Thumbnail section DirIndex = %d", DirIndex);
1328 DirIndex = DataWriteIndex;
1329 NumEntries = 2;
1330 DataWriteIndex += 2 + NumEntries*12 + 4;
1331
1332 Put16u(Buffer+DirIndex, NumEntries); // Number of entries
1333 DirIndex += 2;
1334 {
1335 // Link to exif dir entry
1336 writeExifTagAndData(TAG_THUMBNAIL_OFFSET,
1337 FMT_ULONG,
1338 1,
1339 DataWriteIndex-8,
1340 FALSE,
1341 Buffer,
1342 &DirIndex,
1343 &DataWriteIndex);
1344 }
1345
1346 {
1347 // Link to exif dir entry
1348 writeExifTagAndData(TAG_THUMBNAIL_LENGTH,
1349 FMT_ULONG,
1350 1,
1351 0,
1352 FALSE,
1353 Buffer,
1354 &DirIndex,
1355 &DataWriteIndex);
1356 }
1357
1358 // End of directory - contains optional link to continued directory.
1359 Put32u(Buffer+DirIndex, 0);
1360 printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
1361 }
1362
1363
1364 Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
1365 Buffer[1] = (unsigned char)DataWriteIndex;
1366
1367 // Remove old exif section, if there was one.
1368 RemoveSectionType(M_EXIF);
1369
1370 {
1371 // Sections need malloced buffers, so do that now, especially because
1372 // we now know how big it needs to be allocated.
1373 unsigned char * NewBuf = malloc(DataWriteIndex);
1374 if (NewBuf == NULL){
1375 ErrFatal("Could not allocate memory");
1376 }
1377 memcpy(NewBuf, Buffer, DataWriteIndex);
1378
1379 CreateSection(M_EXIF, NewBuf, DataWriteIndex);
1380
1381 // Re-parse new exif section, now that its in place
1382 // otherwise, we risk touching data that has already been freed.
1383 process_EXIF(NewBuf, DataWriteIndex);
1384 }
1385 }
1386
1387 //--------------------------------------------------------------------------
1388 // Cler the rotation tag in the exif header to 1.
1389 //--------------------------------------------------------------------------
ClearOrientation(void)1390 const char * ClearOrientation(void)
1391 {
1392 int a;
1393 if (NumOrientations == 0) return NULL;
1394
1395 for (a=0;a<NumOrientations;a++){
1396 switch(OrientationNumFormat[a]){
1397 case FMT_SBYTE:
1398 case FMT_BYTE:
1399 *(uchar *)(OrientationPtr[a]) = 1;
1400 break;
1401
1402 case FMT_USHORT:
1403 Put16u(OrientationPtr[a], 1);
1404 break;
1405
1406 case FMT_ULONG:
1407 case FMT_SLONG:
1408 memset(OrientationPtr, 0, 4);
1409 // Can't be bothered to write generic Put32 if I only use it once.
1410 if (MotorolaOrder){
1411 ((uchar *)OrientationPtr[a])[3] = 1;
1412 }else{
1413 ((uchar *)OrientationPtr[a])[0] = 1;
1414 }
1415 break;
1416
1417 default:
1418 return NULL;
1419 }
1420 }
1421
1422 return OrientTab[ImageInfo.Orientation];
1423 }
1424
1425
1426
1427 //--------------------------------------------------------------------------
1428 // Remove thumbnail out of the exif image.
1429 //--------------------------------------------------------------------------
RemoveThumbnail(unsigned char * ExifSection)1430 int RemoveThumbnail(unsigned char * ExifSection)
1431 {
1432 if (!DirWithThumbnailPtrs ||
1433 ImageInfo.ThumbnailOffset == 0 ||
1434 ImageInfo.ThumbnailSize == 0){
1435 // No thumbnail, or already deleted it.
1436 return 0;
1437 }
1438 if (ImageInfo.ThumbnailAtEnd == FALSE){
1439 ErrNonfatal("Thumbnail is not at end of header, can't chop it off", 0, 0);
1440 return 0;
1441 }
1442
1443 {
1444 int de;
1445 int NumDirEntries;
1446 NumDirEntries = Get16u(DirWithThumbnailPtrs);
1447
1448 for (de=0;de<NumDirEntries;de++){
1449 int Tag;
1450 unsigned char * DirEntry;
1451 DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de);
1452 Tag = Get16u(DirEntry);
1453 if (Tag == TAG_THUMBNAIL_LENGTH){
1454 // Set length to zero.
1455 if (Get16u(DirEntry+2) != FMT_ULONG){
1456 // non standard format encoding. Can't do it.
1457 ErrNonfatal("Can't remove thumbnail", 0, 0);
1458 return 0;
1459 }
1460 Put32u(DirEntry+8, 0);
1461 }
1462 }
1463 }
1464
1465 // This is how far the non thumbnail data went.
1466 return ImageInfo.ThumbnailOffset+8;
1467
1468 }
1469
1470
1471 //--------------------------------------------------------------------------
1472 // Convert exif time to Unix time structure
1473 //--------------------------------------------------------------------------
Exif2tm(struct tm * timeptr,char * ExifTime)1474 int Exif2tm(struct tm * timeptr, char * ExifTime)
1475 {
1476 int a;
1477
1478 timeptr->tm_wday = -1;
1479
1480 // Check for format: YYYY:MM:DD HH:MM:SS format.
1481 // Date and time normally separated by a space, but also seen a ':' there, so
1482 // skip the middle space with '%*c' so it can be any character.
1483 a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
1484 &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
1485 &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
1486
1487
1488 if (a == 6){
1489 timeptr->tm_isdst = -1;
1490 timeptr->tm_mon -= 1; // Adjust for unix zero-based months
1491 timeptr->tm_year -= 1900; // Adjust for year starting at 1900
1492 return TRUE; // worked.
1493 }
1494
1495 return FALSE; // Wasn't in Exif date format.
1496 }
1497
1498
1499 //--------------------------------------------------------------------------
1500 // Show the collected image info, displaying camera F-stop and shutter speed
1501 // in a consistent and legible fashion.
1502 //--------------------------------------------------------------------------
ShowImageInfo(int ShowFileInfo)1503 void ShowImageInfo(int ShowFileInfo)
1504 {
1505 if (ShowFileInfo){
1506 printf("File name : %s\n",ImageInfo.FileName);
1507 printf("File size : %d bytes\n",ImageInfo.FileSize);
1508
1509 {
1510 char Temp[20];
1511 FileTimeAsString(Temp);
1512 printf("File date : %s\n",Temp);
1513 }
1514 }
1515
1516 if (ImageInfo.CameraMake[0]){
1517 printf("Camera make : %s\n",ImageInfo.CameraMake);
1518 printf("Camera model : %s\n",ImageInfo.CameraModel);
1519 }
1520 if (ImageInfo.DateTime[0]){
1521 printf("Date/Time : %s\n",ImageInfo.DateTime);
1522 }
1523 printf("Resolution : %d x %d\n",ImageInfo.Width, ImageInfo.Height);
1524
1525 if (ImageInfo.Orientation > 1){
1526 // Only print orientation if one was supplied, and if its not 1 (normal orientation)
1527 printf("Orientation : %s\n", OrientTab[ImageInfo.Orientation]);
1528 }
1529
1530 if (ImageInfo.IsColor == 0){
1531 printf("Color/bw : Black and white\n");
1532 }
1533
1534 if (ImageInfo.FlashUsed >= 0){
1535 if (ImageInfo.FlashUsed & 1){
1536 printf("Flash used : Yes");
1537 switch (ImageInfo.FlashUsed){
1538 case 0x5: printf(" (Strobe light not detected)"); break;
1539 case 0x7: printf(" (Strobe light detected) "); break;
1540 case 0x9: printf(" (manual)"); break;
1541 case 0xd: printf(" (manual, return light not detected)"); break;
1542 case 0xf: printf(" (manual, return light detected)"); break;
1543 case 0x19:printf(" (auto)"); break;
1544 case 0x1d:printf(" (auto, return light not detected)"); break;
1545 case 0x1f:printf(" (auto, return light detected)"); break;
1546 case 0x41:printf(" (red eye reduction mode)"); break;
1547 case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
1548 case 0x47:printf(" (red eye reduction mode return light detected)"); break;
1549 case 0x49:printf(" (manual, red eye reduction mode)"); break;
1550 case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
1551 case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
1552 case 0x59:printf(" (auto, red eye reduction mode)"); break;
1553 case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
1554 case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
1555 }
1556 }else{
1557 printf("Flash used : No");
1558 switch (ImageInfo.FlashUsed){
1559 case 0x18:printf(" (auto)"); break;
1560 }
1561 }
1562 printf("\n");
1563 }
1564
1565
1566 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
1567 printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom);
1568 if (ImageInfo.FocalLength35mmEquiv){
1569 printf(" (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
1570 }
1571 printf("\n");
1572 }
1573
1574 if (ImageInfo.DigitalZoomRatio > 1){
1575 // Digital zoom used. Shame on you!
1576 printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio);
1577 }
1578
1579 if (ImageInfo.CCDWidth){
1580 printf("CCD width : %4.2fmm\n",(double)ImageInfo.CCDWidth);
1581 }
1582
1583 if (ImageInfo.ExposureTime){
1584 if (ImageInfo.ExposureTime < 0.010){
1585 printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
1586 }else{
1587 printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
1588 }
1589 if (ImageInfo.ExposureTime <= 0.5){
1590 printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1591 }
1592 printf("\n");
1593 }
1594 if (ImageInfo.ApertureFNumber){
1595 printf("Aperture : f/%3.1f\n",(double)ImageInfo.ApertureFNumber);
1596 }
1597 if (ImageInfo.Distance){
1598 if (ImageInfo.Distance < 0){
1599 printf("Focus dist. : Infinite\n");
1600 }else{
1601 printf("Focus dist. : %4.2fm\n",(double)ImageInfo.Distance);
1602 }
1603 }
1604
1605 if (ImageInfo.ISOequivalent){
1606 printf("ISO equiv. : %2d\n",(int)ImageInfo.ISOequivalent);
1607 }
1608
1609 if (ImageInfo.ExposureBias){
1610 // If exposure bias was specified, but set to zero, presumably its no bias at all,
1611 // so only show it if its nonzero.
1612 printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
1613 }
1614
1615 switch(ImageInfo.Whitebalance) {
1616 case 1:
1617 printf("Whitebalance : Manual\n");
1618 break;
1619 case 0:
1620 printf("Whitebalance : Auto\n");
1621 break;
1622 }
1623
1624 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
1625 switch(ImageInfo.LightSource) {
1626 case 1:
1627 printf("Light Source : Daylight\n");
1628 break;
1629 case 2:
1630 printf("Light Source : Fluorescent\n");
1631 break;
1632 case 3:
1633 printf("Light Source : Incandescent\n");
1634 break;
1635 case 4:
1636 printf("Light Source : Flash\n");
1637 break;
1638 case 9:
1639 printf("Light Source : Fine weather\n");
1640 break;
1641 case 11:
1642 printf("Light Source : Shade\n");
1643 break;
1644 default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
1645 // If it just says 'unknown' or we don't know it, then
1646 // don't bother showing it - it doesn't add any useful information.
1647 }
1648
1649 if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs
1650 switch(ImageInfo.MeteringMode) {
1651 case 2:
1652 printf("Metering Mode: center weight\n");
1653 break;
1654 case 3:
1655 printf("Metering Mode: spot\n");
1656 break;
1657 case 5:
1658 printf("Metering Mode: matrix\n");
1659 break;
1660 }
1661 }
1662
1663 if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
1664 switch(ImageInfo.ExposureProgram) {
1665 case 1:
1666 printf("Exposure : Manual\n");
1667 break;
1668 case 2:
1669 printf("Exposure : program (auto)\n");
1670 break;
1671 case 3:
1672 printf("Exposure : aperture priority (semi-auto)\n");
1673 break;
1674 case 4:
1675 printf("Exposure : shutter priority (semi-auto)\n");
1676 break;
1677 case 5:
1678 printf("Exposure : Creative Program (based towards depth of field)\n");
1679 break;
1680 case 6:
1681 printf("Exposure : Action program (based towards fast shutter speed)\n");
1682 break;
1683 case 7:
1684 printf("Exposure : Portrait Mode\n");
1685 break;
1686 case 8:
1687 printf("Exposure : LandscapeMode \n");
1688 break;
1689 default:
1690 break;
1691 }
1692 }
1693 switch(ImageInfo.ExposureMode){
1694 case 0: // Automatic (not worth cluttering up output for)
1695 break;
1696 case 1: printf("Exposure Mode: Manual\n");
1697 break;
1698 case 2: printf("Exposure Mode: Auto bracketing\n");
1699 break;
1700 }
1701
1702 if (ImageInfo.DistanceRange) {
1703 printf("Focus range : ");
1704 switch(ImageInfo.DistanceRange) {
1705 case 1:
1706 printf("macro");
1707 break;
1708 case 2:
1709 printf("close");
1710 break;
1711 case 3:
1712 printf("distant");
1713 break;
1714 }
1715 printf("\n");
1716 }
1717
1718
1719
1720 if (ImageInfo.Process != M_SOF0){
1721 // don't show it if its the plain old boring 'baseline' process, but do
1722 // show it if its something else, like 'progressive' (used on web sometimes)
1723 int a;
1724 for (a=0;;a++){
1725 if (a >= (int)PROCESS_TABLE_SIZE){
1726 // ran off the end of the table.
1727 printf("Jpeg process : Unknown\n");
1728 break;
1729 }
1730 if (ProcessTable[a].Tag == ImageInfo.Process){
1731 printf("Jpeg process : %s\n",ProcessTable[a].Desc);
1732 break;
1733 }
1734 }
1735 }
1736
1737 if (ImageInfo.GpsInfoPresent){
1738 printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
1739 printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
1740 if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
1741 }
1742
1743 // Print the comment. Print 'Comment:' for each new line of comment.
1744 if (ImageInfo.Comments[0]){
1745 int a,c;
1746 printf("Comment : ");
1747 if (!ImageInfo.CommentWidchars){
1748 for (a=0;a<MAX_COMMENT_SIZE;a++){
1749 c = ImageInfo.Comments[a];
1750 if (c == '\0') break;
1751 if (c == '\n'){
1752 // Do not start a new line if the string ends with a carriage return.
1753 if (ImageInfo.Comments[a+1] != '\0'){
1754 printf("\nComment : ");
1755 }else{
1756 printf("\n");
1757 }
1758 }else{
1759 putchar(c);
1760 }
1761 }
1762 printf("\n");
1763 }else{
1764 printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
1765 }
1766 }
1767 if (ImageInfo.ThumbnailOffset){
1768 printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
1769 } else {
1770 printf("NO thumbnail");
1771 }
1772 }
1773
1774
1775 //--------------------------------------------------------------------------
1776 // Summarize highlights of image info on one line (suitable for grep-ing)
1777 //--------------------------------------------------------------------------
ShowConciseImageInfo(void)1778 void ShowConciseImageInfo(void)
1779 {
1780 printf("\"%s\"",ImageInfo.FileName);
1781
1782 printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);
1783
1784 if (ImageInfo.ExposureTime){
1785 if (ImageInfo.ExposureTime <= 0.5){
1786 printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
1787 }else{
1788 printf(" (%1.1f)",ImageInfo.ExposureTime);
1789 }
1790 }
1791
1792 if (ImageInfo.ApertureFNumber){
1793 printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
1794 }
1795
1796 if (ImageInfo.FocalLength35mmEquiv){
1797 printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
1798 }
1799
1800 if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
1801 printf(" (flash)");
1802 }
1803
1804 if (ImageInfo.IsColor == 0){
1805 printf(" (bw)");
1806 }
1807
1808 printf("\n");
1809 }
1810