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"
34 #define min(a,b) (a)>(b)?(b):(a)
37 #define max(a,b) (a)<(b)?(b):(a)
41 // Prototypes for exif utility functions.
42 static void ErrNonfatal(const char* const msg
, int a1
, int a2
);
44 #define DIR_ENTRY_ADDR(Start, Entry) ((Start)+2+12*(Entry))
47 //--------------------------------------------------------------------------
48 // Describes tag values
49 #define TAG_DESCRIPTION 0x010E
50 #define TAG_MAKE 0x010F
51 #define TAG_MODEL 0x0110
52 #define TAG_ORIENTATION 0x0112
53 #define TAG_X_RESOLUTION 0x011A // Not processed. Format rational64u (see http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.76/html/TagNames/EXIF.html)
54 #define TAG_Y_RESOLUTION 0x011B // Not processed. Format rational64u
55 #define TAG_RESOLUTION_UNIT 0x0128 // Not processed. Format int16u. Values: 1-none; 2-inches; 3-cm
56 #define TAG_SOFTWARE 0x0131
57 #define TAG_DATETIME 0x0132
58 #define TAG_THUMBNAIL_OFFSET 0x0201
59 #define TAG_THUMBNAIL_LENGTH 0x0202
60 #define TAG_Y_CB_CR_POS 0x0213 // Not processed. Format int16u. Values: 1-Centered; 2-Co-sited
61 #define TAG_EXPOSURETIME 0x829A
62 #define TAG_FNUMBER 0x829D
63 #define TAG_EXIF_OFFSET 0x8769
64 #define TAG_EXPOSURE_PROGRAM 0x8822
65 #define TAG_GPSINFO 0x8825
66 #define TAG_ISO_EQUIVALENT 0x8827
67 #define TAG_EXIF_VERSION 0x9000 // Not processed.
68 #define TAG_COMPONENT_CFG 0x9101 // Not processed.
69 #define TAG_DATETIME_ORIGINAL 0x9003
70 #define TAG_DATETIME_DIGITIZED 0x9004
71 #define TAG_SHUTTERSPEED 0x9201
72 #define TAG_APERTURE 0x9202
73 #define TAG_EXPOSURE_BIAS 0x9204
74 #define TAG_MAXAPERTURE 0x9205
75 #define TAG_SUBJECT_DISTANCE 0x9206
76 #define TAG_METERING_MODE 0x9207
77 #define TAG_LIGHT_SOURCE 0x9208
78 #define TAG_FLASH 0x9209
79 #define TAG_FOCALLENGTH 0x920A
80 #define TAG_MAKER_NOTE 0x927C // Not processed yet. Maybe in the future.
81 #define TAG_USERCOMMENT 0x9286
82 #define TAG_XP_COMMENT 0x9c9c
83 #define TAG_FLASHPIX_VERSION 0xA000 // Not processed.
84 #define TAG_COLOUR_SPACE 0xA001 // Not processed. Format int16u. Values: 1-RGB; 2-Adobe RGB 65535-Uncalibrated
85 #define TAG_EXIF_IMAGEWIDTH 0xa002
86 #define TAG_EXIF_IMAGELENGTH 0xa003
87 #define TAG_INTEROP_OFFSET 0xa005
88 #define TAG_FOCALPLANEXRES 0xa20E
89 #define TAG_FOCALPLANEUNITS 0xa210
90 #define TAG_EXPOSURE_INDEX 0xa215
91 #define TAG_EXPOSURE_MODE 0xa402
92 #define TAG_WHITEBALANCE 0xa403
93 #define TAG_DIGITALZOOMRATIO 0xA404
94 #define TAG_FOCALLENGTH_35MM 0xa405
96 #define TAG_GPS_LAT_REF 1
98 #define TAG_GPS_LONG_REF 3
99 #define TAG_GPS_LONG 4
100 #define TAG_GPS_ALT_REF 5
101 #define TAG_GPS_ALT 6
103 //--------------------------------------------------------------------------
104 // Exif format descriptor stuff
107 constexpr auto FMT_BYTE
= 1;
108 constexpr auto FMT_USHORT
= 2;
109 constexpr auto FMT_ULONG
= 3;
110 constexpr auto FMT_URATIONAL
= 4;
111 constexpr auto FMT_SBYTE
= 5;
112 constexpr auto FMT_SSHORT
= 6;
113 constexpr auto FMT_SLONG
= 7;
114 constexpr auto FMT_SRATIONAL
= 8;
115 constexpr auto FMT_SINGLE
= 9;
116 constexpr auto FMT_DOUBLE
= 10;
117 // NOTE: Remember to change NUM_FORMATS if you define a new format
118 constexpr auto NUM_FORMATS
= 10;
120 const unsigned int BytesPerFormat
[NUM_FORMATS
] = {1, 2, 4, 8, 1, 2, 4, 8, 4, 8};
123 //--------------------------------------------------------------------------
124 // Internationalisation string IDs. The enum order must match that in the
125 // language file (e.g. 'language/resource.language.en_gb/strings.po', 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 //--------------------------------------------------------------------------
196 // Convert a 16 bit unsigned value from file's native byte order
197 //--------------------------------------------------------------------------
198 int CExifParse::Get16(const void* const Short
, const bool motorolaOrder
)
201 return (((const unsigned char *)Short
)[0] << 8) | ((const unsigned char *)Short
)[1];
203 return (((const unsigned char *)Short
)[1] << 8) | ((const unsigned char *)Short
)[0];
207 //--------------------------------------------------------------------------
208 // Convert a 32 bit signed value from file's native byte order
209 //--------------------------------------------------------------------------
210 int CExifParse::Get32(const void* const Long
, const bool motorolaOrder
)
213 return (((const char *)Long
)[0] << 24) | (((const unsigned char *)Long
)[1] << 16)
214 | (((const unsigned char *)Long
)[2] << 8 ) | (((const unsigned char *)Long
)[3] << 0 );
216 return (((const char *)Long
)[3] << 24) | (((const unsigned char *)Long
)[2] << 16)
217 | (((const unsigned char *)Long
)[1] << 8 ) | (((const unsigned char *)Long
)[0] << 0 );
221 //--------------------------------------------------------------------------
222 // It appears that CStdString constructor replaces "\n" with "\r\n" which results
223 // in "\r\r\n" if there already is "\r\n", which in turn results in corrupted
224 // display. So this is an attempt to undo effects of a smart constructor. Also,
225 // replaces all nonprintable characters with "."
226 //--------------------------------------------------------------------------
227 /*void CExifParse::FixComment(CStdString& comment)
229 comment.Replace("\r\r\n", "\r\n");
230 for (unsigned int i=0; i<comment.length(); i++)
232 if ((comment[i] < 32) && (comment[i] != '\n') && (comment[i] != '\t') && (comment[i] != '\r'))
239 //--------------------------------------------------------------------------
240 // Evaluate number, be it int, rational, or float from directory.
241 //--------------------------------------------------------------------------
242 double CExifParse::ConvertAnyFormat(const void* const ValuePtr
, int Format
)
249 case FMT_SBYTE
: Value
= *(const signed char*)ValuePtr
; break;
250 case FMT_BYTE
: Value
= *(const unsigned char*)ValuePtr
; break;
252 case FMT_USHORT
: Value
= Get16(ValuePtr
, m_MotorolaOrder
); break;
253 case FMT_ULONG
: Value
= (unsigned)Get32(ValuePtr
, m_MotorolaOrder
); break;
259 Num
= Get32(ValuePtr
, m_MotorolaOrder
);
260 Den
= Get32(4+(const char *)ValuePtr
, m_MotorolaOrder
);
262 if (Den
== 0) Value
= 0;
263 else Value
= (double)Num
/Den
;
267 case FMT_SSHORT
: Value
= (signed short)Get16(ValuePtr
, m_MotorolaOrder
); break;
268 case FMT_SLONG
: Value
= Get32(ValuePtr
, m_MotorolaOrder
); break;
270 // Not sure if this is correct (never seen float used in Exif format)
271 case FMT_SINGLE
: Value
= (double)*(const float*)ValuePtr
; break;
272 case FMT_DOUBLE
: Value
= *(const double*)ValuePtr
; break;
275 ErrNonfatal("Illegal format code %d",Format
,0);
280 //--------------------------------------------------------------------------
281 // Exif date tag is stored as a fixed format string "YYYY:MM:DD HH:MM:SS".
282 // If date is not set, then the string is filled with blanks and colons:
283 // " : : : : ". We want this string localised.
284 //--------------------------------------------------------------------------
285 /*void CExifParse::LocaliseDate (void)
287 if (m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME][0] != ' ')
289 int year = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(0, 4).c_str());
290 int month = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(5, 2).c_str());
291 int day = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(8, 2).c_str());
292 int hour = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(11,2).c_str());
293 int min = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(14,2).c_str());
294 int sec = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(17,2).c_str());
295 CDateTime date(year, month, day, hour, min, sec);
296 m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME] = date.GetAsLocalizedDateTime();
301 //--------------------------------------------------------------------------
302 // Convert exposure time into a human readable format
303 //--------------------------------------------------------------------------
304 /*void CExifParse::GetExposureTime(const float exposureTime, CStdString& outStr)
308 if (exposureTime < 0.010) outStr.Format("%6.4fs ", exposureTime);
309 else outStr.Format("%5.3fs ", exposureTime);
310 if (exposureTime <= 0.5) outStr.Format("%s (1/%d)", outStr, (int)(0.5 + 1/exposureTime));
314 //--------------------------------------------------------------------------
315 // Process one of the nested EXIF directories.
316 //--------------------------------------------------------------------------
317 void CExifParse::ProcessDir(const unsigned char* const DirStart
,
318 const unsigned char* const OffsetBase
,
319 const unsigned ExifLength
,
322 if (NestingLevel
> 4)
324 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
328 char IndentString
[25];
329 memset(IndentString
, ' ', 25);
330 IndentString
[NestingLevel
* 4] = '\0';
333 int NumDirEntries
= Get16((const void*)DirStart
, m_MotorolaOrder
);
335 const unsigned char* const DirEnd
= DIR_ENTRY_ADDR(DirStart
, NumDirEntries
);
336 if (DirEnd
+4 > (OffsetBase
+ExifLength
))
338 if (DirEnd
+2 == OffsetBase
+ExifLength
|| DirEnd
== OffsetBase
+ExifLength
)
340 // Version 1.3 of jhead would truncate a bit too much.
341 // This also caught later on as well.
345 ErrNonfatal("Illegally sized directory", 0,0);
350 for (int de
=0;de
<NumDirEntries
;de
++)
352 int Tag
, Format
, Components
;
353 unsigned char* ValuePtr
;
355 const unsigned char* const DirEntry
= DIR_ENTRY_ADDR(DirStart
, de
);
357 Tag
= Get16(DirEntry
, m_MotorolaOrder
);
358 Format
= Get16(DirEntry
+2, m_MotorolaOrder
);
359 Components
= Get32(DirEntry
+4, m_MotorolaOrder
);
361 if (Format
<= 0 || Format
> NUM_FORMATS
)
363 ErrNonfatal("Illegal number format %d for tag %04x", Format
, Tag
);
367 if ((unsigned)Components
> 0x10000)
369 ErrNonfatal("Illegal number of components %d for tag %04x", Components
, Tag
);
373 ByteCount
= Components
* BytesPerFormat
[Format
- 1];
378 OffsetVal
= (unsigned)Get32(DirEntry
+8, m_MotorolaOrder
);
379 // If its bigger than 4 bytes, the dir entry contains an offset.
380 if (OffsetVal
> UINT32_MAX
- ByteCount
|| OffsetVal
+ ByteCount
> ExifLength
)
382 // Bogus pointer offset and / or bytecount value
383 ErrNonfatal("Illegal value pointer for tag %04x", Tag
,0);
386 ValuePtr
= (unsigned char*)(const_cast<unsigned char*>(OffsetBase
)+OffsetVal
);
388 if (OffsetVal
> m_LargestExifOffset
)
390 m_LargestExifOffset
= OffsetVal
;
395 // 4 bytes or less and value is in the dir entry itself
396 ValuePtr
= (unsigned char*)(const_cast<unsigned char*>(DirEntry
)+8);
400 // Extract useful components of tag
403 case TAG_DESCRIPTION
:
405 int length
= max(ByteCount
, 0);
406 length
= min(length
, MAX_COMMENT
);
407 strncpy(m_ExifInfo
->Description
, (char *)ValuePtr
, length
);
408 m_ExifInfo
->Description
[length
] = '\0';
413 int space
= sizeof(m_ExifInfo
->CameraMake
);
416 strncpy(m_ExifInfo
->CameraMake
, (char *)ValuePtr
, space
- 1);
417 m_ExifInfo
->CameraMake
[space
- 1] = '\0';
423 int space
= sizeof(m_ExifInfo
->CameraModel
);
426 strncpy(m_ExifInfo
->CameraModel
, (char *)ValuePtr
, space
- 1);
427 m_ExifInfo
->CameraModel
[space
- 1] = '\0';
431 // case TAG_SOFTWARE: strncpy(m_ExifInfo->Software, ValuePtr, 5); break;
432 case TAG_FOCALPLANEXRES
: m_FocalPlaneXRes
= ConvertAnyFormat(ValuePtr
, Format
); break;
433 case TAG_THUMBNAIL_OFFSET
: m_ExifInfo
->ThumbnailOffset
= (unsigned)ConvertAnyFormat(ValuePtr
, Format
); break;
434 case TAG_THUMBNAIL_LENGTH
: m_ExifInfo
->ThumbnailSize
= (unsigned)ConvertAnyFormat(ValuePtr
, Format
); break;
440 case TAG_DATETIME_ORIGINAL
:
443 int space
= sizeof(m_ExifInfo
->DateTime
);
446 strncpy(m_ExifInfo
->DateTime
, (char *)ValuePtr
, space
- 1);
447 m_ExifInfo
->DateTime
[space
- 1] = '\0';
448 // If we get a DATETIME_ORIGINAL, we use that one.
453 case TAG_DATETIME_DIGITIZED
:
458 // If we don't already have a DATETIME_ORIGINAL, use whatever
459 // time fields we may have.
460 int space
= sizeof(m_ExifInfo
->DateTime
);
463 strncpy(m_ExifInfo
->DateTime
, (char *)ValuePtr
, space
- 1);
464 m_ExifInfo
->DateTime
[space
- 1] = '\0';
469 case TAG_USERCOMMENT
:
471 // The UserComment allows comments without the charset limitations of ImageDescription.
472 // Therefore the UserComment field is prefixed by a CharacterCode field (8 Byte):
473 // - ASCII: 'ASCII\0\0\0'
474 // - Unicode: 'UNICODE\0'
475 // - JIS X208-1990: 'JIS\0\0\0\0\0'
476 // - Unknown: '\0\0\0\0\0\0\0\0' (application specific)
478 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_UNKNOWN
;
480 const int EXIF_COMMENT_CHARSET_LENGTH
= 8;
481 if (ByteCount
>= EXIF_COMMENT_CHARSET_LENGTH
)
483 // As some implementations use spaces instead of \0 for the padding,
484 // we're not so strict and check only the prefix.
485 if (memcmp(ValuePtr
, "ASCII", 5) == 0)
486 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_ASCII
;
487 else if (memcmp(ValuePtr
, "UNICODE", 7) == 0)
488 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_UNICODE
;
489 else if (memcmp(ValuePtr
, "JIS", 3) == 0)
490 m_ExifInfo
->CommentsCharset
= EXIF_COMMENT_CHARSET_JIS
;
492 int length
= ByteCount
- EXIF_COMMENT_CHARSET_LENGTH
;
493 length
= min(length
, MAX_COMMENT
);
494 memcpy(m_ExifInfo
->Comments
, ValuePtr
+ EXIF_COMMENT_CHARSET_LENGTH
, length
);
495 m_ExifInfo
->Comments
[length
] = '\0';
496 // FixComment(comment); // Ensure comment is printable
503 // The XP user comment field is always unicode (UCS-2) encoded
504 m_ExifInfo
->XPCommentsCharset
= EXIF_COMMENT_CHARSET_UNICODE
;
505 size_t length
= min(ByteCount
, MAX_COMMENT
);
506 memcpy(m_ExifInfo
->XPComment
, ValuePtr
, length
);
507 m_ExifInfo
->XPComment
[length
] = '\0';
512 // Simplest way of expressing aperture, so I trust it the most.
513 // (overwrite previously computd value if there is one)
514 m_ExifInfo
->ApertureFNumber
= (float)ConvertAnyFormat(ValuePtr
, Format
);
518 case TAG_MAXAPERTURE
:
519 // More relevant info always comes earlier, so only use this field if we don't
520 // have appropriate aperture information yet.
521 if (m_ExifInfo
->ApertureFNumber
== 0)
523 m_ExifInfo
->ApertureFNumber
= (float)exp(ConvertAnyFormat(ValuePtr
, Format
)*log(2.0)*0.5);
527 case TAG_FOCALLENGTH
:
528 // Nice digital cameras actually save the focal length as a function
529 // of how far they are zoomed in.
530 m_ExifInfo
->FocalLength
= (float)ConvertAnyFormat(ValuePtr
, Format
);
533 case TAG_SUBJECT_DISTANCE
:
534 // Inidcates the distacne the autofocus camera is focused to.
535 // Tends to be less accurate as distance increases.
537 float distance
= (float)ConvertAnyFormat(ValuePtr
, Format
);
538 m_ExifInfo
->Distance
= distance
;
542 case TAG_EXPOSURETIME
:
544 // Simplest way of expressing exposure time, so I trust it most.
545 // (overwrite previously computd value if there is one)
546 float expTime
= (float)ConvertAnyFormat(ValuePtr
, Format
);
548 m_ExifInfo
->ExposureTime
= expTime
;
552 case TAG_SHUTTERSPEED
:
553 // More complicated way of expressing exposure time, so only use
554 // this value if we don't already have it from somewhere else.
555 if (m_ExifInfo
->ExposureTime
== 0)
557 m_ExifInfo
->ExposureTime
= (float)(1/exp(ConvertAnyFormat(ValuePtr
, Format
)*log(2.0)));
562 m_ExifInfo
->FlashUsed
= (int)ConvertAnyFormat(ValuePtr
, Format
);
565 case TAG_ORIENTATION
:
566 m_ExifInfo
->Orientation
= (int)ConvertAnyFormat(ValuePtr
, Format
);
567 if (m_ExifInfo
->Orientation
< 0 || m_ExifInfo
->Orientation
> 8)
569 ErrNonfatal("Undefined rotation value %d", m_ExifInfo
->Orientation
, 0);
570 m_ExifInfo
->Orientation
= 0;
574 case TAG_EXIF_IMAGELENGTH
:
575 case TAG_EXIF_IMAGEWIDTH
:
576 // Use largest of height and width to deal with images that have been
577 // rotated to portrait format.
579 int a
= (int)ConvertAnyFormat(ValuePtr
, Format
);
580 if (m_ExifImageWidth
< a
) m_ExifImageWidth
= a
;
584 case TAG_FOCALPLANEUNITS
:
585 switch((int)ConvertAnyFormat(ValuePtr
, Format
))
587 // According to the information I was using, 2 means meters.
588 // But looking at the Cannon powershot's files, inches is the only
590 case 1: m_FocalPlaneUnits
= 25.4; break; // inch
591 case 2: m_FocalPlaneUnits
= 25.4; break;
592 case 3: m_FocalPlaneUnits
= 10; break; // centimeter
593 case 4: m_FocalPlaneUnits
= 1; break; // millimeter
594 case 5: m_FocalPlaneUnits
= .001; break; // micrometer
598 case TAG_EXPOSURE_BIAS
:
599 m_ExifInfo
->ExposureBias
= (float)ConvertAnyFormat(ValuePtr
, Format
);
602 case TAG_WHITEBALANCE
:
603 m_ExifInfo
->Whitebalance
= (int)ConvertAnyFormat(ValuePtr
, Format
);
606 case TAG_LIGHT_SOURCE
:
607 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
608 m_ExifInfo
->LightSource
= (int)ConvertAnyFormat(ValuePtr
, Format
);
611 case TAG_METERING_MODE
:
612 m_ExifInfo
->MeteringMode
= (int)ConvertAnyFormat(ValuePtr
, Format
);
615 case TAG_EXPOSURE_PROGRAM
:
616 m_ExifInfo
->ExposureProgram
= (int)ConvertAnyFormat(ValuePtr
, Format
);
619 case TAG_EXPOSURE_INDEX
:
620 if (m_ExifInfo
->ISOequivalent
== 0)
622 // Exposure index and ISO equivalent are often used interchangeably,
623 // so we will do the same.
624 // http://photography.about.com/library/glossary/bldef_ei.htm
625 m_ExifInfo
->ISOequivalent
= (int)ConvertAnyFormat(ValuePtr
, Format
);
629 case TAG_ISO_EQUIVALENT
:
630 m_ExifInfo
->ISOequivalent
= (int)ConvertAnyFormat(ValuePtr
, Format
);
631 if (m_ExifInfo
->ISOequivalent
< 50)
632 m_ExifInfo
->ISOequivalent
*= 200; // Fixes strange encoding on some older digicams.
635 case TAG_EXPOSURE_MODE
:
636 m_ExifInfo
->ExposureMode
= (int)ConvertAnyFormat(ValuePtr
, Format
);
639 case TAG_DIGITALZOOMRATIO
:
640 m_ExifInfo
->DigitalZoomRatio
= (float)ConvertAnyFormat(ValuePtr
, Format
);
643 case TAG_EXIF_OFFSET
:
644 case TAG_INTEROP_OFFSET
:
646 const unsigned char* const SubdirStart
= OffsetBase
+ (unsigned)Get32(ValuePtr
, m_MotorolaOrder
);
647 if (SubdirStart
< OffsetBase
|| SubdirStart
> OffsetBase
+ExifLength
)
649 ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
653 ProcessDir(SubdirStart
, OffsetBase
, ExifLength
, NestingLevel
+1);
661 const unsigned char* const SubdirStart
= OffsetBase
+ (unsigned)Get32(ValuePtr
, m_MotorolaOrder
);
662 if (SubdirStart
< OffsetBase
|| SubdirStart
> OffsetBase
+ExifLength
)
664 ErrNonfatal("Illegal GPS directory link",0,0);
668 ProcessGpsInfo(SubdirStart
, ByteCount
, OffsetBase
, ExifLength
);
674 case TAG_FOCALLENGTH_35MM
:
675 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
676 // if its present, use it to compute equivalent focal length instead of
677 // computing it from sensor geometry and actual focal length.
678 m_ExifInfo
->FocalLength35mmEquiv
= (unsigned)ConvertAnyFormat(ValuePtr
, Format
);
684 // In addition to linking to subdirectories via exif tags,
685 // there's also a potential link to another directory at the end of each
686 // directory. this has got to be the result of a committee!
689 if (DIR_ENTRY_ADDR(DirStart
, NumDirEntries
) + 4 <= OffsetBase
+ExifLength
)
691 Offset
= (unsigned)Get32(DirStart
+2+12*NumDirEntries
, m_MotorolaOrder
);
694 const unsigned char* const SubdirStart
= OffsetBase
+ Offset
;
695 if (SubdirStart
> OffsetBase
+ExifLength
|| SubdirStart
< OffsetBase
)
697 if (SubdirStart
> OffsetBase
&& SubdirStart
< OffsetBase
+ExifLength
+20)
699 // Jhead 1.3 or earlier would crop the whole directory!
700 // As Jhead produces this form of format incorrectness,
701 // I'll just let it pass silently
705 ErrNonfatal("Illegal subdirectory link",0,0);
710 if (SubdirStart
<= OffsetBase
+ExifLength
)
712 ProcessDir(SubdirStart
, OffsetBase
, ExifLength
, NestingLevel
+1);
715 if (Offset
> m_LargestExifOffset
)
717 m_LargestExifOffset
= Offset
;
723 // The exif header ends before the last next directory pointer.
726 if (m_ExifInfo
->ThumbnailOffset
)
728 m_ExifInfo
->ThumbnailAtEnd
= false;
730 if (m_ExifInfo
->ThumbnailOffset
<= ExifLength
)
732 if (m_ExifInfo
->ThumbnailSize
> ExifLength
- m_ExifInfo
->ThumbnailOffset
)
734 // If thumbnail extends past exif header, only save the part that
735 // actually exists. Canon's EOS viewer utility will do this - the
736 // thumbnail extracts ok with this hack.
737 m_ExifInfo
->ThumbnailSize
= ExifLength
- m_ExifInfo
->ThumbnailOffset
;
744 //--------------------------------------------------------------------------
745 // Process a EXIF marker
746 // Describes all the drivel that most digital cameras include...
747 //--------------------------------------------------------------------------
748 bool CExifParse::Process (const unsigned char* const ExifSection
, const unsigned short length
, ExifInfo_t
*info
)
751 // EXIF signature: "Exif\0\0"
752 // Check EXIF signatures
753 const char ExifHeader
[] = "Exif\0\0";
754 const char ExifAlignment0
[] = "II";
755 const char ExifAlignment1
[] = "MM";
756 const char ExifExtra
= 0x2a;
758 const char* pos
= (const char*)(ExifSection
+ sizeof(short)); // position data pointer after length field
760 if (memcmp(pos
, ExifHeader
,6))
762 printf("ExifParse: incorrect Exif header");
767 if (memcmp(pos
, ExifAlignment0
, strlen(ExifAlignment0
)) == 0)
769 m_MotorolaOrder
= false;
771 else if (memcmp(pos
, ExifAlignment1
, strlen(ExifAlignment1
)) == 0)
773 m_MotorolaOrder
= true;
777 printf("ExifParse: invalid Exif alignment marker");
780 pos
+= strlen(ExifAlignment0
);
782 // Check the next value for correctness.
783 if (Get16((const void*)(pos
), m_MotorolaOrder
) != ExifExtra
)
785 printf("ExifParse: invalid Exif start (1)");
788 pos
+= sizeof(short);
790 unsigned long FirstOffset
= (unsigned)Get32((const void*)pos
, m_MotorolaOrder
);
791 if (FirstOffset
< 8 || FirstOffset
+ 8 >= length
)
793 ErrNonfatal("Invalid offset of first IFD value: %u", FirstOffset
, 0);
799 // First directory starts 16 bytes in. All offset are relative to 8 bytes in.
800 ProcessDir(ExifSection
+8+FirstOffset
, ExifSection
+8, length
-8, 0);
802 m_ExifInfo
->ThumbnailAtEnd
= m_ExifInfo
->ThumbnailOffset
>= m_LargestExifOffset
;
804 // Compute the CCD width, in millimeters.
805 if (m_FocalPlaneXRes
!= 0)
807 // Note: With some cameras, its not possible to compute this correctly because
808 // they don't adjust the indicated focal plane resolution units when using less
809 // than maximum resolution, so the CCDWidth value comes out too small. Nothing
810 // that Jhead can do about it - its a camera problem.
811 m_ExifInfo
->CCDWidth
= (float)(m_ExifImageWidth
* m_FocalPlaneUnits
/ m_FocalPlaneXRes
);
814 if (m_ExifInfo
->FocalLength
)
816 if (m_ExifInfo
->FocalLength35mmEquiv
== 0)
818 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
819 // already got it explicitly from a tag.
820 if (m_ExifInfo
->CCDWidth
!= 0.0f
)
822 m_ExifInfo
->FocalLength35mmEquiv
=
823 (int)(m_ExifInfo
->FocalLength
/ m_ExifInfo
->CCDWidth
* 36 + 0.5f
);
832 //--------------------------------------------------------------------------
833 // GPS Lat/Long extraction helper function
834 //--------------------------------------------------------------------------
835 void CExifParse::GetLatLong(
836 const unsigned int Format
,
837 const unsigned char* ValuePtr
,
838 const int ComponentSize
,
841 if (Format
!= FMT_URATIONAL
)
843 ErrNonfatal("Illegal number format %d for GPS Lat/Long", Format
, 0);
848 for (unsigned a
=0; a
<3 ;a
++)
850 Values
[a
] = ConvertAnyFormat(ValuePtr
+a
*ComponentSize
, Format
);
852 if (Values
[0] < 0 || Values
[0] > 180 || Values
[1] < 0 || Values
[1] >= 60 || Values
[2] < 0 || Values
[2] >= 60)
854 // Ignore invalid values (DMS format expected)
855 ErrNonfatal("Invalid Lat/Long value", 0, 0);
856 latLongString
[0] = 0;
861 sprintf(latLong
, "%3.0fd %2.0f' %5.2f\"", Values
[0], Values
[1], Values
[2]);
862 strcat(latLongString
, latLong
);
867 //--------------------------------------------------------------------------
868 // Process GPS info directory
869 //--------------------------------------------------------------------------
870 void CExifParse::ProcessGpsInfo(
871 const unsigned char* const DirStart
,
873 const unsigned char* const OffsetBase
,
876 int NumDirEntries
= Get16(DirStart
, m_MotorolaOrder
);
878 for (int de
=0;de
<NumDirEntries
;de
++)
880 const unsigned char* DirEntry
= DIR_ENTRY_ADDR(DirStart
, de
);
882 // Fix from aosp 34a2564d3268a5ca1472c5076675782fbaf724d6
883 if (DirEntry
+ 12 > OffsetBase
+ ExifLength
)
885 ErrNonfatal("GPS info directory goes past end of exif", 0, 0);
889 unsigned Tag
= Get16(DirEntry
, m_MotorolaOrder
);
890 unsigned Format
= Get16(DirEntry
+2, m_MotorolaOrder
);
891 unsigned Components
= (unsigned)Get32(DirEntry
+4, m_MotorolaOrder
);
892 if (Format
== 0 || Format
> NUM_FORMATS
)
894 ErrNonfatal("Illegal number format %d for tag %04x", Format
, Tag
);
898 unsigned ComponentSize
= BytesPerFormat
[Format
- 1];
899 unsigned ByteCount
= Components
* ComponentSize
;
901 const unsigned char* ValuePtr
;
905 unsigned OffsetVal
= (unsigned)Get32(DirEntry
+8, m_MotorolaOrder
);
906 // If its bigger than 4 bytes, the dir entry contains an offset.
907 if (OffsetVal
> UINT32_MAX
- ByteCount
|| OffsetVal
+ ByteCount
> ExifLength
)
909 // Bogus pointer offset and / or bytecount value
910 ErrNonfatal("Illegal value pointer for tag %04x", Tag
,0);
913 ValuePtr
= OffsetBase
+OffsetVal
;
917 // 4 bytes or less and value is in the dir entry itself
918 ValuePtr
= DirEntry
+8;
923 case TAG_GPS_LAT_REF
:
924 m_ExifInfo
->GpsLat
[0] = ValuePtr
[0];
925 m_ExifInfo
->GpsLat
[1] = 0;
928 case TAG_GPS_LONG_REF
:
929 m_ExifInfo
->GpsLong
[0] = ValuePtr
[0];
930 m_ExifInfo
->GpsLong
[1] = 0;
934 GetLatLong(Format
, ValuePtr
, ComponentSize
, m_ExifInfo
->GpsLat
);
937 GetLatLong(Format
, ValuePtr
, ComponentSize
, m_ExifInfo
->GpsLong
);
940 case TAG_GPS_ALT_REF
:
941 if (ValuePtr
[0] != 0)
942 m_ExifInfo
->GpsAlt
[0] = '-';
943 m_ExifInfo
->GpsAlt
[1] = 0;
949 sprintf(temp
, "%.2fm", static_cast<double>(ConvertAnyFormat(ValuePtr
, Format
)));
950 strcat(m_ExifInfo
->GpsAlt
, temp
);