Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Implements a EXIF directory reader
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam
9 : * Copyright (c) 2012,2017, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Portions Copyright (c) Her majesty the Queen in right of Canada as
12 : * represented by the Minister of National Defence, 2006.
13 : *
14 : * SPDX-License-Identifier: MIT
15 : ****************************************************************************/
16 :
17 : #include "cpl_port.h"
18 : #include "gdal_priv.h"
19 : #include "gdalexif.h"
20 :
21 : #include <climits>
22 : #include <cmath>
23 : #include <cstddef>
24 : #include <cstdio>
25 : #include <cstring>
26 : #if HAVE_FCNTL_H
27 : #include <fcntl.h>
28 : #endif
29 :
30 : #include <algorithm>
31 : #include <limits>
32 : #include <vector>
33 :
34 : #include "cpl_conv.h"
35 : #include "cpl_error.h"
36 : #include "cpl_string.h"
37 : #include "cpl_vsi.h"
38 :
39 : using std::vector;
40 :
41 : constexpr int MAXSTRINGLENGTH = 65535;
42 : constexpr int EXIFOFFSETTAG = 0x8769;
43 : constexpr int INTEROPERABILITYOFFSET = 0xA005;
44 : constexpr int GPSOFFSETTAG = 0x8825;
45 :
46 : constexpr GUInt16 TAG_SIZE = 12;
47 : constexpr GUInt16 EXIF_HEADER_SIZE = 6;
48 :
49 : constexpr char COND_MANDATORY = 'M';
50 : constexpr char COND_RECOMMENDED = 'R';
51 : constexpr char COND_OPTIONAL = 'O';
52 : constexpr char COND_NOT_ALLOWED = 'N';
53 : constexpr char COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER = 'J';
54 :
55 : struct EXIFTagDesc
56 : {
57 : GUInt16 tag;
58 : GDALEXIFTIFFDataType datatype;
59 : GUInt32 length;
60 : const char *name;
61 : // comprCond is only used when DUMP_EXIF_ITEMS is defined
62 : // cppcheck-suppress unusedStructMember
63 : char comprCond;
64 : };
65 :
66 : static const EXIFTagDesc gpstags[] = {
67 : {0x00, TIFF_BYTE, 4, "EXIF_GPSVersionID", COND_OPTIONAL},
68 : {0x01, TIFF_ASCII, 2, "EXIF_GPSLatitudeRef", COND_OPTIONAL},
69 : {0x02, TIFF_RATIONAL, 3, "EXIF_GPSLatitude", COND_OPTIONAL},
70 : {0x03, TIFF_ASCII, 2, "EXIF_GPSLongitudeRef", COND_OPTIONAL},
71 : {0x04, TIFF_RATIONAL, 3, "EXIF_GPSLongitude", COND_OPTIONAL},
72 : {0x05, TIFF_BYTE, 1, "EXIF_GPSAltitudeRef", COND_OPTIONAL},
73 : {0x06, TIFF_RATIONAL, 1, "EXIF_GPSAltitude", COND_OPTIONAL},
74 : {0x07, TIFF_RATIONAL, 3, "EXIF_GPSTimeStamp", COND_OPTIONAL},
75 : {0x08, TIFF_ASCII, 0, "EXIF_GPSSatellites", COND_OPTIONAL},
76 : {0x09, TIFF_ASCII, 2, "EXIF_GPSStatus", COND_OPTIONAL},
77 : {0x0a, TIFF_ASCII, 2, "EXIF_GPSMeasureMode", COND_OPTIONAL},
78 : {0x0b, TIFF_RATIONAL, 1, "EXIF_GPSDOP", COND_OPTIONAL},
79 : {0x0c, TIFF_ASCII, 2, "EXIF_GPSSpeedRef", COND_OPTIONAL},
80 : {0x0d, TIFF_RATIONAL, 1, "EXIF_GPSSpeed", COND_OPTIONAL},
81 : {0x0e, TIFF_ASCII, 2, "EXIF_GPSTrackRef", COND_OPTIONAL},
82 : {0x0f, TIFF_RATIONAL, 1, "EXIF_GPSTrack", COND_OPTIONAL},
83 : {0x10, TIFF_ASCII, 2, "EXIF_GPSImgDirectionRef", COND_OPTIONAL},
84 : {0x11, TIFF_RATIONAL, 1, "EXIF_GPSImgDirection", COND_OPTIONAL},
85 : {0x12, TIFF_ASCII, 0, "EXIF_GPSMapDatum", COND_OPTIONAL},
86 : {0x13, TIFF_ASCII, 2, "EXIF_GPSDestLatitudeRef", COND_OPTIONAL},
87 : {0x14, TIFF_RATIONAL, 3, "EXIF_GPSDestLatitude", COND_OPTIONAL},
88 : {0x15, TIFF_ASCII, 2, "EXIF_GPSDestLongitudeRef", COND_OPTIONAL},
89 : {0x16, TIFF_RATIONAL, 3, "EXIF_GPSDestLongitude", COND_OPTIONAL},
90 : {0x17, TIFF_ASCII, 2, "EXIF_GPSDestBearingRef", COND_OPTIONAL},
91 : {0x18, TIFF_RATIONAL, 1, "EXIF_GPSDestBearing", COND_OPTIONAL},
92 : {0x19, TIFF_ASCII, 2, "EXIF_GPSDestDistanceRef", COND_OPTIONAL},
93 : {0x1a, TIFF_RATIONAL, 1, "EXIF_GPSDestDistance", COND_OPTIONAL},
94 : {0x1b, TIFF_UNDEFINED, 0, "EXIF_GPSProcessingMethod", COND_OPTIONAL},
95 : {0x1c, TIFF_UNDEFINED, 0, "EXIF_GPSAreaInformation", COND_OPTIONAL},
96 : {0x1d, TIFF_ASCII, 11, "EXIF_GPSDateStamp", COND_OPTIONAL},
97 : {0x1e, TIFF_SHORT, 1, "EXIF_GPSDifferential", COND_OPTIONAL},
98 : {0x1f, TIFF_RATIONAL, 1, "EXIF_GPSHPositioningError", COND_OPTIONAL},
99 : {0xffff, TIFF_NOTYPE, 0, "", COND_NOT_ALLOWED}};
100 :
101 : static const EXIFTagDesc exiftags[] = {
102 : //{ 0x100, "EXIF_Image_Width"},
103 : // { 0x101, "EXIF_Image_Length"},
104 : {0x102, TIFF_NOTYPE, 0, "EXIF_BitsPerSample",
105 : COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER},
106 : {0x103, TIFF_NOTYPE, 0, "EXIF_Compression",
107 : COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER},
108 : {0x106, TIFF_NOTYPE, 0, "EXIF_PhotometricInterpretation", COND_NOT_ALLOWED},
109 : {0x10A, TIFF_NOTYPE, 0, "EXIF_Fill_Order",
110 : COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER}, // not sure of cond
111 : {0x10D, TIFF_ASCII, 0, "EXIF_Document_Name",
112 : COND_OPTIONAL}, // not sure of cond
113 : {0x10E, TIFF_ASCII, 0, "EXIF_ImageDescription", COND_RECOMMENDED},
114 : {0x10F, TIFF_ASCII, 0, "EXIF_Make", COND_RECOMMENDED},
115 : {0x110, TIFF_ASCII, 0, "EXIF_Model", COND_RECOMMENDED},
116 : {0x111, TIFF_NOTYPE, 0, "EXIF_StripOffsets", COND_NOT_ALLOWED},
117 : {0x112, TIFF_SHORT, 1, "EXIF_Orientation", COND_RECOMMENDED},
118 : {0x115, TIFF_NOTYPE, 0, "EXIF_SamplesPerPixel",
119 : COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER},
120 : {0x116, TIFF_NOTYPE, 0, "EXIF_RowsPerStrip", COND_NOT_ALLOWED},
121 : {0x117, TIFF_NOTYPE, 0, "EXIF_StripByteCounts", COND_NOT_ALLOWED},
122 : {0x11A, TIFF_RATIONAL, 1, "EXIF_XResolution", COND_MANDATORY},
123 : {0x11B, TIFF_RATIONAL, 1, "EXIF_YResolution", COND_MANDATORY},
124 : {0x11C, TIFF_NOTYPE, 0, "EXIF_PlanarConfiguration",
125 : COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER},
126 : {0x128, TIFF_SHORT, 1, "EXIF_ResolutionUnit", COND_MANDATORY},
127 : {0x12D, TIFF_SHORT, 768, "EXIF_TransferFunction", COND_OPTIONAL},
128 : {0x131, TIFF_ASCII, 0, "EXIF_Software", COND_OPTIONAL},
129 : {0x132, TIFF_ASCII, 20, "EXIF_DateTime", COND_RECOMMENDED},
130 : {0x13B, TIFF_ASCII, 0, "EXIF_Artist", COND_OPTIONAL},
131 : {0x13E, TIFF_RATIONAL, 2, "EXIF_WhitePoint", COND_OPTIONAL},
132 : {0x13F, TIFF_RATIONAL, 6, "EXIF_PrimaryChromaticities", COND_OPTIONAL},
133 : {0x156, TIFF_NOTYPE, 0, "EXIF_Transfer_Range",
134 : COND_NOT_ALLOWED}, // not sure of cond
135 : {0x200, TIFF_NOTYPE, 0, "EXIF_JPEG_Proc",
136 : COND_NOT_ALLOWED}, // not sure of cond
137 : {0x201, TIFF_NOTYPE, 0, "EXIF_JPEGInterchangeFormat", COND_NOT_ALLOWED},
138 : {0x202, TIFF_NOTYPE, 0, "EXIF_JPEGInterchangeFormatLength",
139 : COND_NOT_ALLOWED},
140 : {0x211, TIFF_RATIONAL, 3, "EXIF_YCbCrCoefficients", COND_OPTIONAL},
141 : {0x212, TIFF_NOTYPE, 0, "EXIF_YCbCrSubSampling",
142 : COND_NOT_ALLOWED_EVEN_IN_JPEG_MARKER},
143 : {0x213, TIFF_SHORT, 1, "EXIF_YCbCrPositioning", COND_MANDATORY},
144 : {0x214, TIFF_RATIONAL, 6, "EXIF_ReferenceBlackWhite", COND_OPTIONAL},
145 : {0x2BC, TIFF_ASCII, 0, "EXIF_XmlPacket",
146 : COND_OPTIONAL}, // not in the EXIF standard. But found in some images
147 : {0x828D, TIFF_NOTYPE, 0, "EXIF_CFA_Repeat_Pattern_Dim", COND_OPTIONAL},
148 : {0x828E, TIFF_NOTYPE, 0, "EXIF_CFA_Pattern", COND_OPTIONAL},
149 : {0x828F, TIFF_NOTYPE, 0, "EXIF_Battery_Level", COND_OPTIONAL},
150 : {0x8298, TIFF_ASCII, 0, "EXIF_Copyright",
151 : COND_OPTIONAL}, // that one is an exception: high tag number, but should
152 : // go to main IFD
153 : {0x829A, TIFF_RATIONAL, 1, "EXIF_ExposureTime", COND_RECOMMENDED},
154 : {0x829D, TIFF_RATIONAL, 1, "EXIF_FNumber", COND_OPTIONAL},
155 : {0x83BB, TIFF_NOTYPE, 0, "EXIF_IPTC/NAA", COND_OPTIONAL},
156 : // { 0x8769, "EXIF_Offset"},
157 : {0x8773, TIFF_NOTYPE, 0, "EXIF_Inter_Color_Profile", COND_OPTIONAL},
158 : {0x8822, TIFF_SHORT, 1, "EXIF_ExposureProgram", COND_OPTIONAL},
159 : {0x8824, TIFF_ASCII, 0, "EXIF_SpectralSensitivity", COND_OPTIONAL},
160 : // { 0x8825, "EXIF_GPSOffset"},
161 : {0x8827, TIFF_SHORT, 0, "EXIF_ISOSpeedRatings", COND_OPTIONAL},
162 : {0x8828, TIFF_UNDEFINED, 0, "EXIF_OECF", COND_OPTIONAL},
163 : {0x8830, TIFF_SHORT, 1, "EXIF_SensitivityType", COND_OPTIONAL},
164 : {0x8831, TIFF_LONG, 1, "EXIF_StandardOutputSensitivity", COND_OPTIONAL},
165 : {0x8832, TIFF_LONG, 1, "EXIF_RecommendedExposureIndex", COND_OPTIONAL},
166 : {0x8833, TIFF_LONG, 1, "EXIF_ISOSpeed", COND_OPTIONAL},
167 : {0x8834, TIFF_LONG, 1, "EXIF_ISOSpeedLatitudeyyy", COND_OPTIONAL},
168 : {0x8835, TIFF_LONG, 1, "EXIF_ISOSpeedLatitudezzz", COND_OPTIONAL},
169 : {0x9000, TIFF_UNDEFINED, 4, "EXIF_ExifVersion", COND_MANDATORY},
170 : {0x9003, TIFF_ASCII, 20, "EXIF_DateTimeOriginal", COND_OPTIONAL},
171 : {0x9004, TIFF_ASCII, 20, "EXIF_DateTimeDigitized", COND_OPTIONAL},
172 : {0x9010, TIFF_ASCII, 7, "EXIF_OffsetTime", COND_OPTIONAL},
173 : {0x9011, TIFF_ASCII, 7, "EXIF_OffsetTimeOriginal", COND_OPTIONAL},
174 : {0x9012, TIFF_ASCII, 7, "EXIF_OffsetTimeDigitized", COND_OPTIONAL},
175 : {0x9101, TIFF_UNDEFINED, 4, "EXIF_ComponentsConfiguration", COND_MANDATORY},
176 : {0x9102, TIFF_RATIONAL, 1, "EXIF_CompressedBitsPerPixel", COND_OPTIONAL},
177 : {0x9201, TIFF_SRATIONAL, 1, "EXIF_ShutterSpeedValue", COND_OPTIONAL},
178 : {0x9202, TIFF_RATIONAL, 1, "EXIF_ApertureValue", COND_OPTIONAL},
179 : {0x9203, TIFF_SRATIONAL, 1, "EXIF_BrightnessValue", COND_OPTIONAL},
180 : {0x9204, TIFF_SRATIONAL, 1, "EXIF_ExposureBiasValue", COND_OPTIONAL},
181 : {0x9205, TIFF_RATIONAL, 1, "EXIF_MaxApertureValue", COND_OPTIONAL},
182 : {0x9206, TIFF_RATIONAL, 1, "EXIF_SubjectDistance", COND_OPTIONAL},
183 : {0x9207, TIFF_SHORT, 1, "EXIF_MeteringMode", COND_OPTIONAL},
184 : {0x9208, TIFF_SHORT, 1, "EXIF_LightSource", COND_OPTIONAL},
185 : {0x9209, TIFF_SHORT, 1, "EXIF_Flash", COND_RECOMMENDED},
186 : {0x920A, TIFF_RATIONAL, 1, "EXIF_FocalLength", COND_OPTIONAL},
187 : {0x9214, TIFF_SHORT, 0, "EXIF_SubjectArea",
188 : COND_OPTIONAL}, // count = 2, 3 or 4
189 : {0x927C, TIFF_UNDEFINED, 0, "EXIF_MakerNote", COND_OPTIONAL},
190 : {0x9286, TIFF_UNDEFINED, 0, "EXIF_UserComment", COND_OPTIONAL},
191 : {0x9290, TIFF_ASCII, 0, "EXIF_SubSecTime", COND_OPTIONAL},
192 : {0x9291, TIFF_ASCII, 0, "EXIF_SubSecTime_Original", COND_OPTIONAL},
193 : {0x9292, TIFF_ASCII, 0, "EXIF_SubSecTime_Digitized", COND_OPTIONAL},
194 : {0xA000, TIFF_UNDEFINED, 4, "EXIF_FlashpixVersion", COND_MANDATORY},
195 : {0xA001, TIFF_SHORT, 1, "EXIF_ColorSpace", COND_MANDATORY},
196 : {0xA002, TIFF_LONG, 1, "EXIF_PixelXDimension",
197 : COND_MANDATORY}, // SHORT also OK
198 : {0xA003, TIFF_LONG, 1, "EXIF_PixelYDimension",
199 : COND_MANDATORY}, // SHORT also OK
200 : {0xA004, TIFF_ASCII, 13, "EXIF_RelatedSoundFile", COND_OPTIONAL},
201 : // { 0xA005, "EXIF_InteroperabilityOffset"},
202 : {0xA20B, TIFF_RATIONAL, 1, "EXIF_FlashEnergy",
203 : COND_OPTIONAL}, // 0x920B in TIFF/EP
204 : {0xA20C, TIFF_UNDEFINED, 0, "EXIF_SpatialFrequencyResponse",
205 : COND_OPTIONAL}, // 0x920C - -
206 : {0xA20E, TIFF_RATIONAL, 1, "EXIF_FocalPlaneXResolution",
207 : COND_OPTIONAL}, // 0x920E - -
208 : {0xA20F, TIFF_RATIONAL, 1, "EXIF_FocalPlaneYResolution",
209 : COND_OPTIONAL}, // 0x920F - -
210 : {0xA210, TIFF_SHORT, 1, "EXIF_FocalPlaneResolutionUnit",
211 : COND_OPTIONAL}, // 0x9210 - -
212 : {0xA214, TIFF_SHORT, 2, "EXIF_SubjectLocation",
213 : COND_OPTIONAL}, // 0x9214 - -
214 : {0xA215, TIFF_RATIONAL, 1, "EXIF_ExposureIndex",
215 : COND_OPTIONAL}, // 0x9215 - -
216 : {0xA217, TIFF_SHORT, 1, "EXIF_SensingMethod", COND_OPTIONAL}, // 0x9217 - -
217 : {0xA300, TIFF_UNDEFINED, 1, "EXIF_FileSource", COND_OPTIONAL},
218 : {0xA301, TIFF_UNDEFINED, 1, "EXIF_SceneType", COND_OPTIONAL},
219 : {0xA302, TIFF_UNDEFINED, 0, "EXIF_CFAPattern", COND_OPTIONAL},
220 : {0xA401, TIFF_SHORT, 1, "EXIF_CustomRendered", COND_OPTIONAL},
221 : {0xA402, TIFF_SHORT, 1, "EXIF_ExposureMode", COND_RECOMMENDED},
222 : {0XA403, TIFF_SHORT, 1, "EXIF_WhiteBalance", COND_RECOMMENDED},
223 : {0xA404, TIFF_RATIONAL, 1, "EXIF_DigitalZoomRatio", COND_OPTIONAL},
224 : {0xA405, TIFF_SHORT, 1, "EXIF_FocalLengthIn35mmFilm", COND_OPTIONAL},
225 : {0xA406, TIFF_SHORT, 1, "EXIF_SceneCaptureType", COND_RECOMMENDED},
226 : {0xA407, TIFF_RATIONAL, 1, "EXIF_GainControl", COND_OPTIONAL},
227 : {0xA408, TIFF_SHORT, 1, "EXIF_Contrast", COND_OPTIONAL},
228 : {0xA409, TIFF_SHORT, 1, "EXIF_Saturation", COND_OPTIONAL},
229 : {0xA40A, TIFF_SHORT, 1, "EXIF_Sharpness", COND_OPTIONAL},
230 : {0xA40B, TIFF_UNDEFINED, 0, "EXIF_DeviceSettingDescription", COND_OPTIONAL},
231 : {0xA40C, TIFF_SHORT, 1, "EXIF_SubjectDistanceRange", COND_OPTIONAL},
232 : {0xA420, TIFF_ASCII, 33, "EXIF_ImageUniqueID", COND_OPTIONAL},
233 : {0xA430, TIFF_ASCII, 0, "EXIF_CameraOwnerName", COND_OPTIONAL},
234 : {0xA431, TIFF_ASCII, 0, "EXIF_BodySerialNumber", COND_OPTIONAL},
235 : {0xA432, TIFF_RATIONAL, 4, "EXIF_LensSpecification", COND_OPTIONAL},
236 : {0xA433, TIFF_ASCII, 0, "EXIF_LensMake", COND_OPTIONAL},
237 : {0xA434, TIFF_ASCII, 0, "EXIF_LensModel", COND_OPTIONAL},
238 : {0xA435, TIFF_ASCII, 0, "EXIF_LensSerialNumber", COND_OPTIONAL},
239 : {0x0000, TIFF_NOTYPE, 0, "", COND_NOT_ALLOWED}};
240 :
241 : static const struct intr_tag
242 : {
243 : GInt16 tag;
244 : const char *name;
245 : } intr_tags[] = {
246 :
247 : {0x1, "EXIF_Interoperability_Index"},
248 : {0x2, "EXIF_Interoperability_Version"},
249 : {0x1000, "EXIF_Related_Image_File_Format"},
250 : {0x1001, "EXIF_Related_Image_Width"},
251 : {0x1002, "EXIF_Related_Image_Length"},
252 : {0x0000, ""}};
253 :
254 : static const EXIFTagDesc IFD0Tags[] = {
255 : {0xC614, TIFF_ASCII, 0, "DNG_UniqueCameraModel", COND_OPTIONAL},
256 : {0xC62F, TIFF_ASCII, 0, "DNG_CameraSerialNumber", COND_OPTIONAL},
257 : {0x0000, TIFF_NOTYPE, 0, "", COND_NOT_ALLOWED}};
258 :
259 : /************************************************************************/
260 : /* EXIFPrintData() */
261 : /************************************************************************/
262 535 : static void EXIFPrintData(char *pszData, GUInt16 type, GUInt32 count,
263 : const unsigned char *data)
264 : {
265 535 : const char *sep = "";
266 : char szTemp[128];
267 535 : char *pszDataEnd = pszData;
268 :
269 535 : pszData[0] = '\0';
270 :
271 535 : switch (type)
272 : {
273 :
274 203 : case TIFF_UNDEFINED:
275 : case TIFF_BYTE:
276 203 : for (; count > 0; count--)
277 : {
278 162 : snprintf(szTemp, sizeof(szTemp), "%s0x%02x", sep, *data);
279 162 : data++;
280 162 : sep = " ";
281 162 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
282 0 : break;
283 162 : strcat(pszDataEnd, szTemp);
284 162 : pszDataEnd += strlen(pszDataEnd);
285 : }
286 41 : break;
287 :
288 0 : case TIFF_SBYTE:
289 0 : for (; count > 0; count--)
290 : {
291 0 : snprintf(szTemp, sizeof(szTemp), "%s%d", sep,
292 0 : *reinterpret_cast<const char *>(data));
293 0 : data++;
294 0 : sep = " ";
295 0 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
296 0 : break;
297 0 : strcat(pszDataEnd, szTemp);
298 0 : pszDataEnd += strlen(pszDataEnd);
299 : }
300 0 : break;
301 :
302 250 : case TIFF_ASCII:
303 250 : memcpy(pszData, data, count);
304 : // Strip trailing spaces or nul characters
305 637 : while (count > 0 &&
306 589 : (pszData[count - 1] == ' ' || pszData[count - 1] == 0))
307 387 : --count;
308 250 : pszData[count] = '\0';
309 250 : break;
310 :
311 85 : case TIFF_SHORT:
312 : {
313 85 : const GUInt16 *wp = reinterpret_cast<const GUInt16 *>(data);
314 939 : for (; count > 0; count--)
315 : {
316 854 : snprintf(szTemp, sizeof(szTemp), "%s%u", sep, *wp);
317 854 : wp++;
318 854 : sep = " ";
319 854 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
320 0 : break;
321 854 : strcat(pszDataEnd, szTemp);
322 854 : pszDataEnd += strlen(pszDataEnd);
323 : }
324 85 : break;
325 : }
326 0 : case TIFF_SSHORT:
327 : {
328 0 : const GInt16 *wp = reinterpret_cast<const GInt16 *>(data);
329 0 : for (; count > 0; count--)
330 : {
331 0 : snprintf(szTemp, sizeof(szTemp), "%s%d", sep, *wp);
332 0 : wp++;
333 0 : sep = " ";
334 0 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
335 0 : break;
336 0 : strcat(pszDataEnd, szTemp);
337 0 : pszDataEnd += strlen(pszDataEnd);
338 : }
339 0 : break;
340 : }
341 23 : case TIFF_LONG:
342 : {
343 23 : const GUInt32 *lp = reinterpret_cast<const GUInt32 *>(data);
344 46 : for (; count > 0; count--)
345 : {
346 23 : snprintf(szTemp, sizeof(szTemp), "%s%u", sep, *lp);
347 23 : lp++;
348 23 : sep = " ";
349 23 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
350 0 : break;
351 23 : strcat(pszDataEnd, szTemp);
352 23 : pszDataEnd += strlen(pszDataEnd);
353 : }
354 23 : break;
355 : }
356 0 : case TIFF_SLONG:
357 : {
358 0 : const GInt32 *lp = reinterpret_cast<const GInt32 *>(data);
359 0 : for (; count > 0; count--)
360 : {
361 0 : snprintf(szTemp, sizeof(szTemp), "%s%d", sep, *lp);
362 0 : lp++;
363 0 : sep = " ";
364 0 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
365 0 : break;
366 0 : strcat(pszDataEnd, szTemp);
367 0 : pszDataEnd += strlen(pszDataEnd);
368 : }
369 0 : break;
370 : }
371 129 : case TIFF_RATIONAL:
372 : {
373 129 : const GUInt32 *lp = reinterpret_cast<const GUInt32 *>(data);
374 : // if(bSwabflag)
375 : // TIFFSwabArrayOfLong((GUInt32*) data, 2*count);
376 380 : for (; count > 0; count--)
377 : {
378 251 : if ((lp[0] == 0) || (lp[1] == 0))
379 : {
380 9 : snprintf(szTemp, sizeof(szTemp), "%s(0)", sep);
381 : }
382 : else
383 : {
384 242 : CPLsnprintf(szTemp, sizeof(szTemp), "%s(%g)", sep,
385 242 : static_cast<double>(lp[0]) /
386 242 : static_cast<double>(lp[1]));
387 : }
388 251 : sep = " ";
389 251 : lp += 2;
390 251 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
391 0 : break;
392 251 : strcat(pszDataEnd, szTemp);
393 251 : pszDataEnd += strlen(pszDataEnd);
394 : }
395 129 : break;
396 : }
397 7 : case TIFF_SRATIONAL:
398 : {
399 7 : const GInt32 *lp = reinterpret_cast<const GInt32 *>(data);
400 14 : for (; count > 0; count--)
401 : {
402 7 : if ((lp[0] == 0) || (lp[1] == 0))
403 : {
404 1 : snprintf(szTemp, sizeof(szTemp), "%s(0)", sep);
405 : }
406 : else
407 : {
408 6 : CPLsnprintf(szTemp, sizeof(szTemp), "%s(%g)", sep,
409 6 : static_cast<double>(lp[0]) /
410 6 : static_cast<double>(lp[1]));
411 : }
412 7 : sep = " ";
413 7 : lp += 2;
414 7 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
415 0 : break;
416 7 : strcat(pszDataEnd, szTemp);
417 7 : pszDataEnd += strlen(pszDataEnd);
418 : }
419 7 : break;
420 : }
421 0 : case TIFF_FLOAT:
422 : {
423 0 : const float *fp = reinterpret_cast<const float *>(data);
424 0 : for (; count > 0; count--)
425 : {
426 0 : CPLsnprintf(szTemp, sizeof(szTemp), "%s%g", sep, *fp);
427 0 : fp++;
428 0 : sep = " ";
429 0 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
430 0 : break;
431 0 : strcat(pszDataEnd, szTemp);
432 0 : pszDataEnd += strlen(pszDataEnd);
433 : }
434 0 : break;
435 : }
436 0 : case TIFF_DOUBLE:
437 : {
438 0 : const double *dp = reinterpret_cast<const double *>(data);
439 0 : for (; count > 0; count--)
440 : {
441 0 : CPLsnprintf(szTemp, sizeof(szTemp), "%s%g", sep, *dp);
442 0 : dp++;
443 0 : sep = " ";
444 0 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
445 0 : break;
446 0 : strcat(pszDataEnd, szTemp);
447 0 : pszDataEnd += strlen(pszDataEnd);
448 : }
449 0 : break;
450 : }
451 :
452 0 : default:
453 0 : return;
454 : }
455 :
456 535 : if (type != TIFF_ASCII && count != 0)
457 : {
458 0 : CPLError(CE_Warning, CPLE_AppDefined, "EXIF metadata truncated");
459 : }
460 : }
461 :
462 : /*
463 : * Return size of TIFFDataType in bytes
464 : */
465 535 : static int EXIF_TIFFDataWidth(int /* GDALEXIFTIFFDataType */ type)
466 : {
467 535 : switch (type)
468 : {
469 291 : case 0: /* nothing */
470 : case TIFF_BYTE:
471 : case TIFF_ASCII:
472 : case TIFF_SBYTE:
473 : case TIFF_UNDEFINED:
474 291 : return 1;
475 85 : case TIFF_SHORT:
476 : case TIFF_SSHORT:
477 85 : return 2;
478 23 : case TIFF_LONG:
479 : case TIFF_SLONG:
480 : case TIFF_FLOAT:
481 : case TIFF_IFD:
482 23 : return 4;
483 136 : case TIFF_RATIONAL:
484 : case TIFF_SRATIONAL:
485 : case TIFF_DOUBLE:
486 : // case TIFF_LONG8:
487 : // case TIFF_SLONG8:
488 : // case TIFF_IFD8:
489 136 : return 8;
490 0 : default:
491 0 : return 0; /* will return 0 for unknown types */
492 : }
493 : }
494 :
495 : /************************************************************************/
496 : /* EXIFExtractMetadata() */
497 : /* */
498 : /* Extract all entry from a IFD */
499 : /************************************************************************/
500 148 : CPLErr EXIFExtractMetadata(char **&papszMetadata, void *fpInL, int nOffset,
501 : int bSwabflag, int nTIFFHEADER, int &nExifOffset,
502 : int &nInterOffset, int &nGPSOffset)
503 : {
504 : /* -------------------------------------------------------------------- */
505 : /* Read number of entry in directory */
506 : /* -------------------------------------------------------------------- */
507 : GUInt16 nEntryCount;
508 148 : VSILFILE *const fp = static_cast<VSILFILE *>(fpInL);
509 :
510 444 : if (nOffset > INT_MAX - nTIFFHEADER ||
511 296 : VSIFSeekL(fp, nOffset + nTIFFHEADER, SEEK_SET) != 0 ||
512 148 : VSIFReadL(&nEntryCount, 1, sizeof(GUInt16), fp) != sizeof(GUInt16))
513 : {
514 0 : CPLError(CE_Failure, CPLE_AppDefined,
515 : "Error reading EXIF Directory count at " CPL_FRMT_GUIB,
516 0 : static_cast<vsi_l_offset>(nOffset) + nTIFFHEADER);
517 0 : return CE_Failure;
518 : }
519 :
520 148 : if (bSwabflag)
521 7 : CPL_SWAP16PTR(&nEntryCount);
522 :
523 : // Some apps write empty directories - see bug 1523.
524 148 : if (nEntryCount == 0)
525 1 : return CE_None;
526 :
527 : // Some files are corrupt, a large entry count is a sign of this.
528 147 : if (nEntryCount > 125)
529 : {
530 1 : CPLError(CE_Warning, CPLE_AppDefined,
531 : "Ignoring EXIF directory with unlikely entry count (%d).",
532 : nEntryCount);
533 1 : return CE_Warning;
534 : }
535 :
536 : GDALEXIFTIFFDirEntry *poTIFFDir = static_cast<GDALEXIFTIFFDirEntry *>(
537 146 : CPLMalloc(nEntryCount * sizeof(GDALEXIFTIFFDirEntry)));
538 :
539 : /* -------------------------------------------------------------------- */
540 : /* Read all directory entries */
541 : /* -------------------------------------------------------------------- */
542 : {
543 292 : const unsigned int n = static_cast<int>(VSIFReadL(
544 146 : poTIFFDir, 1, nEntryCount * sizeof(GDALEXIFTIFFDirEntry), fp));
545 146 : if (n != nEntryCount * sizeof(GDALEXIFTIFFDirEntry))
546 : {
547 0 : CPLError(CE_Failure, CPLE_AppDefined,
548 : "Could not read all directories");
549 0 : CPLFree(poTIFFDir);
550 0 : return CE_Failure;
551 : }
552 : }
553 :
554 : /* -------------------------------------------------------------------- */
555 : /* Parse all entry information in this directory */
556 : /* -------------------------------------------------------------------- */
557 146 : vector<char> oTempStorage(MAXSTRINGLENGTH + 1, 0);
558 146 : char *const szTemp = &oTempStorage[0];
559 :
560 : char szName[128];
561 :
562 146 : GDALEXIFTIFFDirEntry *poTIFFDirEntry = poTIFFDir;
563 :
564 757 : for (unsigned int i = nEntryCount; i > 0; i--, poTIFFDirEntry++)
565 : {
566 611 : if (bSwabflag)
567 : {
568 31 : CPL_SWAP16PTR(&poTIFFDirEntry->tdir_tag);
569 31 : CPL_SWAP16PTR(&poTIFFDirEntry->tdir_type);
570 31 : CPL_SWAP32PTR(&poTIFFDirEntry->tdir_count);
571 31 : CPL_SWAP32PTR(&poTIFFDirEntry->tdir_offset);
572 : }
573 :
574 : /* --------------------------------------------------------------------
575 : */
576 : /* Find Tag name in table */
577 : /* --------------------------------------------------------------------
578 : */
579 611 : szName[0] = '\0';
580 611 : szTemp[0] = '\0';
581 :
582 41046 : for (const EXIFTagDesc *poExifTags = exiftags; poExifTags->tag;
583 : poExifTags++)
584 : {
585 40812 : if (poExifTags->tag == poTIFFDirEntry->tdir_tag)
586 : {
587 377 : CPLAssert(nullptr != poExifTags->name);
588 :
589 377 : CPLStrlcpy(szName, poExifTags->name, sizeof(szName));
590 377 : break;
591 : }
592 : }
593 :
594 611 : if (szName[0] == 0)
595 : {
596 696 : for (const EXIFTagDesc *poTag = IFD0Tags; poTag->tag; poTag++)
597 : {
598 466 : if (poTag->tag == poTIFFDirEntry->tdir_tag)
599 : {
600 4 : CPLAssert(nullptr != poTag->name);
601 :
602 4 : CPLStrlcpy(szName, poTag->name, sizeof(szName));
603 4 : break;
604 : }
605 : }
606 : }
607 :
608 611 : if (nOffset == nGPSOffset)
609 : {
610 459 : for (const EXIFTagDesc *poGPSTags = gpstags;
611 459 : poGPSTags->tag != 0xffff; poGPSTags++)
612 : {
613 459 : if (poGPSTags->tag == poTIFFDirEntry->tdir_tag)
614 : {
615 154 : CPLAssert(nullptr != poGPSTags->name);
616 154 : CPLStrlcpy(szName, poGPSTags->name, sizeof(szName));
617 154 : break;
618 : }
619 : }
620 : }
621 : /* --------------------------------------------------------------------
622 : */
623 : /* If the tag was not found, look into the interoperability table
624 : */
625 : /* --------------------------------------------------------------------
626 : */
627 611 : if (nOffset == nInterOffset)
628 : {
629 0 : const struct intr_tag *poInterTags = intr_tags;
630 0 : for (; poInterTags->tag; poInterTags++)
631 0 : if (poInterTags->tag == poTIFFDirEntry->tdir_tag)
632 : {
633 0 : CPLAssert(nullptr != poInterTags->name);
634 0 : CPLStrlcpy(szName, poInterTags->name, sizeof(szName));
635 0 : break;
636 : }
637 : }
638 :
639 : /* --------------------------------------------------------------------
640 : */
641 : /* Save important directory tag offset */
642 : /* --------------------------------------------------------------------
643 : */
644 :
645 : // Our current API uses int32 and not uint32
646 611 : if (poTIFFDirEntry->tdir_offset < INT_MAX)
647 : {
648 609 : if (poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG)
649 : {
650 46 : nExifOffset = poTIFFDirEntry->tdir_offset;
651 46 : continue;
652 : }
653 563 : else if (poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET)
654 : {
655 0 : nInterOffset = poTIFFDirEntry->tdir_offset;
656 0 : continue;
657 : }
658 563 : else if (poTIFFDirEntry->tdir_tag == GPSOFFSETTAG)
659 : {
660 30 : nGPSOffset = poTIFFDirEntry->tdir_offset;
661 30 : continue;
662 : }
663 : }
664 :
665 : /* ----------------------------------------------------------------- */
666 : /* If we didn't recognise the tag, report it as CPLDebug() */
667 : /* ----------------------------------------------------------------- */
668 535 : bool bUnknownTag = false;
669 535 : if (szName[0] == '\0')
670 : {
671 0 : snprintf(szName, sizeof(szName), "EXIF_%u",
672 0 : poTIFFDirEntry->tdir_tag);
673 0 : bUnknownTag = true;
674 : }
675 :
676 535 : vsi_l_offset nTagValueOffset = poTIFFDirEntry->tdir_offset;
677 :
678 : /* --------------------------------------------------------------------
679 : */
680 : /* For UserComment we need to ignore the language binding and */
681 : /* just return the actual contents. */
682 : /* --------------------------------------------------------------------
683 : */
684 535 : if (EQUAL(szName, "EXIF_UserComment"))
685 : {
686 0 : poTIFFDirEntry->tdir_type = TIFF_ASCII;
687 :
688 0 : if (poTIFFDirEntry->tdir_count >= 8)
689 : {
690 0 : poTIFFDirEntry->tdir_count -= 8;
691 0 : nTagValueOffset += 8;
692 : }
693 : }
694 :
695 : /* --------------------------------------------------------------------
696 : */
697 : /* Make some UNDEFINED or BYTE fields ASCII for readability. */
698 : /* --------------------------------------------------------------------
699 : */
700 535 : if (EQUAL(szName, "EXIF_ExifVersion") ||
701 498 : EQUAL(szName, "EXIF_FlashPixVersion") ||
702 470 : EQUAL(szName, "EXIF_MakerNote") ||
703 469 : EQUAL(szName, "GPSProcessingMethod") ||
704 469 : EQUAL(szName, "EXIF_XmlPacket"))
705 66 : poTIFFDirEntry->tdir_type = TIFF_ASCII;
706 :
707 : /* --------------------------------------------------------------------
708 : */
709 : /* Print tags */
710 : /* --------------------------------------------------------------------
711 : */
712 535 : if (poTIFFDirEntry->tdir_count > static_cast<GUInt32>(MAXSTRINGLENGTH))
713 : {
714 0 : CPLError(CE_Warning, CPLE_AppDefined,
715 : "Too many bytes in tag: %u, ignoring tag.",
716 : poTIFFDirEntry->tdir_count);
717 0 : continue;
718 : }
719 :
720 535 : const int nDataWidth = EXIF_TIFFDataWidth(poTIFFDirEntry->tdir_type);
721 535 : if (nDataWidth == 0 || poTIFFDirEntry->tdir_type >= TIFF_IFD)
722 : {
723 0 : CPLError(CE_Warning, CPLE_AppDefined,
724 : "Invalid or unhandled EXIF data type: %d, ignoring tag.",
725 0 : poTIFFDirEntry->tdir_type);
726 0 : continue;
727 : }
728 :
729 : /* --------------------------------------------------------------------
730 : */
731 : /* This is at most 4 byte data so we can read it from tdir_offset
732 : */
733 : /* --------------------------------------------------------------------
734 : */
735 535 : const int space = poTIFFDirEntry->tdir_count * nDataWidth;
736 535 : if (space >= 0 && space <= 4)
737 : {
738 :
739 : unsigned char data[4];
740 323 : memcpy(data, &poTIFFDirEntry->tdir_offset, 4);
741 323 : if (bSwabflag)
742 : {
743 : GUInt32 nValUInt32;
744 : // Unswab 32bit value, and reswab per data type.
745 14 : memcpy(&nValUInt32, data, 4);
746 14 : CPL_SWAP32PTR(&nValUInt32);
747 14 : memcpy(data, &nValUInt32, 4);
748 :
749 14 : switch (poTIFFDirEntry->tdir_type)
750 : {
751 0 : case TIFF_LONG:
752 : case TIFF_SLONG:
753 : case TIFF_FLOAT:
754 0 : memcpy(&nValUInt32, data, 4);
755 0 : CPL_SWAP32PTR(&nValUInt32);
756 0 : memcpy(data, &nValUInt32, 4);
757 0 : break;
758 :
759 8 : case TIFF_SSHORT:
760 : case TIFF_SHORT:
761 16 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
762 : j++)
763 : {
764 8 : CPL_SWAP16PTR(reinterpret_cast<GUInt16 *>(data) +
765 : j);
766 : }
767 8 : break;
768 :
769 6 : default:
770 6 : break;
771 : }
772 : }
773 :
774 : /* coverity[overrun-buffer-arg] */
775 323 : EXIFPrintData(szTemp, poTIFFDirEntry->tdir_type,
776 323 : poTIFFDirEntry->tdir_count, data);
777 : }
778 : /* --------------------------------------------------------------------
779 : */
780 : /* The data is being read where tdir_offset point to in the file */
781 : /* --------------------------------------------------------------------
782 : */
783 212 : else if (space > 0 && space < MAXSTRINGLENGTH)
784 : {
785 : unsigned char *data =
786 212 : static_cast<unsigned char *>(VSIMalloc(space));
787 :
788 212 : if (data)
789 : {
790 212 : CPL_IGNORE_RET_VAL(
791 212 : VSIFSeekL(fp, nTagValueOffset + nTIFFHEADER, SEEK_SET));
792 212 : CPL_IGNORE_RET_VAL(VSIFReadL(data, 1, space, fp));
793 :
794 212 : if (bSwabflag)
795 : {
796 14 : switch (poTIFFDirEntry->tdir_type)
797 : {
798 0 : case TIFF_SHORT:
799 : case TIFF_SSHORT:
800 : {
801 0 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
802 : j++)
803 : {
804 0 : CPL_SWAP16PTR(
805 : reinterpret_cast<GUInt16 *>(data) + j);
806 : }
807 0 : break;
808 : }
809 0 : case TIFF_LONG:
810 : case TIFF_SLONG:
811 : case TIFF_FLOAT:
812 : {
813 0 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
814 : j++)
815 : {
816 0 : CPL_SWAP32PTR(
817 : reinterpret_cast<GUInt32 *>(data) + j);
818 : }
819 0 : break;
820 : }
821 6 : case TIFF_RATIONAL:
822 : case TIFF_SRATIONAL:
823 : {
824 6 : for (unsigned j = 0;
825 18 : j < 2 * poTIFFDirEntry->tdir_count; j++)
826 : {
827 12 : CPL_SWAP32PTR(
828 : reinterpret_cast<GUInt32 *>(data) + j);
829 : }
830 6 : break;
831 : }
832 0 : case TIFF_DOUBLE:
833 : {
834 0 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
835 : j++)
836 : {
837 0 : CPL_SWAPDOUBLE(
838 : reinterpret_cast<double *>(data) + j);
839 : }
840 0 : break;
841 : }
842 8 : default:
843 8 : break;
844 : }
845 : }
846 :
847 212 : EXIFPrintData(szTemp, poTIFFDirEntry->tdir_type,
848 : poTIFFDirEntry->tdir_count, data);
849 212 : CPLFree(data);
850 212 : }
851 : }
852 : else
853 : {
854 0 : CPLError(CE_Warning, CPLE_AppDefined,
855 : "Invalid EXIF header size: %ld, ignoring tag.",
856 : static_cast<long>(space));
857 : }
858 :
859 535 : if (bUnknownTag)
860 0 : CPLDebug("EXIF", "Ignoring %s=%s", szName, szTemp);
861 : else
862 535 : papszMetadata = CSLSetNameValue(papszMetadata, szName, szTemp);
863 : }
864 146 : CPLFree(poTIFFDir);
865 :
866 146 : return CE_None;
867 : }
868 :
869 : /************************************************************************/
870 : /* WriteLEUInt16() */
871 : /************************************************************************/
872 :
873 575 : static void WriteLEUInt16(GByte *pabyData, GUInt32 &nBufferOff, GUInt16 nVal)
874 : {
875 575 : pabyData[nBufferOff] = static_cast<GByte>(nVal & 0xff);
876 575 : pabyData[nBufferOff + 1] = static_cast<GByte>(nVal >> 8);
877 575 : nBufferOff += 2;
878 575 : }
879 :
880 : /************************************************************************/
881 : /* WriteLEUInt32() */
882 : /************************************************************************/
883 :
884 627 : static void WriteLEUInt32(GByte *pabyData, GUInt32 &nBufferOff, GUInt32 nVal)
885 : {
886 627 : pabyData[nBufferOff] = static_cast<GByte>(nVal & 0xff);
887 627 : pabyData[nBufferOff + 1] = static_cast<GByte>((nVal >> 8) & 0xff);
888 627 : pabyData[nBufferOff + 2] = static_cast<GByte>((nVal >> 16) & 0xff);
889 627 : pabyData[nBufferOff + 3] = static_cast<GByte>(nVal >> 24);
890 627 : nBufferOff += 4;
891 627 : }
892 :
893 : /************************************************************************/
894 : /* GetHexValue() */
895 : /************************************************************************/
896 :
897 195 : static int GetHexValue(char ch)
898 : {
899 195 : const char chDEC_ZERO = '0';
900 195 : if (ch >= chDEC_ZERO && ch <= '9')
901 183 : return ch - chDEC_ZERO;
902 12 : if (ch >= 'a' && ch <= 'f')
903 5 : return ch - 'a' + 10;
904 7 : if (ch >= 'A' && ch <= 'F')
905 7 : return ch - 'A' + 10;
906 0 : return -1;
907 : }
908 :
909 : /************************************************************************/
910 : /* ParseUndefined() */
911 : /************************************************************************/
912 :
913 32 : static GByte *ParseUndefined(const char *pszVal, GUInt32 *pnLength)
914 : {
915 32 : GUInt32 nSize = 0;
916 32 : bool bIsHexExcaped = true;
917 32 : const char chDEC_ZERO = '0';
918 32 : GByte *pabyData = reinterpret_cast<GByte *>(CPLMalloc(strlen(pszVal) + 1));
919 :
920 : // Is it a hexadecimal string like "0xA 0x1E 00 0xDF..." ?
921 60 : for (size_t i = 0; pszVal[i] != '\0';)
922 : {
923 : // 0xA
924 97 : if (pszVal[i] == chDEC_ZERO && pszVal[i + 1] == 'x' &&
925 157 : GetHexValue(pszVal[i + 2]) >= 0 &&
926 39 : (pszVal[i + 3] == ' ' || pszVal[i + 3] == '\0'))
927 : {
928 0 : pabyData[nSize] = static_cast<GByte>(GetHexValue(pszVal[i + 2]));
929 0 : nSize++;
930 0 : if (pszVal[i + 3] == '\0')
931 0 : break;
932 0 : i += 4;
933 : }
934 : // 0xAA
935 97 : else if (pszVal[i] == chDEC_ZERO && pszVal[i + 1] == 'x' &&
936 78 : GetHexValue(pszVal[i + 2]) >= 0 &&
937 157 : GetHexValue(pszVal[i + 3]) >= 0 &&
938 39 : (pszVal[i + 4] == ' ' || pszVal[i + 4] == '\0'))
939 : {
940 39 : pabyData[nSize] = static_cast<GByte>(
941 39 : GetHexValue(pszVal[i + 2]) * 16 + GetHexValue(pszVal[i + 3]));
942 39 : nSize++;
943 39 : if (pszVal[i + 4] == '\0')
944 11 : break;
945 28 : i += 5;
946 : }
947 : // 00
948 21 : else if (pszVal[i] == chDEC_ZERO && pszVal[i + 1] == chDEC_ZERO &&
949 0 : (pszVal[i + 2] == ' ' || pszVal[i + 2] == '\0'))
950 : {
951 0 : pabyData[nSize] = 0;
952 0 : nSize++;
953 0 : if (pszVal[i + 2] == '\0')
954 0 : break;
955 0 : i += 3;
956 : }
957 : else
958 : {
959 21 : bIsHexExcaped = false;
960 21 : break;
961 : }
962 : }
963 :
964 32 : if (bIsHexExcaped)
965 : {
966 11 : *pnLength = nSize;
967 11 : return pabyData;
968 : }
969 :
970 : // Otherwise take the string value as a byte value
971 21 : memcpy(pabyData, pszVal, strlen(pszVal) + 1);
972 21 : *pnLength = static_cast<GUInt32>(strlen(pszVal));
973 21 : return pabyData;
974 : }
975 :
976 : /************************************************************************/
977 : /* EXIFTagSort() */
978 : /************************************************************************/
979 :
980 : struct TagValue
981 : {
982 : GUInt16 tag;
983 : GDALEXIFTIFFDataType datatype;
984 : GByte *pabyVal;
985 : GUInt32 nLength;
986 : GUInt32 nLengthBytes;
987 : int nRelOffset;
988 : };
989 :
990 226 : static bool EXIFTagSort(const TagValue &a, const TagValue &b)
991 : {
992 226 : return a.tag <= b.tag;
993 : }
994 :
995 : /************************************************************************/
996 : /* GetNumDenomFromDouble() */
997 : /************************************************************************/
998 :
999 80 : static bool GetNumDenomFromDouble(GDALEXIFTIFFDataType datatype, double dfVal,
1000 : GUInt32 &nNum, GUInt32 &nDenom)
1001 : {
1002 80 : nNum = 0;
1003 80 : nDenom = 1;
1004 80 : if (std::isnan(dfVal))
1005 : {
1006 1 : return false;
1007 : }
1008 79 : else if (datatype == TIFF_RATIONAL)
1009 : {
1010 72 : if (dfVal < 0)
1011 : {
1012 1 : return false;
1013 : }
1014 142 : else if (dfVal <= std::numeric_limits<unsigned int>::max() &&
1015 71 : dfVal == static_cast<GUInt32>(dfVal))
1016 : {
1017 54 : nNum = static_cast<GUInt32>(dfVal);
1018 54 : nDenom = 1;
1019 : }
1020 17 : else if (dfVal < 1.0)
1021 : {
1022 1 : nNum = static_cast<GUInt32>(
1023 1 : dfVal * std::numeric_limits<unsigned int>::max());
1024 1 : nDenom = std::numeric_limits<unsigned int>::max();
1025 : }
1026 : else
1027 : {
1028 16 : nNum = std::numeric_limits<unsigned int>::max();
1029 16 : nDenom = static_cast<GUInt32>(
1030 16 : std::numeric_limits<unsigned int>::max() / dfVal);
1031 : }
1032 : }
1033 7 : else if (dfVal < 0.0)
1034 : {
1035 6 : if (dfVal >= std::numeric_limits<int>::min() &&
1036 3 : dfVal == static_cast<GInt32>(dfVal))
1037 : {
1038 1 : nNum = static_cast<GInt32>(dfVal);
1039 1 : nDenom = 1;
1040 : }
1041 2 : else if (dfVal > -1.0)
1042 : {
1043 2 : nNum = -static_cast<GInt32>((-dfVal) *
1044 1 : std::numeric_limits<int>::max());
1045 1 : nDenom = std::numeric_limits<int>::max();
1046 : }
1047 : else
1048 : {
1049 1 : nNum = -std::numeric_limits<int>::max();
1050 1 : nDenom =
1051 1 : static_cast<GInt32>(std::numeric_limits<int>::max() / (-dfVal));
1052 : }
1053 : }
1054 : else
1055 : {
1056 8 : if (dfVal <= std::numeric_limits<int>::max() &&
1057 4 : dfVal == static_cast<GInt32>(dfVal))
1058 : {
1059 2 : nNum = static_cast<GInt32>(dfVal);
1060 2 : nDenom = 1;
1061 : }
1062 2 : else if (dfVal < 1.0)
1063 : {
1064 1 : nNum = static_cast<GInt32>(dfVal * std::numeric_limits<int>::max());
1065 1 : nDenom = std::numeric_limits<int>::max();
1066 : }
1067 : else
1068 : {
1069 1 : nNum = std::numeric_limits<int>::max();
1070 1 : nDenom =
1071 1 : static_cast<GInt32>(std::numeric_limits<int>::max() / dfVal);
1072 : }
1073 : }
1074 78 : return true;
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* EXIFFormatTagValue() */
1079 : /************************************************************************/
1080 :
1081 : enum class EXIFLocation
1082 : {
1083 : MAIN_IFD,
1084 : EXIF_IFD,
1085 : GPS_IFD
1086 : };
1087 :
1088 90 : static std::vector<TagValue> EXIFFormatTagValue(char **papszEXIFMetadata,
1089 : EXIFLocation location,
1090 : GUInt32 *pnOfflineSize)
1091 : {
1092 90 : std::vector<TagValue> tags;
1093 90 : int nRelOffset = 0;
1094 90 : const EXIFTagDesc *tagdescArray =
1095 90 : (location == EXIFLocation::GPS_IFD) ? gpstags : exiftags;
1096 :
1097 561 : for (char **papszIter = papszEXIFMetadata; papszIter && *papszIter;
1098 : ++papszIter)
1099 : {
1100 471 : if (!STARTS_WITH_CI(*papszIter, "EXIF_"))
1101 316 : continue;
1102 465 : if (location == EXIFLocation::GPS_IFD &&
1103 155 : !STARTS_WITH_CI(*papszIter, "EXIF_GPS"))
1104 111 : continue;
1105 354 : if (location != EXIFLocation::GPS_IFD &&
1106 310 : STARTS_WITH_CI(*papszIter, "EXIF_GPS"))
1107 88 : continue;
1108 :
1109 266 : bool bFound = false;
1110 266 : size_t i = 0; // needed after loop
1111 9985 : for (; tagdescArray[i].name[0] != '\0'; i++)
1112 : {
1113 9983 : if (STARTS_WITH_CI(*papszIter, tagdescArray[i].name) &&
1114 312 : (*papszIter)[strlen(tagdescArray[i].name)] == '=')
1115 : {
1116 264 : bFound = true;
1117 264 : break;
1118 : }
1119 : }
1120 :
1121 266 : if (location == EXIFLocation::MAIN_IFD)
1122 : {
1123 111 : if (tagdescArray[i].tag > 0x8298) // EXIF_Copyright
1124 : {
1125 70 : continue;
1126 : }
1127 : }
1128 155 : else if (location == EXIFLocation::EXIF_IFD)
1129 : {
1130 111 : if (tagdescArray[i].tag <= 0x8298) // EXIF_Copyright
1131 : {
1132 41 : continue;
1133 : }
1134 : }
1135 :
1136 155 : char *pszKey = nullptr;
1137 155 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
1138 155 : if (!bFound || pszKey == nullptr || pszValue == nullptr)
1139 : {
1140 1 : CPLError(CE_Warning, CPLE_NotSupported,
1141 1 : "Cannot write unknown %s tag", pszKey ? pszKey : "");
1142 : }
1143 154 : else if (tagdescArray[i].datatype == TIFF_NOTYPE)
1144 : {
1145 0 : CPLDebug("EXIF", "Tag %s ignored on write", tagdescArray[i].name);
1146 : }
1147 : else
1148 : {
1149 : TagValue tag;
1150 154 : tag.tag = tagdescArray[i].tag;
1151 154 : tag.datatype = tagdescArray[i].datatype;
1152 154 : tag.pabyVal = nullptr;
1153 154 : tag.nLength = 0;
1154 154 : tag.nLengthBytes = 0;
1155 154 : tag.nRelOffset = -1;
1156 :
1157 154 : if (tag.datatype == TIFF_ASCII)
1158 : {
1159 50 : if (tagdescArray[i].length == 0 ||
1160 36 : strlen(pszValue) + 1 == tagdescArray[i].length)
1161 : {
1162 34 : tag.pabyVal =
1163 34 : reinterpret_cast<GByte *>(CPLStrdup(pszValue));
1164 34 : tag.nLength = 1 + static_cast<int>(strlen(pszValue));
1165 : }
1166 16 : else if (strlen(pszValue) >= tagdescArray[i].length)
1167 : {
1168 1 : CPLError(CE_Warning, CPLE_AppDefined,
1169 : "Value of %s will be truncated",
1170 1 : tagdescArray[i].name);
1171 1 : tag.pabyVal = reinterpret_cast<GByte *>(
1172 1 : CPLMalloc(tagdescArray[i].length));
1173 1 : memcpy(tag.pabyVal, pszValue, tagdescArray[i].length);
1174 1 : tag.nLength = tagdescArray[i].length;
1175 1 : tag.pabyVal[tag.nLength - 1] = '\0';
1176 : }
1177 : else
1178 : {
1179 15 : tag.pabyVal = reinterpret_cast<GByte *>(
1180 15 : CPLMalloc(tagdescArray[i].length));
1181 15 : memset(tag.pabyVal, ' ', tagdescArray[i].length);
1182 15 : memcpy(tag.pabyVal, pszValue, strlen(pszValue));
1183 15 : tag.nLength = tagdescArray[i].length;
1184 15 : tag.pabyVal[tag.nLength - 1] = '\0';
1185 : }
1186 50 : tag.nLengthBytes = tag.nLength;
1187 : }
1188 104 : else if (tag.datatype == TIFF_BYTE ||
1189 96 : tag.datatype == TIFF_UNDEFINED)
1190 : {
1191 32 : GUInt32 nValLength = 0;
1192 32 : GByte *pabyVal = ParseUndefined(pszValue, &nValLength);
1193 32 : if (tagdescArray[i].length == 0 ||
1194 30 : nValLength == tagdescArray[i].length)
1195 : {
1196 30 : if (tag.tag == 0x9286 &&
1197 1 : strncmp(pszValue, "0x", 2) != 0) // EXIF_UserComment
1198 : {
1199 1 : const char *pszRealVal =
1200 : reinterpret_cast<char *>(pabyVal);
1201 1 : const int nValueLen =
1202 1 : static_cast<int>(strlen(pszRealVal));
1203 : // 8 first bytes are the character code
1204 : // Set them to 0 to mean undefined
1205 1 : tag.pabyVal =
1206 1 : static_cast<GByte *>(CPLCalloc(1, 8 + nValueLen));
1207 1 : tag.nLength = 8 + nValueLen;
1208 1 : memcpy(tag.pabyVal + 8, pszRealVal, nValueLen);
1209 1 : CPLFree(pabyVal);
1210 : }
1211 : else
1212 : {
1213 29 : tag.pabyVal = pabyVal;
1214 29 : tag.nLength = nValLength;
1215 30 : }
1216 : }
1217 2 : else if (nValLength > tagdescArray[i].length)
1218 : {
1219 1 : CPLError(CE_Warning, CPLE_AppDefined,
1220 : "Value of %s will be truncated",
1221 1 : tagdescArray[i].name);
1222 1 : tag.pabyVal = pabyVal;
1223 1 : tag.nLength = tagdescArray[i].length;
1224 : }
1225 : else
1226 : {
1227 1 : tag.pabyVal = reinterpret_cast<GByte *>(
1228 1 : CPLRealloc(pabyVal, tagdescArray[i].length));
1229 1 : memset(tag.pabyVal + nValLength, '\0',
1230 1 : tagdescArray[i].length - nValLength);
1231 1 : tag.nLength = tagdescArray[i].length;
1232 : }
1233 32 : tag.nLengthBytes = tag.nLength;
1234 : }
1235 72 : else if (tag.datatype == TIFF_SHORT || tag.datatype == TIFF_LONG)
1236 : {
1237 26 : char **papszTokens = CSLTokenizeString2(pszValue, " ", 0);
1238 26 : GUInt32 nTokens = static_cast<GUInt32>(CSLCount(papszTokens));
1239 26 : const GUInt32 nDataTypeSize =
1240 26 : (tag.datatype == TIFF_SHORT) ? 2 : 4;
1241 26 : if (tagdescArray[i].length == 0 ||
1242 25 : nTokens == tagdescArray[i].length)
1243 : {
1244 : // ok
1245 : }
1246 2 : else if (nTokens > tagdescArray[i].length)
1247 : {
1248 1 : CPLError(CE_Warning, CPLE_AppDefined,
1249 : "Value of %s will be truncated",
1250 1 : tagdescArray[i].name);
1251 : }
1252 : else
1253 : {
1254 1 : CPLError(CE_Warning, CPLE_AppDefined,
1255 : "Not enough values for %s: %d expected. "
1256 : "Filling with zeroes",
1257 1 : tagdescArray[i].name, tagdescArray[i].length);
1258 : }
1259 :
1260 52 : tag.nLength = (tagdescArray[i].length == 0)
1261 26 : ? nTokens
1262 25 : : tagdescArray[i].length;
1263 26 : tag.pabyVal = reinterpret_cast<GByte *>(CPLCalloc(
1264 26 : 1, cpl::fits_on<int>(nDataTypeSize * tag.nLength)));
1265 :
1266 26 : GUInt32 nOffset = 0;
1267 55 : for (GUInt32 j = 0; j < std::min(nTokens, tag.nLength); j++)
1268 : {
1269 29 : GUInt32 nVal = atoi(papszTokens[j]);
1270 29 : if (tag.datatype == TIFF_SHORT)
1271 14 : WriteLEUInt16(tag.pabyVal, nOffset,
1272 : static_cast<GUInt16>(nVal));
1273 : else
1274 15 : WriteLEUInt32(tag.pabyVal, nOffset, nVal);
1275 : }
1276 26 : CSLDestroy(papszTokens);
1277 :
1278 26 : tag.nLengthBytes = tag.nLength * nDataTypeSize;
1279 : }
1280 46 : else if (tag.datatype == TIFF_RATIONAL ||
1281 7 : tag.datatype == TIFF_SRATIONAL)
1282 : {
1283 46 : char **papszTokens = CSLTokenizeString2(pszValue, " ", 0);
1284 46 : GUInt32 nTokens = static_cast<GUInt32>(CSLCount(papszTokens));
1285 46 : const GUInt32 nDataTypeSize = 8;
1286 46 : if (tagdescArray[i].length == 0 ||
1287 46 : nTokens == tagdescArray[i].length)
1288 : {
1289 : // ok
1290 : }
1291 1 : else if (nTokens > tagdescArray[i].length)
1292 : {
1293 1 : CPLError(CE_Warning, CPLE_AppDefined,
1294 : "Value of %s will be truncated",
1295 1 : tagdescArray[i].name);
1296 : }
1297 : else
1298 : {
1299 0 : CPLError(CE_Warning, CPLE_AppDefined,
1300 : "Not enough values for %s: %d expected. "
1301 : "Filling with zeroes",
1302 0 : tagdescArray[i].name, tagdescArray[i].length);
1303 : }
1304 :
1305 92 : tag.nLength = (tagdescArray[i].length == 0)
1306 46 : ? nTokens
1307 46 : : tagdescArray[i].length;
1308 46 : tag.pabyVal = reinterpret_cast<GByte *>(
1309 46 : CPLCalloc(1, nDataTypeSize * tag.nLength));
1310 :
1311 46 : GUInt32 nOffset = 0;
1312 126 : for (GUInt32 j = 0; j < std::min(nTokens, tag.nLength); j++)
1313 : {
1314 : double dfVal =
1315 98 : CPLAtof(papszTokens[j][0] == '(' ? papszTokens[j] + 1
1316 18 : : papszTokens[j]);
1317 80 : GUInt32 nNum = 1;
1318 80 : GUInt32 nDenom = 0;
1319 80 : if (!GetNumDenomFromDouble(tag.datatype, dfVal, nNum,
1320 : nDenom))
1321 : {
1322 2 : CPLError(CE_Warning, CPLE_AppDefined,
1323 : "Value %f is illegal for tag %s", dfVal,
1324 2 : tagdescArray[i].name);
1325 : }
1326 :
1327 80 : WriteLEUInt32(tag.pabyVal, nOffset, nNum);
1328 80 : WriteLEUInt32(tag.pabyVal, nOffset, nDenom);
1329 : }
1330 46 : CSLDestroy(papszTokens);
1331 :
1332 46 : tag.nLengthBytes = tag.nLength * nDataTypeSize;
1333 : }
1334 : else
1335 : {
1336 : // Shouldn't happen. Programming error
1337 0 : CPLError(CE_Warning, CPLE_NotSupported,
1338 0 : "Unhandled type %d for tag %s", tag.datatype,
1339 0 : tagdescArray[i].name);
1340 : }
1341 :
1342 154 : if (tag.nLengthBytes != 0)
1343 : {
1344 154 : if (tag.nLengthBytes > 4)
1345 : {
1346 68 : tag.nRelOffset = nRelOffset;
1347 68 : nRelOffset += tag.nLengthBytes + (tag.nLengthBytes % 1);
1348 : }
1349 154 : tags.push_back(tag);
1350 : }
1351 : }
1352 155 : CPLFree(pszKey);
1353 : }
1354 :
1355 : // Sort tags by ascending order
1356 90 : std::sort(tags.begin(), tags.end(), EXIFTagSort);
1357 :
1358 : #ifdef notdef
1359 : if (location == EXIF_IFD &&
1360 : CSLFetchNameValue(papszEXIFMetadata, "EXIF_ExifVersion") == nullptr)
1361 : {
1362 : const GUInt16 EXIF_VERSION = 0x9000;
1363 : TagValue tag;
1364 : tag.tag = EXIF_VERSION;
1365 : tag.datatype = TIFF_UNDEFINED;
1366 : tag.pabyVal = reinterpret_cast<GByte *>(CPLStrdup("0231"));
1367 : tag.nLength = 4;
1368 : tag.nLengthBytes = 4;
1369 : tag.nRelOffset = -1;
1370 : tags.push_back(tag);
1371 : }
1372 : #endif
1373 :
1374 90 : *pnOfflineSize = nRelOffset;
1375 :
1376 90 : return tags;
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* WriteTag() */
1381 : /************************************************************************/
1382 :
1383 64 : static void WriteTag(GByte *pabyData, GUInt32 &nBufferOff, GUInt16 nTag,
1384 : GDALEXIFTIFFDataType nType, GUInt32 nCount, GUInt32 nVal)
1385 : {
1386 64 : WriteLEUInt16(pabyData, nBufferOff, nTag);
1387 64 : WriteLEUInt16(pabyData, nBufferOff, static_cast<GUInt16>(nType));
1388 64 : WriteLEUInt32(pabyData, nBufferOff, nCount);
1389 64 : WriteLEUInt32(pabyData, nBufferOff, nVal);
1390 64 : }
1391 :
1392 : /************************************************************************/
1393 : /* WriteTags() */
1394 : /************************************************************************/
1395 :
1396 42 : static void WriteTags(GByte *pabyData, GUInt32 &nBufferOff,
1397 : GUInt32 offsetIFDData, const std::vector<TagValue> &tags)
1398 : {
1399 195 : for (const auto &tag : tags)
1400 : {
1401 153 : WriteLEUInt16(pabyData, nBufferOff, tag.tag);
1402 153 : WriteLEUInt16(pabyData, nBufferOff, static_cast<GUInt16>(tag.datatype));
1403 153 : WriteLEUInt32(pabyData, nBufferOff, tag.nLength);
1404 153 : if (tag.nRelOffset < 0)
1405 : {
1406 86 : CPLAssert(tag.nLengthBytes <= 4);
1407 86 : memcpy(pabyData + nBufferOff, tag.pabyVal, tag.nLengthBytes);
1408 86 : nBufferOff += 4;
1409 : }
1410 : else
1411 : {
1412 67 : WriteLEUInt32(pabyData, nBufferOff, tag.nRelOffset + offsetIFDData);
1413 67 : memcpy(pabyData + EXIF_HEADER_SIZE + tag.nRelOffset + offsetIFDData,
1414 67 : tag.pabyVal, tag.nLengthBytes);
1415 : }
1416 : }
1417 42 : }
1418 :
1419 : /************************************************************************/
1420 : /* FreeTags() */
1421 : /************************************************************************/
1422 :
1423 90 : static void FreeTags(std::vector<TagValue> &tags)
1424 : {
1425 244 : for (auto &tag : tags)
1426 : {
1427 154 : CPLFree(tag.pabyVal);
1428 : }
1429 90 : }
1430 :
1431 : /************************************************************************/
1432 : /* EXIFCreate() */
1433 : /************************************************************************/
1434 :
1435 262 : GByte *EXIFCreate(char **papszEXIFMetadata, GByte *pabyThumbnail,
1436 : GUInt32 nThumbnailSize, GUInt32 nThumbnailWidth,
1437 : GUInt32 nThumbnailHeight, GUInt32 *pnOutBufferSize)
1438 : {
1439 262 : *pnOutBufferSize = 0;
1440 :
1441 262 : bool bHasEXIFMetadata = false;
1442 292 : for (char **papszIter = papszEXIFMetadata; papszIter && *papszIter;
1443 : ++papszIter)
1444 : {
1445 55 : if (STARTS_WITH_CI(*papszIter, "EXIF_"))
1446 : {
1447 25 : bHasEXIFMetadata = true;
1448 25 : break;
1449 : }
1450 : }
1451 262 : if (!bHasEXIFMetadata && pabyThumbnail == nullptr)
1452 : {
1453 : // Nothing to do
1454 232 : return nullptr;
1455 : }
1456 :
1457 30 : GUInt32 nOfflineSizeMain = 0;
1458 : std::vector<TagValue> mainTags = EXIFFormatTagValue(
1459 60 : papszEXIFMetadata, EXIFLocation::MAIN_IFD, &nOfflineSizeMain);
1460 :
1461 30 : GUInt32 nOfflineSizeEXIF = 0;
1462 : std::vector<TagValue> exifTags = EXIFFormatTagValue(
1463 60 : papszEXIFMetadata, EXIFLocation::EXIF_IFD, &nOfflineSizeEXIF);
1464 :
1465 30 : GUInt32 nOfflineSizeGPS = 0;
1466 : std::vector<TagValue> gpsTags = EXIFFormatTagValue(
1467 60 : papszEXIFMetadata, EXIFLocation::GPS_IFD, &nOfflineSizeGPS);
1468 :
1469 30 : const GUInt16 nEXIFTags = static_cast<GUInt16>(exifTags.size());
1470 30 : const GUInt16 nGPSTags = static_cast<GUInt16>(gpsTags.size());
1471 :
1472 : // including TIFFTAG_EXIFIFD and TIFFTAG_GPSIFD
1473 30 : GUInt16 nIFD0Entries = (nEXIFTags ? 1 : 0) + (nGPSTags ? 1 : 0) +
1474 30 : static_cast<GUInt16>(mainTags.size());
1475 :
1476 30 : GUInt32 nBufferSize = EXIF_HEADER_SIZE + // Exif header
1477 : 4 + // Tiff signature
1478 : 4 + // Offset of IFD0
1479 : 2 + // Number of entries of IFD0
1480 30 : nIFD0Entries * TAG_SIZE + // Entries of IFD0
1481 : nOfflineSizeMain;
1482 :
1483 30 : if (nEXIFTags)
1484 : {
1485 24 : nBufferSize += 2 + // Number of entries of private EXIF IFD
1486 24 : nEXIFTags * TAG_SIZE + nOfflineSizeEXIF;
1487 : }
1488 :
1489 30 : if (nGPSTags)
1490 : {
1491 11 : nBufferSize += 2 + // Number of entries of private GPS IFD
1492 11 : nGPSTags * TAG_SIZE + nOfflineSizeGPS;
1493 : }
1494 :
1495 30 : GUInt16 nIFD1Entries = 0;
1496 30 : if (pabyThumbnail)
1497 : {
1498 6 : nIFD1Entries = 5;
1499 6 : nBufferSize += 4 + // Offset of IFD1
1500 : 2 + // Number of entries of IFD1
1501 6 : nIFD1Entries * TAG_SIZE + // Entries of IFD1
1502 : nThumbnailSize;
1503 : }
1504 30 : nBufferSize += 4; // Offset of next IFD
1505 :
1506 30 : GByte *pabyData = nullptr;
1507 30 : if (nBufferSize > 65536)
1508 : {
1509 1 : CPLError(CE_Warning, CPLE_AppDefined,
1510 : "Cannot write EXIF segment. "
1511 : "The size of the EXIF segment exceeds 65536 bytes");
1512 : }
1513 : else
1514 : {
1515 29 : pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBufferSize));
1516 : }
1517 30 : if (pabyData == nullptr)
1518 : {
1519 1 : FreeTags(mainTags);
1520 1 : FreeTags(exifTags);
1521 1 : FreeTags(gpsTags);
1522 1 : return nullptr;
1523 : }
1524 :
1525 29 : memcpy(pabyData, "Exif\0\0", EXIF_HEADER_SIZE);
1526 29 : GUInt32 nBufferOff = EXIF_HEADER_SIZE;
1527 29 : GUInt32 nTIFFStartOff = nBufferOff;
1528 :
1529 : // TIFF little-endian signature.
1530 29 : const GUInt16 TIFF_LITTLEENDIAN = 0x4949;
1531 29 : WriteLEUInt16(pabyData, nBufferOff, TIFF_LITTLEENDIAN);
1532 29 : const GUInt16 TIFF_VERSION = 42;
1533 29 : WriteLEUInt16(pabyData, nBufferOff, TIFF_VERSION);
1534 :
1535 : // Offset of IFD0
1536 29 : WriteLEUInt32(pabyData, nBufferOff, nBufferOff - nTIFFStartOff + 4);
1537 :
1538 : // Number of entries of IFD0
1539 29 : WriteLEUInt16(pabyData, nBufferOff, nIFD0Entries);
1540 :
1541 29 : if (!mainTags.empty())
1542 : {
1543 8 : GUInt32 offsetIFDData =
1544 8 : nBufferOff - nTIFFStartOff + nIFD0Entries * TAG_SIZE + 4;
1545 8 : WriteTags(pabyData, nBufferOff, offsetIFDData, mainTags);
1546 : }
1547 :
1548 29 : GUInt32 nEXIFIFDOffset = 0;
1549 29 : if (nEXIFTags)
1550 : {
1551 23 : WriteTag(pabyData, nBufferOff, EXIFOFFSETTAG, TIFF_LONG, 1, 0);
1552 23 : nEXIFIFDOffset = nBufferOff - 4;
1553 : }
1554 :
1555 29 : GUInt32 nGPSIFDOffset = 0;
1556 29 : if (nGPSTags)
1557 : {
1558 11 : WriteTag(pabyData, nBufferOff, GPSOFFSETTAG, TIFF_LONG, 1, 0);
1559 11 : nGPSIFDOffset = nBufferOff - 4; // offset to patch
1560 : }
1561 :
1562 : // Offset of next IFD
1563 29 : GUInt32 nOffsetOfIFDAfterIFD0 = nBufferOff;
1564 29 : WriteLEUInt32(pabyData, nBufferOff, 0); // offset to patch
1565 :
1566 : // Space for offline tag values (already written)
1567 29 : nBufferOff += nOfflineSizeMain;
1568 :
1569 29 : if (nEXIFTags)
1570 : {
1571 : // Patch value of EXIFOFFSETTAG
1572 : {
1573 23 : GUInt32 nTmp = nEXIFIFDOffset;
1574 23 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1575 : }
1576 :
1577 : // Number of entries of EXIF IFD
1578 23 : WriteLEUInt16(pabyData, nBufferOff, nEXIFTags);
1579 :
1580 23 : GUInt32 offsetIFDData =
1581 23 : nBufferOff - nTIFFStartOff + nEXIFTags * TAG_SIZE;
1582 23 : WriteTags(pabyData, nBufferOff, offsetIFDData, exifTags);
1583 :
1584 : // Space for offline tag values (already written)
1585 23 : nBufferOff += nOfflineSizeEXIF;
1586 : }
1587 :
1588 29 : if (nGPSTags)
1589 : {
1590 : // Patch value of GPSOFFSETTAG
1591 : {
1592 11 : GUInt32 nTmp = nGPSIFDOffset;
1593 11 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1594 : }
1595 :
1596 : // Number of entries of GPS IFD
1597 11 : WriteLEUInt16(pabyData, nBufferOff, nGPSTags);
1598 :
1599 11 : GUInt32 offsetIFDData =
1600 11 : nBufferOff - nTIFFStartOff + nGPSTags * TAG_SIZE;
1601 11 : WriteTags(pabyData, nBufferOff, offsetIFDData, gpsTags);
1602 :
1603 : // Space for offline tag values (already written)
1604 11 : nBufferOff += nOfflineSizeGPS;
1605 : }
1606 :
1607 29 : if (nIFD1Entries)
1608 : {
1609 : // Patch value of offset after next IFD
1610 : {
1611 6 : GUInt32 nTmp = nOffsetOfIFDAfterIFD0;
1612 6 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1613 : }
1614 :
1615 : // Number of entries of IFD1
1616 6 : WriteLEUInt16(pabyData, nBufferOff, nIFD1Entries);
1617 :
1618 6 : const GUInt16 JPEG_TIFF_IMAGEWIDTH = 0x100;
1619 6 : const GUInt16 JPEG_TIFF_IMAGEHEIGHT = 0x101;
1620 6 : const GUInt16 JPEG_TIFF_COMPRESSION = 0x103;
1621 6 : const GUInt16 JPEG_EXIF_JPEGIFOFSET = 0x201;
1622 6 : const GUInt16 JPEG_EXIF_JPEGIFBYTECOUNT = 0x202;
1623 :
1624 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_IMAGEWIDTH, TIFF_LONG, 1,
1625 : nThumbnailWidth);
1626 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_IMAGEHEIGHT, TIFF_LONG, 1,
1627 : nThumbnailHeight);
1628 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_COMPRESSION, TIFF_SHORT, 1,
1629 : 6); // JPEG compression
1630 6 : WriteTag(pabyData, nBufferOff, JPEG_EXIF_JPEGIFOFSET, TIFF_LONG, 1,
1631 6 : nBufferSize - EXIF_HEADER_SIZE - nThumbnailSize);
1632 6 : WriteTag(pabyData, nBufferOff, JPEG_EXIF_JPEGIFBYTECOUNT, TIFF_LONG, 1,
1633 : nThumbnailSize);
1634 :
1635 : // Offset of next IFD
1636 6 : WriteLEUInt32(pabyData, nBufferOff, 0);
1637 : }
1638 :
1639 29 : CPLAssert(nBufferOff + nThumbnailSize == nBufferSize);
1640 29 : if (pabyThumbnail != nullptr && nThumbnailSize)
1641 6 : memcpy(pabyData + nBufferOff, pabyThumbnail, nThumbnailSize);
1642 :
1643 29 : FreeTags(mainTags);
1644 29 : FreeTags(exifTags);
1645 29 : FreeTags(gpsTags);
1646 :
1647 29 : *pnOutBufferSize = nBufferSize;
1648 29 : return pabyData;
1649 : }
1650 :
1651 : #ifdef DUMP_EXIF_ITEMS
1652 :
1653 : // To help generate the doc page
1654 : // g++ -DDUMP_EXIF_ITEMS gcore/gdalexif.cpp -o dumpexif -Iport -Igcore -Iogr -L.
1655 : // -lgdal
1656 :
1657 : int main()
1658 : {
1659 : printf("<table border=\"1\">\n"); /* ok */
1660 : printf("<tr><th>Metadata item name</th><th>Hex code</th>" /* ok */
1661 : "<th>Type</th><th>Number of values</th><th>Optionality</th></tr>\n");
1662 : for (size_t i = 0; exiftags[i].name[0] != '\0'; i++)
1663 : {
1664 : if (exiftags[i].datatype == TIFF_NOTYPE)
1665 : continue;
1666 : printf(/* ok */ "<tr><td>%s</td><td>0x%04X</td><td>%s</td><td>%s</"
1667 : "td><td>%s</"
1668 : "td></tr>\n",
1669 : exiftags[i].name, exiftags[i].tag,
1670 : exiftags[i].datatype == TIFF_BYTE ? "BYTE"
1671 : : exiftags[i].datatype == TIFF_ASCII ? "ASCII"
1672 : : exiftags[i].datatype == TIFF_UNDEFINED ? "UNDEFINED"
1673 : : exiftags[i].datatype == TIFF_SHORT ? "SHORT"
1674 : : exiftags[i].datatype == TIFF_LONG ? "LONG"
1675 : : exiftags[i].datatype == TIFF_RATIONAL ? "RATIONAL"
1676 : : exiftags[i].datatype == TIFF_SRATIONAL ? "SRATIONAL"
1677 : : "?????",
1678 : exiftags[i].length ? CPLSPrintf("%d", exiftags[i].length)
1679 : : "variable",
1680 : exiftags[i].comprCond == COND_MANDATORY ? "<b>Mandatory</b>"
1681 : : exiftags[i].comprCond == COND_OPTIONAL ? "Optional"
1682 : : exiftags[i].comprCond == COND_RECOMMENDED ? "Recommended"
1683 : : "?????");
1684 : }
1685 : printf("</table>\n"); /* ok */
1686 :
1687 : printf("<table border=\"1\">\n"); /* ok */
1688 : printf("<tr><th>Metadata item name</th><th>Hex code</th>" /* ok */
1689 : "<th>Type</th><th>Number of values</th><th>Optionality</th></tr>\n");
1690 : for (size_t i = 0; gpstags[i].name[0] != '\0'; i++)
1691 : {
1692 : if (gpstags[i].datatype == TIFF_NOTYPE)
1693 : continue;
1694 : printf(/* ok */
1695 : "<tr><td>%s</td><td>0x%04X</td><td>%s</td><td>%s</td><td>%s</"
1696 : "td></tr>\n",
1697 : gpstags[i].name, gpstags[i].tag,
1698 : gpstags[i].datatype == TIFF_BYTE ? "BYTE"
1699 : : gpstags[i].datatype == TIFF_ASCII ? "ASCII"
1700 : : gpstags[i].datatype == TIFF_UNDEFINED ? "UNDEFINED"
1701 : : gpstags[i].datatype == TIFF_SHORT ? "SHORT"
1702 : : gpstags[i].datatype == TIFF_LONG ? "LONG"
1703 : : gpstags[i].datatype == TIFF_RATIONAL ? "RATIONAL"
1704 : : gpstags[i].datatype == TIFF_SRATIONAL ? "SRATIONAL"
1705 : : "?????",
1706 : gpstags[i].length ? CPLSPrintf("%d", gpstags[i].length)
1707 : : "variable",
1708 : gpstags[i].comprCond == COND_MANDATORY ? "<b>Mandatory</b>"
1709 : : gpstags[i].comprCond == COND_OPTIONAL ? "Optional"
1710 : : gpstags[i].comprCond == COND_RECOMMENDED ? "Recommended"
1711 : : "?????");
1712 : }
1713 : printf("</table>\n"); /* ok */
1714 :
1715 : return 0;
1716 : }
1717 :
1718 : #endif
|