2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 //--------------------------------------------------------------------------
10 // Program to pull the EXIF information out of various types of digital
11 // images and present it in a reasonably consistent way
13 // Original code pulled from 'jhead' by Matthias Wandel (http://www.sentex.net/~mwandel/) - jhead
14 // Adapted for XBMC by DD.
15 //--------------------------------------------------------------------------
17 // Note: Jhead supports TAG_MAKER_NOTE exif field,
18 // but that is omitted for now - to make porting easier and addition smaller
20 #include "ExifParse.h"
33 #define min(a,b) (a)>(b)?(b):(a)
36 #define max(a,b) (a)<(b)?(b):(a)
40 // Prototypes for exif utility functions.
41 static void ErrNonfatal(const char* const msg
, int a1
, int a2
);
43 #define DIR_ENTRY_ADDR(Start, Entry) ((Start)+2+12*(Entry))
46 //--------------------------------------------------------------------------
47 // Describes tag values
48 #define TAG_DESCRIPTION 0x010E
49 #define TAG_MAKE 0x010F
50 #define TAG_MODEL 0x0110
51 #define TAG_ORIENTATION 0x0112
52 #define TAG_X_RESOLUTION 0x011A // Not processed. Format rational64u (see http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.76/html/TagNames/EXIF.html)
53 #define TAG_Y_RESOLUTION 0x011B // Not processed. Format rational64u
54 #define TAG_RESOLUTION_UNIT 0x0128 // Not processed. Format int16u. Values: 1-none; 2-inches; 3-cm
55 #define TAG_SOFTWARE 0x0131
56 #define TAG_DATETIME 0x0132
57 #define TAG_THUMBNAIL_OFFSET 0x0201
58 #define TAG_THUMBNAIL_LENGTH 0x0202
59 #define TAG_Y_CB_CR_POS 0x0213 // Not processed. Format int16u. Values: 1-Centered; 2-Co-sited
60 #define TAG_EXPOSURETIME 0x829A
61 #define TAG_FNUMBER 0x829D
62 #define TAG_EXIF_OFFSET 0x8769
63 #define TAG_EXPOSURE_PROGRAM 0x8822
64 #define TAG_GPSINFO 0x8825
65 #define TAG_ISO_EQUIVALENT 0x8827
66 #define TAG_EXIF_VERSION 0x9000 // Not processed.
67 #define TAG_COMPONENT_CFG 0x9101 // Not processed.
68 #define TAG_DATETIME_ORIGINAL 0x9003
69 #define TAG_DATETIME_DIGITIZED 0x9004
70 #define TAG_SHUTTERSPEED 0x9201
71 #define TAG_APERTURE 0x9202
72 #define TAG_EXPOSURE_BIAS 0x9204
73 #define TAG_MAXAPERTURE 0x9205
74 #define TAG_SUBJECT_DISTANCE 0x9206
75 #define TAG_METERING_MODE 0x9207
76 #define TAG_LIGHT_SOURCE 0x9208
77 #define TAG_FLASH 0x9209
78 #define TAG_FOCALLENGTH 0x920A
79 #define TAG_MAKER_NOTE 0x927C // Not processed yet. Maybe in the future.
80 #define TAG_USERCOMMENT 0x9286
81 #define TAG_XP_COMMENT 0x9c9c
82 #define TAG_FLASHPIX_VERSION 0xA000 // Not processed.
83 #define TAG_COLOUR_SPACE 0xA001 // Not processed. Format int16u. Values: 1-RGB; 2-Adobe RGB 65535-Uncalibrated
84 #define TAG_EXIF_IMAGEWIDTH 0xa002
85 #define TAG_EXIF_IMAGELENGTH 0xa003
86 #define TAG_INTEROP_OFFSET 0xa005
87 #define TAG_FOCALPLANEXRES 0xa20E
88 #define TAG_FOCALPLANEUNITS 0xa210
89 #define TAG_EXPOSURE_INDEX 0xa215
90 #define TAG_EXPOSURE_MODE 0xa402
91 #define TAG_WHITEBALANCE 0xa403
92 #define TAG_DIGITALZOOMRATIO 0xA404
93 #define TAG_FOCALLENGTH_35MM 0xa405
95 #define TAG_GPS_LAT_REF 1
97 #define TAG_GPS_LONG_REF 3
98 #define TAG_GPS_LONG 4
99 #define TAG_GPS_ALT_REF 5
100 #define TAG_GPS_ALT 6
102 //--------------------------------------------------------------------------
103 // Exif format descriptor stuff
106 constexpr auto FMT_BYTE
= 1;
107 constexpr auto FMT_USHORT
= 2;
108 constexpr auto FMT_ULONG
= 3;
109 constexpr auto FMT_URATIONAL
= 4;
110 constexpr auto FMT_SBYTE
= 5;
111 constexpr auto FMT_SSHORT
= 6;
112 constexpr auto FMT_SLONG
= 7;
113 constexpr auto FMT_SRATIONAL
= 8;
114 constexpr auto FMT_SINGLE
= 9;
115 constexpr auto FMT_DOUBLE
= 10;
116 // NOTE: Remember to change NUM_FORMATS if you define a new format
117 constexpr auto NUM_FORMATS
= 10;
119 const unsigned int BytesPerFormat
[NUM_FORMATS
] = {1, 2, 4, 8, 1, 2, 4, 8, 4, 8};
122 //--------------------------------------------------------------------------
123 // Internationalisation string IDs. The enum order must match that in the
124 // language file (e.g. 'language/resource.language.en_gb/strings.po', and EXIF_PARSE_STRING_ID_BASE
125 // must match the ID of the first Exif string in that file.
126 #define EXIF_PARSE_STRING_ID_BASE 21800
129 ExifStrDistanceInfinite
= EXIF_PARSE_STRING_ID_BASE
,
130 // Whitebalance et.al.
136 ExifStrFlashNoStrobe
,
139 ExifStrFlashManualNoReturn
,
140 ExifStrFlashManualReturn
,
142 ExifStrFlashAutoNoReturn
,
143 ExifStrFlashAutoReturn
,
145 ExifStrFlashRedEyeNoReturn
,
146 ExifStrFlashRedEyeReturn
,
147 ExifStrFlashManualRedEye
,
148 ExifStrFlashManualRedEyeNoReturn
,
149 ExifStrFlashManualRedEyeReturn
,
150 ExifStrFlashAutoRedEye
,
151 ExifStrFlashAutoRedEyeNoReturn
,
152 ExifStrFlashAutoRedEyeReturn
,
161 ExifStrMeteringCenter
,
163 ExifStrMeteringMatrix
,
165 ExifStrExposureProgram
,
166 ExifStrExposureAperture
,
167 ExifStrExposureShutter
,
168 ExifStrExposureCreative
,
169 ExifStrExposureAction
,
170 ExifStrExposurePortrait
,
171 ExifStrExposureLandscape
,
173 ExifStrExposureModeAuto
,
175 ExifStrIsoEquivalent
,
176 // GPS latitude, longitude, altitude
185 //--------------------------------------------------------------------------
186 // Report non fatal errors. Now that microsoft.net modifies exif headers,
187 // there's corrupted ones, and there could be more in the future.
188 //--------------------------------------------------------------------------
189 static void ErrNonfatal(const char* const msg
, int a1
, int a2
)
191 printf("ExifParse - Nonfatal Error : %s %d %d", msg
, a1
, a2
);
194 //--------------------------------------------------------------------------
195 // Convert a 16 bit unsigned value from file's native byte order
196 //--------------------------------------------------------------------------
197 int CExifParse::Get16(const void* const Short
, const bool motorolaOrder
)
200 return (((const unsigned char *)Short
)[0] << 8) | ((const unsigned char *)Short
)[1];
202 return (((const unsigned char *)Short
)[1] << 8) | ((const unsigned char *)Short
)[0];
206 //--------------------------------------------------------------------------
207 // Convert a 32 bit signed value from file's native byte order
208 //--------------------------------------------------------------------------
209 int CExifParse::Get32(const void* const Long
, const bool motorolaOrder
)
212 return (((const char *)Long
)[0] << 24) | (((const unsigned char *)Long
)[1] << 16)
213 | (((const unsigned char *)Long
)[2] << 8 ) | (((const unsigned char *)Long
)[3] << 0 );
215 return (((const char *)Long
)[3] << 24) | (((const unsigned char *)Long
)[2] << 16)
216 | (((const unsigned char *)Long
)[1] << 8 ) | (((const unsigned char *)Long
)[0] << 0 );
220 //--------------------------------------------------------------------------
221 // It appears that CStdString constructor replaces "\n" with "\r\n" which results
222 // in "\r\r\n" if there already is "\r\n", which in turn results in corrupted
223 // display. So this is an attempt to undo effects of a smart constructor. Also,
224 // replaces all nonprintable characters with "."
225 //--------------------------------------------------------------------------
226 /*void CExifParse::FixComment(CStdString& comment)
228 comment.Replace("\r\r\n", "\r\n");
229 for (unsigned int i=0; i<comment.length(); i++)
231 if ((comment[i] < 32) && (comment[i] != '\n') && (comment[i] != '\t') && (comment[i] != '\r'))
238 //--------------------------------------------------------------------------
239 // Evaluate number, be it int, rational, or float from directory.
240 //--------------------------------------------------------------------------
241 double CExifParse::ConvertAnyFormat(const void* const ValuePtr
, int Format
)
248 case FMT_SBYTE
: Value
= *(const signed char*)ValuePtr
; break;
249 case FMT_BYTE
: Value
= *(const unsigned char*)ValuePtr
; break;
251 case FMT_USHORT
: Value
= Get16(ValuePtr
, m_MotorolaOrder
); break;
252 case FMT_ULONG
: Value
= (unsigned)Get32(ValuePtr
, m_MotorolaOrder
); break;
258 Num
= Get32(ValuePtr
, m_MotorolaOrder
);
259 Den
= Get32(4+(const char *)ValuePtr
, m_MotorolaOrder
);
261 if (Den
== 0) Value
= 0;
262 else Value
= (double)Num
/Den
;
266 case FMT_SSHORT
: Value
= (signed short)Get16(ValuePtr
, m_MotorolaOrder
); break;
267 case FMT_SLONG
: Value
= Get32(ValuePtr
, m_MotorolaOrder
); break;
269 // Not sure if this is correct (never seen float used in Exif format)
270 case FMT_SINGLE
: Value
= (double)*(const float*)ValuePtr
; break;
271 case FMT_DOUBLE
: Value
= *(const double*)ValuePtr
; break;
274 ErrNonfatal("Illegal format code %d",Format
,0);
279 //--------------------------------------------------------------------------
280 // Exif date tag is stored as a fixed format string "YYYY:MM:DD HH:MM:SS".
281 // If date is not set, then the string is filled with blanks and colons:
282 // " : : : : ". We want this string localised.
283 //--------------------------------------------------------------------------
284 /*void CExifParse::LocaliseDate (void)
286 if (m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME][0] != ' ')
288 int year = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(0, 4).c_str());
289 int month = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(5, 2).c_str());
290 int day = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(8, 2).c_str());
291 int hour = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(11,2).c_str());
292 int min = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(14,2).c_str());
293 int sec = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(17,2).c_str());
294 CDateTime date(year, month, day, hour, min, sec);
295 m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME] = date.GetAsLocalizedDateTime();
300 //--------------------------------------------------------------------------
301 // Convert exposure time into a human readable format
302 //--------------------------------------------------------------------------
303 /*void CExifParse::GetExposureTime(const float exposureTime, CStdString& outStr)
307 if (exposureTime < 0.010) outStr.Format("%6.4fs ", exposureTime);
308 else outStr.Format("%5.3fs ", exposureTime);
309 if (exposureTime <= 0.5) outStr.Format("%s (1/%d)", outStr, (int)(0.5 + 1/exposureTime));
313 //--------------------------------------------------------------------------
314 // Process one of the nested EXIF directories.
315 //--------------------------------------------------------------------------
316 void CExifParse::ProcessDir(const unsigned char* const DirStart
,
317 const unsigned char* const OffsetBase
,
318 const unsigned ExifLength
,
321 if (NestingLevel
> 4)
323 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
327 char IndentString
[25];
328 memset(IndentString
, ' ', 25);
329 IndentString
[NestingLevel
* 4] = '\0';
332 int NumDirEntries
= Get16((const void*)DirStart
, m_MotorolaOrder
);
334 const unsigned char* const DirEnd
= DIR_ENTRY_ADDR(DirStart
, NumDirEntries
);
335 if (DirEnd
+4 > (OffsetBase
+ExifLength
))
337 if (DirEnd
+2 == OffsetBase
+ExifLength
|| DirEnd
== OffsetBase
+ExifLength
)
339 // Version 1.3 of jhead would truncate a bit too much.
340 // This also caught later on as well.
344 ErrNonfatal("Illegally sized directory", 0,0);
349 for (int de
=0;de
<NumDirEntries
;de
++)
351 int Tag
, Format
, Components
;
352 unsigned char* ValuePtr
;
354 const unsigned char* const DirEntry
= DIR_ENTRY_ADDR(DirStart
, de
);
356 Tag
= Get16(DirEntry
, m_MotorolaOrder
);
357 Format
= Get16(DirEntry
+2, m_MotorolaOrder
);
358 Components
= Get32(DirEntry
+4, m_MotorolaOrder
);
360 if (Format
<= 0 || Format
> NUM_FORMATS
)
362 ErrNonfatal("Illegal number format %d for tag %04x", Format
, Tag
);
366 if ((unsigned)Components
> 0x10000)
368 ErrNonfatal("Illegal number of components %d for tag %04x", Components
, Tag
);
372 ByteCount
= Components
* BytesPerFormat
[Format
- 1];
377 OffsetVal
= (unsigned)Get32(DirEntry
+8, m_MotorolaOrder
);
378 // If its bigger than 4 bytes, the dir entry contains an offset.
379 if (OffsetVal
+ByteCount
> ExifLength
)
381 // Bogus pointer offset and / or bytecount value
382 ErrNonfatal("Illegal value pointer for tag %04x", Tag
,0);
385 ValuePtr
= (unsigned char*)(const_cast<unsigned char*>(OffsetBase
)+OffsetVal
);
387 if (OffsetVal
> m_LargestExifOffset
)
389 m_LargestExifOffset
= OffsetVal
;
394 // 4 bytes or less and value is in the dir entry itself
395 ValuePtr
= (unsigned char*)(const_cast<unsigned char*>(DirEntry
)+8);
399 // Extract useful components of tag
402 case TAG_DESCRIPTION
:
404 int length
= max(ByteCount
, 0);
405 length
= min(length
, MAX_COMMENT
);
406 strncpy(m_ExifInfo
->Description
, (char *)ValuePtr
, length
);
407 m_ExifInfo
->Description
[length
] = '\0';
412 int space
= sizeof(m_ExifInfo
->CameraMake
);
415 strncpy(m_ExifInfo
->CameraMake
, (char *)ValuePtr
, space
- 1);
416 m_ExifInfo
->CameraMake
[space
- 1] = '\0';
422 int space
= sizeof(m_ExifInfo
->CameraModel
);
425 strncpy(m_ExifInfo
->CameraModel
, (char *)ValuePtr
, space
- 1);
426 m_ExifInfo
->CameraModel
[space
- 1] = '\0';
430 // case TAG_SOFTWARE: strncpy(m_ExifInfo->Software, ValuePtr, 5); break;
431 case TAG_FOCALPLANEXRES
: m_FocalPlaneXRes
= ConvertAnyFormat(ValuePtr
, Format
); break;
432 case TAG_THUMBNAIL_OFFSET
: m_ExifInfo
->ThumbnailOffset
= (unsigned)ConvertAnyFormat(ValuePtr
, Format
); break;
433 case TAG_THUMBNAIL_LENGTH
: m_ExifInfo
->ThumbnailSize
= (unsigned)ConvertAnyFormat(ValuePtr
, Format
); break;
439 case TAG_DATETIME_ORIGINAL
:
442 int space
= sizeof(m_ExifInfo
->DateTime
);
445 strncpy(m_ExifInfo
->DateTime
, (char *)ValuePtr
, space
- 1);
446 m_ExifInfo
->DateTime
[space
- 1] = '\0';
447 // If we get a DATETIME_ORIGINAL, we use that one.
452 case TAG_DATETIME_DIGITIZED
:
457 // If we don't already have a DATETIME_ORIGINAL, use whatever
458 // time fields we may have.
459 int space
= sizeof(m_ExifInfo
->DateTime
);
462 strncpy(m_ExifInfo
->DateTime
, (char *)ValuePtr
, space
- 1);
463 m_ExifInfo
->DateTime
[space
- 1] = '\0';
468 case TAG_USERCOMMENT
:
470 // The UserComment allows comments without the charset limitations of ImageDescription.
471 // Therefore the UserComment field is prefixed by a CharacterCode field (8 Byte):
472 // - ASCII: 'ASCII\0\0\0'
473 // - Unicode: 'UNICODE\0'
474 // - JIS X208-1990: 'JIS\0\0\0\0\0'
475 // - Unknown: '\0\0\0\0\0\0\0\0' (application specific)
477 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_UNKNOWN
;
479 const int EXIF_COMMENT_CHARSET_LENGTH
= 8;
480 if (ByteCount
>= EXIF_COMMENT_CHARSET_LENGTH
)
482 // As some implementations use spaces instead of \0 for the padding,
483 // we're not so strict and check only the prefix.
484 if (memcmp(ValuePtr
, "ASCII", 5) == 0)
485 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_ASCII
;
486 else if (memcmp(ValuePtr
, "UNICODE", 7) == 0)
487 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_UNICODE
;
488 else if (memcmp(ValuePtr
, "JIS", 3) == 0)
489 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_JIS
;
491 int length
= ByteCount
- EXIF_COMMENT_CHARSET_LENGTH
;
492 length
= min(length
, MAX_COMMENT
);
493 memcpy(m_ExifInfo
->Comments
, ValuePtr
+ EXIF_COMMENT_CHARSET_LENGTH
, length
);
494 m_ExifInfo
->Comments
[length
] = '\0';
495 // FixComment(comment); // Ensure comment is printable
502 // The XP user comment field is always unicode (UCS-2) encoded
503 m_ExifInfo
->XPCommentsCharset
= EXIF_COMMENT_CHARSET_UNICODE
;
504 size_t length
= min(ByteCount
, MAX_COMMENT
);
505 memcpy(m_ExifInfo
->XPComment
, ValuePtr
, length
);
506 m_ExifInfo
->XPComment
[length
] = '\0';
511 // Simplest way of expressing aperture, so I trust it the most.
512 // (overwrite previously computd value if there is one)
513 m_ExifInfo
->ApertureFNumber
= (float)ConvertAnyFormat(ValuePtr
, Format
);
517 case TAG_MAXAPERTURE
:
518 // More relevant info always comes earlier, so only use this field if we don't
519 // have appropriate aperture information yet.
520 if (m_ExifInfo
->ApertureFNumber
== 0)
522 m_ExifInfo
->ApertureFNumber
= (float)exp(ConvertAnyFormat(ValuePtr
, Format
)*log(2.0)*0.5);
526 case TAG_FOCALLENGTH
:
527 // Nice digital cameras actually save the focal length as a function
528 // of how far they are zoomed in.
529 m_ExifInfo
->FocalLength
= (float)ConvertAnyFormat(ValuePtr
, Format
);
532 case TAG_SUBJECT_DISTANCE
:
533 // Inidcates the distacne the autofocus camera is focused to.
534 // Tends to be less accurate as distance increases.
536 float distance
= (float)ConvertAnyFormat(ValuePtr
, Format
);
537 m_ExifInfo
->Distance
= distance
;
541 case TAG_EXPOSURETIME
:
543 // Simplest way of expressing exposure time, so I trust it most.
544 // (overwrite previously computd value if there is one)
545 float expTime
= (float)ConvertAnyFormat(ValuePtr
, Format
);
547 m_ExifInfo
->ExposureTime
= expTime
;
551 case TAG_SHUTTERSPEED
:
552 // More complicated way of expressing exposure time, so only use
553 // this value if we don't already have it from somewhere else.
554 if (m_ExifInfo
->ExposureTime
== 0)
556 m_ExifInfo
->ExposureTime
= (float)(1/exp(ConvertAnyFormat(ValuePtr
, Format
)*log(2.0)));
561 m_ExifInfo
->FlashUsed
= (int)ConvertAnyFormat(ValuePtr
, Format
);
564 case TAG_ORIENTATION
:
565 m_ExifInfo
->Orientation
= (int)ConvertAnyFormat(ValuePtr
, Format
);
566 if (m_ExifInfo
->Orientation
< 0 || m_ExifInfo
->Orientation
> 8)
568 ErrNonfatal("Undefined rotation value %d", m_ExifInfo
->Orientation
, 0);
569 m_ExifInfo
->Orientation
= 0;
573 case TAG_EXIF_IMAGELENGTH
:
574 case TAG_EXIF_IMAGEWIDTH
:
575 // Use largest of height and width to deal with images that have been
576 // rotated to portrait format.
578 int a
= (int)ConvertAnyFormat(ValuePtr
, Format
);
579 if (m_ExifImageWidth
< a
) m_ExifImageWidth
= a
;
583 case TAG_FOCALPLANEUNITS
:
584 switch((int)ConvertAnyFormat(ValuePtr
, Format
))
586 // According to the information I was using, 2 means meters.
587 // But looking at the Cannon powershot's files, inches is the only
589 case 1: m_FocalPlaneUnits
= 25.4; break; // inch
590 case 2: m_FocalPlaneUnits
= 25.4; break;
591 case 3: m_FocalPlaneUnits
= 10; break; // centimeter
592 case 4: m_FocalPlaneUnits
= 1; break; // millimeter
593 case 5: m_FocalPlaneUnits
= .001; break; // micrometer
597 case TAG_EXPOSURE_BIAS
:
598 m_ExifInfo
->ExposureBias
= (float)ConvertAnyFormat(ValuePtr
, Format
);
601 case TAG_WHITEBALANCE
:
602 m_ExifInfo
->Whitebalance
= (int)ConvertAnyFormat(ValuePtr
, Format
);
605 case TAG_LIGHT_SOURCE
:
606 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
607 m_ExifInfo
->LightSource
= (int)ConvertAnyFormat(ValuePtr
, Format
);
610 case TAG_METERING_MODE
:
611 m_ExifInfo
->MeteringMode
= (int)ConvertAnyFormat(ValuePtr
, Format
);
614 case TAG_EXPOSURE_PROGRAM
:
615 m_ExifInfo
->ExposureProgram
= (int)ConvertAnyFormat(ValuePtr
, Format
);
618 case TAG_EXPOSURE_INDEX
:
619 if (m_ExifInfo
->ISOequivalent
== 0)
621 // Exposure index and ISO equivalent are often used interchangeably,
622 // so we will do the same.
623 // http://photography.about.com/library/glossary/bldef_ei.htm
624 m_ExifInfo
->ISOequivalent
= (int)ConvertAnyFormat(ValuePtr
, Format
);
628 case TAG_ISO_EQUIVALENT
:
629 m_ExifInfo
->ISOequivalent
= (int)ConvertAnyFormat(ValuePtr
, Format
);
630 if (m_ExifInfo
->ISOequivalent
< 50)
631 m_ExifInfo
->ISOequivalent
*= 200; // Fixes strange encoding on some older digicams.
634 case TAG_EXPOSURE_MODE
:
635 m_ExifInfo
->ExposureMode
= (int)ConvertAnyFormat(ValuePtr
, Format
);
638 case TAG_DIGITALZOOMRATIO
:
639 m_ExifInfo
->DigitalZoomRatio
= (float)ConvertAnyFormat(ValuePtr
, Format
);
642 case TAG_EXIF_OFFSET
:
643 case TAG_INTEROP_OFFSET
:
645 const unsigned char* const SubdirStart
= OffsetBase
+ (unsigned)Get32(ValuePtr
, m_MotorolaOrder
);
646 if (SubdirStart
< OffsetBase
|| SubdirStart
> OffsetBase
+ExifLength
)
648 ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
652 ProcessDir(SubdirStart
, OffsetBase
, ExifLength
, NestingLevel
+1);
660 const unsigned char* const SubdirStart
= OffsetBase
+ (unsigned)Get32(ValuePtr
, m_MotorolaOrder
);
661 if (SubdirStart
< OffsetBase
|| SubdirStart
> OffsetBase
+ExifLength
)
663 ErrNonfatal("Illegal GPS directory link",0,0);
667 ProcessGpsInfo(SubdirStart
, ByteCount
, OffsetBase
, ExifLength
);
673 case TAG_FOCALLENGTH_35MM
:
674 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
675 // if its present, use it to compute equivalent focal length instead of
676 // computing it from sensor geometry and actual focal length.
677 m_ExifInfo
->FocalLength35mmEquiv
= (unsigned)ConvertAnyFormat(ValuePtr
, Format
);
683 // In addition to linking to subdirectories via exif tags,
684 // there's also a potential link to another directory at the end of each
685 // directory. this has got to be the result of a committee!
688 if (DIR_ENTRY_ADDR(DirStart
, NumDirEntries
) + 4 <= OffsetBase
+ExifLength
)
690 Offset
= (unsigned)Get32(DirStart
+2+12*NumDirEntries
, m_MotorolaOrder
);
693 const unsigned char* const SubdirStart
= OffsetBase
+ Offset
;
694 if (SubdirStart
> OffsetBase
+ExifLength
|| SubdirStart
< OffsetBase
)
696 if (SubdirStart
> OffsetBase
&& SubdirStart
< OffsetBase
+ExifLength
+20)
698 // Jhead 1.3 or earlier would crop the whole directory!
699 // As Jhead produces this form of format incorrectness,
700 // I'll just let it pass silently
704 ErrNonfatal("Illegal subdirectory link",0,0);
709 if (SubdirStart
<= OffsetBase
+ExifLength
)
711 ProcessDir(SubdirStart
, OffsetBase
, ExifLength
, NestingLevel
+1);
714 if (Offset
> m_LargestExifOffset
)
716 m_LargestExifOffset
= Offset
;
722 // The exif header ends before the last next directory pointer.
725 if (m_ExifInfo
->ThumbnailOffset
)
727 m_ExifInfo
->ThumbnailAtEnd
= false;
729 if (m_ExifInfo
->ThumbnailOffset
<= ExifLength
)
731 if (m_ExifInfo
->ThumbnailSize
> ExifLength
- m_ExifInfo
->ThumbnailOffset
)
733 // If thumbnail extends past exif header, only save the part that
734 // actually exists. Canon's EOS viewer utility will do this - the
735 // thumbnail extracts ok with this hack.
736 m_ExifInfo
->ThumbnailSize
= ExifLength
- m_ExifInfo
->ThumbnailOffset
;
743 //--------------------------------------------------------------------------
744 // Process a EXIF marker
745 // Describes all the drivel that most digital cameras include...
746 //--------------------------------------------------------------------------
747 bool CExifParse::Process (const unsigned char* const ExifSection
, const unsigned short length
, ExifInfo_t
*info
)
750 // EXIF signature: "Exif\0\0"
751 // Check EXIF signatures
752 const char ExifHeader
[] = "Exif\0\0";
753 const char ExifAlignment0
[] = "II";
754 const char ExifAlignment1
[] = "MM";
755 const char ExifExtra
= 0x2a;
757 const char* pos
= (const char*)(ExifSection
+ sizeof(short)); // position data pointer after length field
759 if (memcmp(pos
, ExifHeader
,6))
761 printf("ExifParse: incorrect Exif header");
766 if (memcmp(pos
, ExifAlignment0
, strlen(ExifAlignment0
)) == 0)
768 m_MotorolaOrder
= false;
770 else if (memcmp(pos
, ExifAlignment1
, strlen(ExifAlignment1
)) == 0)
772 m_MotorolaOrder
= true;
776 printf("ExifParse: invalid Exif alignment marker");
779 pos
+= strlen(ExifAlignment0
);
781 // Check the next value for correctness.
782 if (Get16((const void*)(pos
), m_MotorolaOrder
) != ExifExtra
)
784 printf("ExifParse: invalid Exif start (1)");
787 pos
+= sizeof(short);
789 unsigned long FirstOffset
= (unsigned)Get32((const void*)pos
, m_MotorolaOrder
);
790 if (FirstOffset
< 8 || FirstOffset
> 16)
792 // Usually set to 8, but other values valid too.
793 // CLog::Log(LOGERROR, "ExifParse: suspicious offset of first IFD value");
798 // First directory starts 16 bytes in. All offset are relative to 8 bytes in.
799 ProcessDir(ExifSection
+8+FirstOffset
, ExifSection
+8, length
-8, 0);
801 m_ExifInfo
->ThumbnailAtEnd
= m_ExifInfo
->ThumbnailOffset
>= m_LargestExifOffset
;
803 // Compute the CCD width, in millimeters.
804 if (m_FocalPlaneXRes
!= 0)
806 // Note: With some cameras, its not possible to compute this correctly because
807 // they don't adjust the indicated focal plane resolution units when using less
808 // than maximum resolution, so the CCDWidth value comes out too small. Nothing
809 // that Jhead can do about it - its a camera problem.
810 m_ExifInfo
->CCDWidth
= (float)(m_ExifImageWidth
* m_FocalPlaneUnits
/ m_FocalPlaneXRes
);
813 if (m_ExifInfo
->FocalLength
)
815 if (m_ExifInfo
->FocalLength35mmEquiv
== 0)
817 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
818 // already got it explicitly from a tag.
819 if (m_ExifInfo
->CCDWidth
!= 0.0f
)
821 m_ExifInfo
->FocalLength35mmEquiv
=
822 (int)(m_ExifInfo
->FocalLength
/ m_ExifInfo
->CCDWidth
* 36 + 0.5f
);
831 //--------------------------------------------------------------------------
832 // GPS Lat/Long extraction helper function
833 //--------------------------------------------------------------------------
834 void CExifParse::GetLatLong(
835 const unsigned int Format
,
836 const unsigned char* ValuePtr
,
837 const int ComponentSize
,
840 if (Format
!= FMT_URATIONAL
)
842 ErrNonfatal("Illegal number format %d for GPS Lat/Long", Format
, 0);
847 for (unsigned a
=0; a
<3 ;a
++)
849 Values
[a
] = ConvertAnyFormat(ValuePtr
+a
*ComponentSize
, Format
);
851 if (Values
[0] < 0 || Values
[0] > 180 || Values
[1] < 0 || Values
[1] >= 60 || Values
[2] < 0 || Values
[2] >= 60)
853 // Ignore invalid values (DMS format expected)
854 ErrNonfatal("Invalid Lat/Long value", 0, 0);
855 latLongString
[0] = 0;
860 sprintf(latLong
, "%3.0fd %2.0f' %5.2f\"", Values
[0], Values
[1], Values
[2]);
861 strcat(latLongString
, latLong
);
866 //--------------------------------------------------------------------------
867 // Process GPS info directory
868 //--------------------------------------------------------------------------
869 void CExifParse::ProcessGpsInfo(
870 const unsigned char* const DirStart
,
872 const unsigned char* const OffsetBase
,
875 int NumDirEntries
= Get16(DirStart
, m_MotorolaOrder
);
877 for (int de
=0;de
<NumDirEntries
;de
++)
879 const unsigned char* DirEntry
= DIR_ENTRY_ADDR(DirStart
, de
);
881 unsigned Tag
= Get16(DirEntry
, m_MotorolaOrder
);
882 unsigned Format
= Get16(DirEntry
+2, m_MotorolaOrder
);
883 unsigned Components
= (unsigned)Get32(DirEntry
+4, m_MotorolaOrder
);
884 if (Format
== 0 || Format
> NUM_FORMATS
)
886 ErrNonfatal("Illegal number format %d for tag %04x", Format
, Tag
);
890 unsigned ComponentSize
= BytesPerFormat
[Format
- 1];
891 unsigned ByteCount
= Components
* ComponentSize
;
893 const unsigned char* ValuePtr
;
897 unsigned OffsetVal
= (unsigned)Get32(DirEntry
+8, m_MotorolaOrder
);
898 // If its bigger than 4 bytes, the dir entry contains an offset.
899 if (OffsetVal
+ByteCount
> ExifLength
)
901 // Bogus pointer offset and / or bytecount value
902 ErrNonfatal("Illegal value pointer for tag %04x", Tag
,0);
905 ValuePtr
= OffsetBase
+OffsetVal
;
909 // 4 bytes or less and value is in the dir entry itself
910 ValuePtr
= DirEntry
+8;
915 case TAG_GPS_LAT_REF
:
916 m_ExifInfo
->GpsLat
[0] = ValuePtr
[0];
917 m_ExifInfo
->GpsLat
[1] = 0;
920 case TAG_GPS_LONG_REF
:
921 m_ExifInfo
->GpsLong
[0] = ValuePtr
[0];
922 m_ExifInfo
->GpsLong
[1] = 0;
926 GetLatLong(Format
, ValuePtr
, ComponentSize
, m_ExifInfo
->GpsLat
);
929 GetLatLong(Format
, ValuePtr
, ComponentSize
, m_ExifInfo
->GpsLong
);
932 case TAG_GPS_ALT_REF
:
933 if (ValuePtr
[0] != 0)
934 m_ExifInfo
->GpsAlt
[0] = '-';
935 m_ExifInfo
->GpsAlt
[1] = 0;
941 sprintf(temp
, "%.2fm", static_cast<double>(ConvertAnyFormat(ValuePtr
, Format
)));
942 strcat(m_ExifInfo
->GpsAlt
, temp
);