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