rjhead 0.2.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }