rjhead 0.2.88

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