2 * Copyright (C) 2005-2007 Team XboxMediaCenter
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 //--------------------------------------------------------------------------
23 // Program to pull the EXIF information out of various types of digital
24 // images and present it in a reasonably consistent way
26 // Original code pulled from 'jhead' by Matthias Wandel (http://www.sentex.net/~mwandel/) - jhead
27 // Adapted for XBMC by DD.
28 //--------------------------------------------------------------------------
30 // Note: Jhead supports TAG_MAKER_NOTE exif field,
31 // but that is ommited for now - to make porting easier and addition smaller
37 #define min(a,b) (a)>(b)?(b):(a)
38 #define max(a,b) (a)<(b)?(b):(a)
42 #include "ExifParse.h"
45 // Prototypes for exif utility functions.
46 static void ErrNonfatal(const char* const msg, int a1, int a2);
48 #define DIR_ENTRY_ADDR(Start, Entry) ((Start)+2+12*(Entry))
51 //--------------------------------------------------------------------------
52 // Describes tag values
53 #define TAG_DESCRIPTION 0x010E
54 #define TAG_MAKE 0x010F
55 #define TAG_MODEL 0x0110
56 #define TAG_ORIENTATION 0x0112
57 #define TAG_X_RESOLUTION 0x011A // Not processed. Format rational64u (see http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.76/html/TagNames/EXIF.html)
58 #define TAG_Y_RESOLUTION 0x011B // Not processed. Format rational64u
59 #define TAG_RESOLUTION_UNIT 0x0128 // Not processed. Format int16u. Values: 1-none; 2-inches; 3-cm
60 #define TAG_SOFTWARE 0x0131
61 #define TAG_DATETIME 0x0132
62 #define TAG_THUMBNAIL_OFFSET 0x0201
63 #define TAG_THUMBNAIL_LENGTH 0x0202
64 #define TAG_Y_CB_CR_POS 0x0213 // Not processed. Format int16u. Values: 1-Centered; 2-Co-sited
65 #define TAG_EXPOSURETIME 0x829A
66 #define TAG_FNUMBER 0x829D
67 #define TAG_EXIF_OFFSET 0x8769
68 #define TAG_EXPOSURE_PROGRAM 0x8822
69 #define TAG_GPSINFO 0x8825
70 #define TAG_ISO_EQUIVALENT 0x8827
71 #define TAG_EXIF_VERSION 0x9000 // Not processed.
72 #define TAG_COMPONENT_CFG 0x9101 // Not processed.
73 #define TAG_DATETIME_ORIGINAL 0x9003
74 #define TAG_DATETIME_DIGITIZED 0x9004
75 #define TAG_SHUTTERSPEED 0x9201
76 #define TAG_APERTURE 0x9202
77 #define TAG_EXPOSURE_BIAS 0x9204
78 #define TAG_MAXAPERTURE 0x9205
79 #define TAG_SUBJECT_DISTANCE 0x9206
80 #define TAG_METERING_MODE 0x9207
81 #define TAG_LIGHT_SOURCE 0x9208
82 #define TAG_FLASH 0x9209
83 #define TAG_FOCALLENGTH 0x920A
84 #define TAG_MAKER_NOTE 0x927C // Not processed yet. Maybe in the future.
85 #define TAG_USERCOMMENT 0x9286
86 #define TAG_FLASHPIX_VERSION 0xA000 // Not processed.
87 #define TAG_COLOUR_SPACE 0xA001 // Not processed. Format int16u. Values: 1-RGB; 2-Adobe RGB 65535-Uncalibrated
88 #define TAG_EXIF_IMAGEWIDTH 0xa002
89 #define TAG_EXIF_IMAGELENGTH 0xa003
90 #define TAG_INTEROP_OFFSET 0xa005
91 #define TAG_FOCALPLANEXRES 0xa20E
92 #define TAG_FOCALPLANEUNITS 0xa210
93 #define TAG_EXPOSURE_INDEX 0xa215
94 #define TAG_EXPOSURE_MODE 0xa402
95 #define TAG_WHITEBALANCE 0xa403
96 #define TAG_DIGITALZOOMRATIO 0xA404
97 #define TAG_FOCALLENGTH_35MM 0xa405
99 #define TAG_GPS_LAT_REF 1
100 #define TAG_GPS_LAT 2
101 #define TAG_GPS_LONG_REF 3
102 #define TAG_GPS_LONG 4
103 #define TAG_GPS_ALT_REF 5
104 #define TAG_GPS_ALT 6
106 //--------------------------------------------------------------------------
107 // Exif format descriptor stuff
112 #define FMT_URATIONAL 5
114 #define FMT_UNDEFINED 7
117 #define FMT_SRATIONAL 10
118 #define FMT_SINGLE 11
119 #define FMT_DOUBLE 12
120 // NOTE: Remember to change NUM_FORMATS if you define a new format
121 #define NUM_FORMATS 12
123 //--------------------------------------------------------------------------
124 // Internationalisation string IDs. The enum order must match that in the
125 // language file (e.g. 'language/English/strings.xml', and EXIF_PARSE_STRING_ID_BASE
126 // must match the ID of the first Exif string in that file.
127 #define EXIF_PARSE_STRING_ID_BASE 21800
130 ExifStrDistanceInfinite = EXIF_PARSE_STRING_ID_BASE,
131 // Whitebalance et.al.
137 ExifStrFlashNoStrobe,
140 ExifStrFlashManualNoReturn,
141 ExifStrFlashManualReturn,
143 ExifStrFlashAutoNoReturn,
144 ExifStrFlashAutoReturn,
146 ExifStrFlashRedEyeNoReturn,
147 ExifStrFlashRedEyeReturn,
148 ExifStrFlashManualRedEye,
149 ExifStrFlashManualRedEyeNoReturn,
150 ExifStrFlashManualRedEyeReturn,
151 ExifStrFlashAutoRedEye,
152 ExifStrFlashAutoRedEyeNoReturn,
153 ExifStrFlashAutoRedEyeReturn,
162 ExifStrMeteringCenter,
164 ExifStrMeteringMatrix,
166 ExifStrExposureProgram,
167 ExifStrExposureAperture,
168 ExifStrExposureShutter,
169 ExifStrExposureCreative,
170 ExifStrExposureAction,
171 ExifStrExposurePortrait,
172 ExifStrExposureLandscape,
174 ExifStrExposureModeAuto,
176 ExifStrIsoEquivalent,
177 // GPS latitude, longitude, altitude
186 //--------------------------------------------------------------------------
187 // Report non fatal errors. Now that microsoft.net modifies exif headers,
188 // there's corrupted ones, and there could be more in the future.
189 //--------------------------------------------------------------------------
190 static void ErrNonfatal(const char* const msg, int a1, int a2)
192 printf("ExifParse - Nonfatal Error : %s %d %d", msg, a1, a2);
195 //--------------------------------------------------------------------------
197 //--------------------------------------------------------------------------
198 CExifParse::CExifParse () : m_FocalPlaneXRes(0.0),
199 m_FocalPlaneUnits(0.0), m_ExifImageWidth(0), m_MotorolaOrder(false),
205 //--------------------------------------------------------------------------
206 // Convert a 16 bit unsigned value from file's native byte order
207 //--------------------------------------------------------------------------
208 int CExifParse::Get16(const void* const Short, const bool motorolaOrder)
211 return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];
213 return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0];
217 //--------------------------------------------------------------------------
218 // Convert a 32 bit signed value from file's native byte order
219 //--------------------------------------------------------------------------
220 int CExifParse::Get32(const void* const Long, const bool motorolaOrder)
223 return ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16)
224 | (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 );
226 return ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16)
227 | (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 );
231 //--------------------------------------------------------------------------
232 // It appears that CStdString constructor replaces "\n" with "\r\n" which results
233 // in "\r\r\n" if there already is "\r\n", which in turn results in corrupted
234 // display. So this is an attempt to undo effects of a smart constructor. Also,
235 // replaces all nonprintable characters with "."
236 //--------------------------------------------------------------------------
237 /*void CExifParse::FixComment(CStdString& comment)
239 comment.Replace("\r\r\n", "\r\n");
240 for (unsigned int i=0; i<comment.length(); i++)
242 if ((comment[i] < 32) && (comment[i] != '\n') && (comment[i] != '\t') && (comment[i] != '\r'))
249 //--------------------------------------------------------------------------
250 // Evaluate number, be it int, rational, or float from directory.
251 //--------------------------------------------------------------------------
252 double CExifParse::ConvertAnyFormat(const void* const ValuePtr, int Format)
259 case FMT_SBYTE: Value = *( signed char*)ValuePtr; break;
260 case FMT_BYTE: Value = *(unsigned char*)ValuePtr; break;
262 case FMT_USHORT: Value = Get16(ValuePtr, m_MotorolaOrder); break;
263 case FMT_ULONG: Value = (unsigned)Get32(ValuePtr, m_MotorolaOrder); break;
269 Num = Get32(ValuePtr, m_MotorolaOrder);
270 Den = Get32(4+(char *)ValuePtr, m_MotorolaOrder);
272 if (Den == 0) Value = 0;
273 else Value = (double)Num/Den;
277 case FMT_SSHORT: Value = (signed short)Get16(ValuePtr, m_MotorolaOrder); break;
278 case FMT_SLONG: Value = Get32(ValuePtr, m_MotorolaOrder); break;
280 // Not sure if this is correct (never seen float used in Exif format)
281 case FMT_SINGLE: Value = (double)*(float*)ValuePtr; break;
282 case FMT_DOUBLE: Value = *(double*)ValuePtr; break;
285 ErrNonfatal("Illegal format code %d",Format,0);
290 //--------------------------------------------------------------------------
291 // Exif date tag is stored as a fixed format string "YYYY:MM:DD HH:MM:SS".
292 // If date is not set, then the string is filled with blanks and colons:
293 // " : : : : ". We want this string localised.
294 //--------------------------------------------------------------------------
295 /*void CExifParse::LocaliseDate (void)
297 if (m_ExifInfo[SLIDE_EXIF_DATE_TIME][0] != ' ')
299 int year = atoi(m_ExifInfo[SLIDE_EXIF_DATE_TIME].substr(0, 4).c_str());
300 int month = atoi(m_ExifInfo[SLIDE_EXIF_DATE_TIME].substr(5, 2).c_str());
301 int day = atoi(m_ExifInfo[SLIDE_EXIF_DATE_TIME].substr(8, 2).c_str());
302 int hour = atoi(m_ExifInfo[SLIDE_EXIF_DATE_TIME].substr(11,2).c_str());
303 int min = atoi(m_ExifInfo[SLIDE_EXIF_DATE_TIME].substr(14,2).c_str());
304 int sec = atoi(m_ExifInfo[SLIDE_EXIF_DATE_TIME].substr(17,2).c_str());
305 CDateTime date(year, month, day, hour, min, sec);
306 m_ExifInfo[SLIDE_EXIF_DATE_TIME] = date.GetAsLocalizedDateTime();
311 //--------------------------------------------------------------------------
312 // Convert exposure time into a human readable format
313 //--------------------------------------------------------------------------
314 /*void CExifParse::GetExposureTime(const float exposureTime, CStdString& outStr)
318 if (exposureTime < 0.010) outStr.Format("%6.4fs ", exposureTime);
319 else outStr.Format("%5.3fs ", exposureTime);
320 if (exposureTime <= 0.5) outStr.Format("%s (1/%d)", outStr, (int)(0.5 + 1/exposureTime));
324 //--------------------------------------------------------------------------
325 // Process one of the nested EXIF directories.
326 //--------------------------------------------------------------------------
327 void CExifParse::ProcessDir(const unsigned char* const DirStart,
328 const unsigned char* const OffsetBase,
329 const unsigned ExifLength,
332 if (NestingLevel > 4)
334 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
338 char IndentString[25];
339 memset(IndentString, ' ', 25);
340 IndentString[NestingLevel * 4] = '\0';
343 int NumDirEntries = Get16((void*)DirStart, m_MotorolaOrder);
345 const unsigned char* const DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
346 if (DirEnd+4 > (OffsetBase+ExifLength))
348 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength)
350 // Version 1.3 of jhead would truncate a bit too much.
351 // This also caught later on as well.
355 ErrNonfatal("Illegally sized directory", 0,0);
360 const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
363 for (int de=0;de<NumDirEntries;de++)
365 int Tag, Format, Components;
366 unsigned char* ValuePtr;
368 const unsigned char* const DirEntry = DIR_ENTRY_ADDR(DirStart, de);
370 Tag = Get16(DirEntry, m_MotorolaOrder);
371 Format = Get16(DirEntry+2, m_MotorolaOrder);
372 Components = Get32(DirEntry+4, m_MotorolaOrder);
374 if ((Format-1) >= NUM_FORMATS)
376 // (-1) catches illegal zero case as unsigned underflows to positive large.
377 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
381 if ((unsigned)Components > 0x10000)
383 ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
387 ByteCount = Components * BytesPerFormat[Format];
392 OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
393 // If its bigger than 4 bytes, the dir entry contains an offset.
394 if (OffsetVal+ByteCount > ExifLength)
396 // Bogus pointer offset and / or bytecount value
397 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
400 ValuePtr = (unsigned char*)(OffsetBase+OffsetVal);
402 if (OffsetVal > m_LargestExifOffset)
404 m_LargestExifOffset = OffsetVal;
409 // 4 bytes or less and value is in the dir entry itself
410 ValuePtr = (unsigned char*)(DirEntry+8);
414 // Extract useful components of tag
417 case TAG_DESCRIPTION:
419 int length = max(ByteCount, 0);
420 length = min(length, MAX_COMMENT);
421 strncpy(m_ExifInfo->Description, (char *)ValuePtr, length);
424 case TAG_MAKE: strncpy(m_ExifInfo->CameraMake, (char *)ValuePtr, 32); break;
425 case TAG_MODEL: strncpy(m_ExifInfo->CameraModel, (char *)ValuePtr, 40); break;
426 // case TAG_SOFTWARE: strncpy(m_ExifInfo->Software, ValuePtr, 5); break;
427 case TAG_FOCALPLANEXRES: m_FocalPlaneXRes = ConvertAnyFormat(ValuePtr, Format); break;
428 case TAG_THUMBNAIL_OFFSET: m_ExifInfo->ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break;
429 case TAG_THUMBNAIL_LENGTH: m_ExifInfo->ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break;
435 case TAG_DATETIME_ORIGINAL:
436 // If we get a DATETIME_ORIGINAL, we use that one.
437 strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, 20);
441 case TAG_DATETIME_DIGITIZED:
443 if (m_DateFound == false)
445 // If we don't already have a DATETIME_ORIGINAL, use whatever
446 // time fields we may have.
447 strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, 20);
452 case TAG_USERCOMMENT:
454 // The UserComment allows comments without the charset limitations of ImageDescription.
455 // Therefore the UserComment field is prefixed by a CharacterCode field (8 Byte):
456 // - ASCII: 'ASCII\0\0\0'
457 // - Unicode: 'UNICODE\0'
458 // - JIS X208-1990: 'JIS\0\0\0\0\0'
459 // - Unknown: '\0\0\0\0\0\0\0\0' (application specific)
461 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNKNOWN;
463 const int EXIF_COMMENT_CHARSET_LENGTH = 8;
464 if (ByteCount >= EXIF_COMMENT_CHARSET_LENGTH)
466 // As some implementations use spaces instead of \0 for the padding,
467 // we're not so strict and check only the prefix.
468 if (memcmp(ValuePtr, "ASCII", 5) == 0)
469 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_ASCII;
470 else if (memcmp(ValuePtr, "UNICODE", 7) == 0)
471 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
472 else if (memcmp(ValuePtr, "JIS", 3) == 0)
473 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_JIS;
475 int length = ByteCount - EXIF_COMMENT_CHARSET_LENGTH;
476 length = min(length, MAX_COMMENT);
477 memcpy(m_ExifInfo->Comments, ValuePtr + EXIF_COMMENT_CHARSET_LENGTH, length);
478 // FixComment(comment); // Ensure comment is printable
484 // Simplest way of expressing aperture, so I trust it the most.
485 // (overwrite previously computd value if there is one)
486 m_ExifInfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
490 case TAG_MAXAPERTURE:
491 // More relevant info always comes earlier, so only use this field if we don't
492 // have appropriate aperture information yet.
493 if (m_ExifInfo->ApertureFNumber == 0)
495 m_ExifInfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
499 case TAG_FOCALLENGTH:
500 // Nice digital cameras actually save the focal length as a function
501 // of how far they are zoomed in.
502 m_ExifInfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
505 case TAG_SUBJECT_DISTANCE:
506 // Inidcates the distacne the autofocus camera is focused to.
507 // Tends to be less accurate as distance increases.
509 float distance = (float)ConvertAnyFormat(ValuePtr, Format);
511 m_ExifInfo->Distance = distance; // infinite
513 m_ExifInfo->Distance = distance;
517 case TAG_EXPOSURETIME:
519 // Simplest way of expressing exposure time, so I trust it most.
520 // (overwrite previously computd value if there is one)
521 float expTime = (float)ConvertAnyFormat(ValuePtr, Format);
523 m_ExifInfo->ExposureTime = expTime;
527 case TAG_SHUTTERSPEED:
528 // More complicated way of expressing exposure time, so only use
529 // this value if we don't already have it from somewhere else.
530 if (m_ExifInfo->ExposureTime == 0)
532 m_ExifInfo->ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
537 m_ExifInfo->FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format);
540 case TAG_ORIENTATION:
541 m_ExifInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
542 if (m_ExifInfo->Orientation < 0 || m_ExifInfo->Orientation > 8)
544 ErrNonfatal("Undefined rotation value %d", m_ExifInfo->Orientation, 0);
545 m_ExifInfo->Orientation = 0;
549 case TAG_EXIF_IMAGELENGTH:
550 case TAG_EXIF_IMAGEWIDTH:
551 // Use largest of height and width to deal with images that have been
552 // rotated to portrait format.
554 int a = (int)ConvertAnyFormat(ValuePtr, Format);
555 if (m_ExifImageWidth < a) m_ExifImageWidth = a;
559 case TAG_FOCALPLANEUNITS:
560 switch((int)ConvertAnyFormat(ValuePtr, Format))
562 // According to the information I was using, 2 means meters.
563 // But looking at the Cannon powershot's files, inches is the only
565 case 1: m_FocalPlaneUnits = 25.4; break; // inch
566 case 2: m_FocalPlaneUnits = 25.4; break;
567 case 3: m_FocalPlaneUnits = 10; break; // centimeter
568 case 4: m_FocalPlaneUnits = 1; break; // millimeter
569 case 5: m_FocalPlaneUnits = .001; break; // micrometer
573 case TAG_EXPOSURE_BIAS:
574 m_ExifInfo->ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
577 case TAG_WHITEBALANCE:
578 m_ExifInfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
581 case TAG_LIGHT_SOURCE:
582 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
583 m_ExifInfo->LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
586 case TAG_METERING_MODE:
587 m_ExifInfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
590 case TAG_EXPOSURE_PROGRAM:
591 m_ExifInfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
594 case TAG_EXPOSURE_INDEX:
595 if (m_ExifInfo->ISOequivalent == 0)
597 // Exposure index and ISO equivalent are often used interchangeably,
598 // so we will do the same.
599 // http://photography.about.com/library/glossary/bldef_ei.htm
600 m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
604 case TAG_ISO_EQUIVALENT:
605 m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
606 if (m_ExifInfo->ISOequivalent < 50)
607 m_ExifInfo->ISOequivalent *= 200; // Fixes strange encoding on some older digicams.
610 case TAG_EXPOSURE_MODE:
611 m_ExifInfo->ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
614 case TAG_DIGITALZOOMRATIO:
615 m_ExifInfo->DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
618 case TAG_EXIF_OFFSET:
619 case TAG_INTEROP_OFFSET:
621 const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
622 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
624 ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
628 ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
636 const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
637 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
639 ErrNonfatal("Illegal GPS directory link",0,0);
643 ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
649 case TAG_FOCALLENGTH_35MM:
650 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
651 // if its present, use it to compute equivalent focal length instead of
652 // computing it from sensor geometry and actual focal length.
653 m_ExifInfo->FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
659 // In addition to linking to subdirectories via exif tags,
660 // there's also a potential link to another directory at the end of each
661 // directory. this has got to be the result of a committee!
664 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength)
666 Offset = (unsigned)Get32(DirStart+2+12*NumDirEntries, m_MotorolaOrder);
669 const unsigned char* const SubdirStart = OffsetBase + Offset;
670 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase)
672 if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20)
674 // Jhead 1.3 or earlier would crop the whole directory!
675 // As Jhead produces this form of format incorrectness,
676 // I'll just let it pass silently
680 ErrNonfatal("Illegal subdirectory link",0,0);
685 if (SubdirStart <= OffsetBase+ExifLength)
687 ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
690 if (Offset > m_LargestExifOffset)
692 m_LargestExifOffset = Offset;
698 // The exif header ends before the last next directory pointer.
701 if (m_ExifInfo->ThumbnailOffset)
703 m_ExifInfo->ThumbnailAtEnd = false;
705 if (m_ExifInfo->ThumbnailOffset <= ExifLength)
707 if (m_ExifInfo->ThumbnailSize > ExifLength - m_ExifInfo->ThumbnailOffset)
709 // If thumbnail extends past exif header, only save the part that
710 // actually exists. Canon's EOS viewer utility will do this - the
711 // thumbnail extracts ok with this hack.
712 m_ExifInfo->ThumbnailSize = ExifLength - m_ExifInfo->ThumbnailOffset;
719 //--------------------------------------------------------------------------
720 // Process a EXIF marker
721 // Describes all the drivel that most digital cameras include...
722 //--------------------------------------------------------------------------
723 bool CExifParse::Process (const unsigned char* const ExifSection, const unsigned short length, ExifInfo_t *info)
726 // EXIF signature: "Exif\0\0"
727 // Check EXIF signatures
728 const char ExifHeader[] = "Exif\0\0";
729 const char ExifAlignment0[] = "II";
730 const char ExifAlignment1[] = "MM";
731 const char ExifExtra = 0x2a;
733 char* pos = (char*)(ExifSection + sizeof(short)); // position data pointer after length field
735 if (memcmp(pos, ExifHeader,6))
737 printf("ExifParse: incorrect Exif header");
742 if (memcmp(pos, ExifAlignment0, strlen(ExifAlignment0)) == 0)
744 m_MotorolaOrder = false;
746 else if (memcmp(pos, ExifAlignment1, strlen(ExifAlignment1)) == 0)
748 m_MotorolaOrder = true;
752 printf("ExifParse: invalid Exif alignment marker");
755 pos += strlen(ExifAlignment0);
757 // Check the next value for correctness.
758 if (Get16((void*)(pos), m_MotorolaOrder) != ExifExtra)
760 printf("ExifParse: invalid Exif start (1)");
763 pos += sizeof(short);
765 unsigned long FirstOffset = (unsigned)Get32((void*)pos, m_MotorolaOrder);
766 if (FirstOffset < 8 || FirstOffset > 16)
768 // Usually set to 8, but other values valid too.
769 // CLog::Log(LOGERROR, "ExifParse: suspicious offset of first IFD value");
774 // First directory starts 16 bytes in. All offset are relative to 8 bytes in.
775 ProcessDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
777 m_ExifInfo->ThumbnailAtEnd = m_ExifInfo->ThumbnailOffset >= m_LargestExifOffset ? true : false;
779 // Compute the CCD width, in millimeters.
780 if (m_FocalPlaneXRes != 0)
782 // Note: With some cameras, its not possible to compute this correctly because
783 // they don't adjust the indicated focal plane resolution units when using less
784 // than maximum resolution, so the CCDWidth value comes out too small. Nothing
785 // that Jhead can do about it - its a camera problem.
786 m_ExifInfo->CCDWidth = (float)(m_ExifImageWidth * m_FocalPlaneUnits / m_FocalPlaneXRes);
789 if (m_ExifInfo->FocalLength)
791 if (m_ExifInfo->FocalLength35mmEquiv == 0)
793 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
794 // already got it explicitly from a tag.
795 if (m_ExifInfo->CCDWidth != 0.0)
797 m_ExifInfo->FocalLength35mmEquiv = (int)(m_ExifInfo->FocalLength/m_ExifInfo->CCDWidth*36 + 0.5);
806 //--------------------------------------------------------------------------
807 // GPS Lat/Long extraction helper function
808 //--------------------------------------------------------------------------
809 void CExifParse::GetLatLong(
810 const unsigned int Format,
811 const unsigned char* ValuePtr,
812 const int ComponentSize,
815 if (Format != FMT_URATIONAL)
817 ErrNonfatal("Illegal number format %d for GPS Lat/Long", Format, 0);
822 for (unsigned a=0; a<3 ;a++)
824 Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
827 sprintf(latLong, "%3.0fd %2.0f' %5.2f\"", Values[0], Values[1], Values[2]);
828 strcat(latLongString, latLong);
832 //--------------------------------------------------------------------------
833 // Process GPS info directory
834 //--------------------------------------------------------------------------
835 void CExifParse::ProcessGpsInfo(
836 const unsigned char* const DirStart,
838 const unsigned char* const OffsetBase,
841 int NumDirEntries = Get16(DirStart, m_MotorolaOrder);
843 for (int de=0;de<NumDirEntries;de++)
845 const unsigned char* DirEntry = DIR_ENTRY_ADDR(DirStart, de);
847 unsigned Tag = Get16(DirEntry, m_MotorolaOrder);
848 unsigned Format = Get16(DirEntry+2, m_MotorolaOrder);
849 unsigned Components = (unsigned)Get32(DirEntry+4, m_MotorolaOrder);
850 if ((Format-1) >= NUM_FORMATS)
852 // (-1) catches illegal zero case as unsigned underflows to positive large.
853 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
857 const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
858 int ComponentSize = BytesPerFormat[Format];
859 unsigned ByteCount = Components * ComponentSize;
861 const unsigned char* ValuePtr;
865 unsigned OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
866 // If its bigger than 4 bytes, the dir entry contains an offset.
867 if (OffsetVal+ByteCount > ExifLength)
869 // Bogus pointer offset and / or bytecount value
870 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
873 ValuePtr = OffsetBase+OffsetVal;
877 // 4 bytes or less and value is in the dir entry itself
878 ValuePtr = DirEntry+8;
883 case TAG_GPS_LAT_REF:
884 m_ExifInfo->GpsLat[0] = ValuePtr[0];
885 m_ExifInfo->GpsLat[1] = 0;
888 case TAG_GPS_LONG_REF:
889 m_ExifInfo->GpsLong[0] = ValuePtr[0];
890 m_ExifInfo->GpsLong[1] = 0;
894 GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLat);
897 GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLong);
900 case TAG_GPS_ALT_REF:
901 if (ValuePtr[0] != 0)
902 m_ExifInfo->GpsAlt[0] = '-';
903 m_ExifInfo->GpsAlt[1] = 0;
909 sprintf(temp,"%dm", Get32(ValuePtr, m_MotorolaOrder));
910 strcat(m_ExifInfo->GpsAlt, temp);