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 511 : static void EXIFPrintData(char *pszData, GUInt16 type, GUInt32 count,
263 : const unsigned char *data)
264 : {
265 511 : const char *sep = "";
266 : char szTemp[128];
267 511 : char *pszDataEnd = pszData;
268 :
269 511 : pszData[0] = '\0';
270 :
271 511 : switch (type)
272 : {
273 :
274 188 : case TIFF_UNDEFINED:
275 : case TIFF_BYTE:
276 188 : for (; count > 0; count--)
277 : {
278 150 : snprintf(szTemp, sizeof(szTemp), "%s0x%02x", sep, *data);
279 150 : data++;
280 150 : sep = " ";
281 150 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
282 0 : break;
283 150 : strcat(pszDataEnd, szTemp);
284 150 : pszDataEnd += strlen(pszDataEnd);
285 : }
286 38 : 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 238 : case TIFF_ASCII:
303 238 : memcpy(pszData, data, count);
304 : // Strip trailing spaces or nul characters
305 604 : while (count > 0 &&
306 558 : (pszData[count - 1] == ' ' || pszData[count - 1] == 0))
307 366 : --count;
308 238 : pszData[count] = '\0';
309 238 : break;
310 :
311 84 : case TIFF_SHORT:
312 : {
313 84 : const GUInt16 *wp = reinterpret_cast<const GUInt16 *>(data);
314 937 : for (; count > 0; count--)
315 : {
316 853 : snprintf(szTemp, sizeof(szTemp), "%s%u", sep, *wp);
317 853 : wp++;
318 853 : sep = " ";
319 853 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
320 0 : break;
321 853 : strcat(pszDataEnd, szTemp);
322 853 : pszDataEnd += strlen(pszDataEnd);
323 : }
324 84 : 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 21 : case TIFF_LONG:
342 : {
343 21 : const GUInt32 *lp = reinterpret_cast<const GUInt32 *>(data);
344 42 : for (; count > 0; count--)
345 : {
346 21 : snprintf(szTemp, sizeof(szTemp), "%s%u", sep, *lp);
347 21 : lp++;
348 21 : sep = " ";
349 21 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
350 0 : break;
351 21 : strcat(pszDataEnd, szTemp);
352 21 : pszDataEnd += strlen(pszDataEnd);
353 : }
354 21 : 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 123 : case TIFF_RATIONAL:
372 : {
373 123 : const GUInt32 *lp = reinterpret_cast<const GUInt32 *>(data);
374 : // if(bSwabflag)
375 : // TIFFSwabArrayOfLong((GUInt32*) data, 2*count);
376 360 : for (; count > 0; count--)
377 : {
378 237 : if ((lp[0] == 0) || (lp[1] == 0))
379 : {
380 8 : snprintf(szTemp, sizeof(szTemp), "%s(0)", sep);
381 : }
382 : else
383 : {
384 229 : CPLsnprintf(szTemp, sizeof(szTemp), "%s(%g)", sep,
385 229 : static_cast<double>(lp[0]) /
386 229 : static_cast<double>(lp[1]));
387 : }
388 237 : sep = " ";
389 237 : lp += 2;
390 237 : if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
391 0 : break;
392 237 : strcat(pszDataEnd, szTemp);
393 237 : pszDataEnd += strlen(pszDataEnd);
394 : }
395 123 : 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 511 : 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 511 : static int EXIF_TIFFDataWidth(int /* GDALEXIFTIFFDataType */ type)
466 : {
467 511 : switch (type)
468 : {
469 276 : case 0: /* nothing */
470 : case TIFF_BYTE:
471 : case TIFF_ASCII:
472 : case TIFF_SBYTE:
473 : case TIFF_UNDEFINED:
474 276 : return 1;
475 84 : case TIFF_SHORT:
476 : case TIFF_SSHORT:
477 84 : return 2;
478 21 : case TIFF_LONG:
479 : case TIFF_SLONG:
480 : case TIFF_FLOAT:
481 : case TIFF_IFD:
482 21 : return 4;
483 130 : case TIFF_RATIONAL:
484 : case TIFF_SRATIONAL:
485 : case TIFF_DOUBLE:
486 : // case TIFF_LONG8:
487 : // case TIFF_SLONG8:
488 : // case TIFF_IFD8:
489 130 : 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 142 : 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 142 : VSILFILE *const fp = static_cast<VSILFILE *>(fpInL);
509 :
510 426 : if (nOffset > INT_MAX - nTIFFHEADER ||
511 284 : VSIFSeekL(fp, nOffset + nTIFFHEADER, SEEK_SET) != 0 ||
512 142 : 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 142 : if (bSwabflag)
521 7 : CPL_SWAP16PTR(&nEntryCount);
522 :
523 : // Some apps write empty directories - see bug 1523.
524 142 : if (nEntryCount == 0)
525 1 : return CE_None;
526 :
527 : // Some files are corrupt, a large entry count is a sign of this.
528 141 : 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 140 : CPLMalloc(nEntryCount * sizeof(GDALEXIFTIFFDirEntry)));
538 :
539 : /* -------------------------------------------------------------------- */
540 : /* Read all directory entries */
541 : /* -------------------------------------------------------------------- */
542 : {
543 280 : const unsigned int n = static_cast<int>(VSIFReadL(
544 140 : poTIFFDir, 1, nEntryCount * sizeof(GDALEXIFTIFFDirEntry), fp));
545 140 : 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 140 : vector<char> oTempStorage(MAXSTRINGLENGTH + 1, 0);
558 140 : char *const szTemp = &oTempStorage[0];
559 :
560 : char szName[128];
561 :
562 140 : GDALEXIFTIFFDirEntry *poTIFFDirEntry = poTIFFDir;
563 :
564 723 : for (unsigned int i = nEntryCount; i > 0; i--, poTIFFDirEntry++)
565 : {
566 583 : 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 583 : szName[0] = '\0';
580 583 : szTemp[0] = '\0';
581 :
582 38909 : for (const EXIFTagDesc *poExifTags = exiftags; poExifTags->tag;
583 : poExifTags++)
584 : {
585 38689 : if (poExifTags->tag == poTIFFDirEntry->tdir_tag)
586 : {
587 363 : CPLAssert(nullptr != poExifTags->name);
588 :
589 363 : CPLStrlcpy(szName, poExifTags->name, sizeof(szName));
590 363 : break;
591 : }
592 : }
593 :
594 583 : if (szName[0] == 0)
595 : {
596 654 : for (const EXIFTagDesc *poTag = IFD0Tags; poTag->tag; poTag++)
597 : {
598 438 : 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 583 : if (nOffset == nGPSOffset)
609 : {
610 429 : for (const EXIFTagDesc *poGPSTags = gpstags;
611 429 : poGPSTags->tag != 0xffff; poGPSTags++)
612 : {
613 429 : if (poGPSTags->tag == poTIFFDirEntry->tdir_tag)
614 : {
615 144 : CPLAssert(nullptr != poGPSTags->name);
616 144 : CPLStrlcpy(szName, poGPSTags->name, sizeof(szName));
617 144 : break;
618 : }
619 : }
620 : }
621 : /* --------------------------------------------------------------------
622 : */
623 : /* If the tag was not found, look into the interoperability table
624 : */
625 : /* --------------------------------------------------------------------
626 : */
627 583 : 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 583 : if (poTIFFDirEntry->tdir_offset < INT_MAX)
647 : {
648 581 : if (poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG)
649 : {
650 44 : nExifOffset = poTIFFDirEntry->tdir_offset;
651 44 : continue;
652 : }
653 537 : else if (poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET)
654 : {
655 0 : nInterOffset = poTIFFDirEntry->tdir_offset;
656 0 : continue;
657 : }
658 537 : else if (poTIFFDirEntry->tdir_tag == GPSOFFSETTAG)
659 : {
660 28 : nGPSOffset = poTIFFDirEntry->tdir_offset;
661 28 : continue;
662 : }
663 : }
664 :
665 : /* ----------------------------------------------------------------- */
666 : /* If we didn't recognise the tag, report it as CPLDebug() */
667 : /* ----------------------------------------------------------------- */
668 511 : bool bUnknownTag = false;
669 511 : 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 511 : 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 511 : 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 511 : if (EQUAL(szName, "EXIF_ExifVersion") ||
701 476 : EQUAL(szName, "EXIF_FlashPixVersion") ||
702 449 : EQUAL(szName, "EXIF_MakerNote") ||
703 448 : EQUAL(szName, "GPSProcessingMethod") ||
704 448 : EQUAL(szName, "EXIF_XmlPacket"))
705 63 : poTIFFDirEntry->tdir_type = TIFF_ASCII;
706 :
707 : /* --------------------------------------------------------------------
708 : */
709 : /* Print tags */
710 : /* --------------------------------------------------------------------
711 : */
712 511 : 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 511 : const int nDataWidth = EXIF_TIFFDataWidth(poTIFFDirEntry->tdir_type);
721 511 : 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 511 : const int space = poTIFFDirEntry->tdir_count * nDataWidth;
736 511 : if (space >= 0 && space <= 4)
737 : {
738 :
739 : unsigned char data[4];
740 308 : memcpy(data, &poTIFFDirEntry->tdir_offset, 4);
741 308 : 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 308 : EXIFPrintData(szTemp, poTIFFDirEntry->tdir_type,
775 308 : poTIFFDirEntry->tdir_count, data);
776 : }
777 : /* --------------------------------------------------------------------
778 : */
779 : /* The data is being read where tdir_offset point to in the file */
780 : /* --------------------------------------------------------------------
781 : */
782 203 : else if (space > 0 && space < MAXSTRINGLENGTH)
783 : {
784 : unsigned char *data =
785 203 : static_cast<unsigned char *>(VSIMalloc(space));
786 :
787 203 : if (data)
788 : {
789 203 : CPL_IGNORE_RET_VAL(
790 203 : VSIFSeekL(fp, nTagValueOffset + nTIFFHEADER, SEEK_SET));
791 203 : CPL_IGNORE_RET_VAL(VSIFReadL(data, 1, space, fp));
792 :
793 203 : if (bSwabflag)
794 : {
795 14 : switch (poTIFFDirEntry->tdir_type)
796 : {
797 0 : case TIFF_SHORT:
798 : case TIFF_SSHORT:
799 : {
800 0 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
801 : j++)
802 : {
803 0 : CPL_SWAP16PTR(
804 : reinterpret_cast<GUInt16 *>(data) + j);
805 : }
806 0 : break;
807 : }
808 0 : case TIFF_LONG:
809 : case TIFF_SLONG:
810 : case TIFF_FLOAT:
811 : {
812 0 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
813 : j++)
814 : {
815 0 : CPL_SWAP32PTR(
816 : reinterpret_cast<GUInt32 *>(data) + j);
817 : }
818 0 : break;
819 : }
820 6 : case TIFF_RATIONAL:
821 : case TIFF_SRATIONAL:
822 : {
823 6 : for (unsigned j = 0;
824 18 : j < 2 * poTIFFDirEntry->tdir_count; j++)
825 : {
826 12 : CPL_SWAP32PTR(
827 : reinterpret_cast<GUInt32 *>(data) + j);
828 : }
829 6 : break;
830 : }
831 0 : case TIFF_DOUBLE:
832 : {
833 0 : for (unsigned j = 0; j < poTIFFDirEntry->tdir_count;
834 : j++)
835 : {
836 0 : CPL_SWAPDOUBLE(
837 : reinterpret_cast<double *>(data) + j);
838 : }
839 0 : break;
840 : }
841 8 : default:
842 8 : break;
843 : }
844 : }
845 :
846 203 : EXIFPrintData(szTemp, poTIFFDirEntry->tdir_type,
847 : poTIFFDirEntry->tdir_count, data);
848 203 : CPLFree(data);
849 203 : }
850 : }
851 : else
852 : {
853 0 : CPLError(CE_Warning, CPLE_AppDefined,
854 : "Invalid EXIF header size: %ld, ignoring tag.",
855 : static_cast<long>(space));
856 : }
857 :
858 511 : if (bUnknownTag)
859 0 : CPLDebug("EXIF", "Ignoring %s=%s", szName, szTemp);
860 : else
861 511 : papszMetadata = CSLSetNameValue(papszMetadata, szName, szTemp);
862 : }
863 140 : CPLFree(poTIFFDir);
864 :
865 140 : return CE_None;
866 : }
867 :
868 : /************************************************************************/
869 : /* WriteLEUInt16() */
870 : /************************************************************************/
871 :
872 575 : static void WriteLEUInt16(GByte *pabyData, GUInt32 &nBufferOff, GUInt16 nVal)
873 : {
874 575 : pabyData[nBufferOff] = static_cast<GByte>(nVal & 0xff);
875 575 : pabyData[nBufferOff + 1] = static_cast<GByte>(nVal >> 8);
876 575 : nBufferOff += 2;
877 575 : }
878 :
879 : /************************************************************************/
880 : /* WriteLEUInt32() */
881 : /************************************************************************/
882 :
883 627 : static void WriteLEUInt32(GByte *pabyData, GUInt32 &nBufferOff, GUInt32 nVal)
884 : {
885 627 : pabyData[nBufferOff] = static_cast<GByte>(nVal & 0xff);
886 627 : pabyData[nBufferOff + 1] = static_cast<GByte>((nVal >> 8) & 0xff);
887 627 : pabyData[nBufferOff + 2] = static_cast<GByte>((nVal >> 16) & 0xff);
888 627 : pabyData[nBufferOff + 3] = static_cast<GByte>(nVal >> 24);
889 627 : nBufferOff += 4;
890 627 : }
891 :
892 : /************************************************************************/
893 : /* GetHexValue() */
894 : /************************************************************************/
895 :
896 195 : static int GetHexValue(char ch)
897 : {
898 195 : const char chDEC_ZERO = '0';
899 195 : if (ch >= chDEC_ZERO && ch <= '9')
900 183 : return ch - chDEC_ZERO;
901 12 : if (ch >= 'a' && ch <= 'f')
902 5 : return ch - 'a' + 10;
903 7 : if (ch >= 'A' && ch <= 'F')
904 7 : return ch - 'A' + 10;
905 0 : return -1;
906 : }
907 :
908 : /************************************************************************/
909 : /* ParseUndefined() */
910 : /************************************************************************/
911 :
912 32 : static GByte *ParseUndefined(const char *pszVal, GUInt32 *pnLength)
913 : {
914 32 : GUInt32 nSize = 0;
915 32 : bool bIsHexExcaped = true;
916 32 : const char chDEC_ZERO = '0';
917 32 : GByte *pabyData = reinterpret_cast<GByte *>(CPLMalloc(strlen(pszVal) + 1));
918 :
919 : // Is it a hexadecimal string like "0xA 0x1E 00 0xDF..." ?
920 60 : for (size_t i = 0; pszVal[i] != '\0';)
921 : {
922 : // 0xA
923 97 : if (pszVal[i] == chDEC_ZERO && pszVal[i + 1] == 'x' &&
924 157 : GetHexValue(pszVal[i + 2]) >= 0 &&
925 39 : (pszVal[i + 3] == ' ' || pszVal[i + 3] == '\0'))
926 : {
927 0 : pabyData[nSize] = static_cast<GByte>(GetHexValue(pszVal[i + 2]));
928 0 : nSize++;
929 0 : if (pszVal[i + 3] == '\0')
930 0 : break;
931 0 : i += 4;
932 : }
933 : // 0xAA
934 97 : else if (pszVal[i] == chDEC_ZERO && pszVal[i + 1] == 'x' &&
935 78 : GetHexValue(pszVal[i + 2]) >= 0 &&
936 157 : GetHexValue(pszVal[i + 3]) >= 0 &&
937 39 : (pszVal[i + 4] == ' ' || pszVal[i + 4] == '\0'))
938 : {
939 39 : pabyData[nSize] = static_cast<GByte>(
940 39 : GetHexValue(pszVal[i + 2]) * 16 + GetHexValue(pszVal[i + 3]));
941 39 : nSize++;
942 39 : if (pszVal[i + 4] == '\0')
943 11 : break;
944 28 : i += 5;
945 : }
946 : // 00
947 21 : else if (pszVal[i] == chDEC_ZERO && pszVal[i + 1] == chDEC_ZERO &&
948 0 : (pszVal[i + 2] == ' ' || pszVal[i + 2] == '\0'))
949 : {
950 0 : pabyData[nSize] = 0;
951 0 : nSize++;
952 0 : if (pszVal[i + 2] == '\0')
953 0 : break;
954 0 : i += 3;
955 : }
956 : else
957 : {
958 21 : bIsHexExcaped = false;
959 21 : break;
960 : }
961 : }
962 :
963 32 : if (bIsHexExcaped)
964 : {
965 11 : *pnLength = nSize;
966 11 : return pabyData;
967 : }
968 :
969 : // Otherwise take the string value as a byte value
970 21 : memcpy(pabyData, pszVal, strlen(pszVal) + 1);
971 21 : *pnLength = static_cast<GUInt32>(strlen(pszVal));
972 21 : return pabyData;
973 : }
974 :
975 : /************************************************************************/
976 : /* TagValue */
977 : /************************************************************************/
978 :
979 225 : struct TagValue
980 : {
981 : GUInt16 tag = 0;
982 : GDALEXIFTIFFDataType datatype = TIFF_NOTYPE;
983 : std::unique_ptr<GByte, VSIFreeReleaser> pabyVal{};
984 : GUInt32 nLength = 0;
985 : GUInt32 nLengthBytes = 0;
986 : int nRelOffset = 0;
987 :
988 154 : TagValue() = default;
989 :
990 441 : TagValue(TagValue &&) = default;
991 : TagValue &operator=(TagValue &&) = default;
992 :
993 226 : bool operator<(const TagValue &other) const
994 : {
995 226 : return tag < other.tag;
996 : }
997 :
998 : private:
999 : TagValue(const TagValue &) = delete;
1000 : TagValue &operator=(const TagValue &) = delete;
1001 : };
1002 :
1003 : /************************************************************************/
1004 : /* GetNumDenomFromDouble() */
1005 : /************************************************************************/
1006 :
1007 80 : static bool GetNumDenomFromDouble(GDALEXIFTIFFDataType datatype, double dfVal,
1008 : GUInt32 &nNum, GUInt32 &nDenom)
1009 : {
1010 80 : nNum = 0;
1011 80 : nDenom = 1;
1012 80 : if (std::isnan(dfVal))
1013 : {
1014 1 : return false;
1015 : }
1016 79 : else if (datatype == TIFF_RATIONAL)
1017 : {
1018 72 : if (dfVal < 0)
1019 : {
1020 1 : return false;
1021 : }
1022 142 : else if (dfVal <= std::numeric_limits<unsigned int>::max() &&
1023 71 : dfVal == static_cast<GUInt32>(dfVal))
1024 : {
1025 54 : nNum = static_cast<GUInt32>(dfVal);
1026 54 : nDenom = 1;
1027 : }
1028 17 : else if (dfVal < 1.0)
1029 : {
1030 1 : nNum = static_cast<GUInt32>(
1031 1 : dfVal * std::numeric_limits<unsigned int>::max());
1032 1 : nDenom = std::numeric_limits<unsigned int>::max();
1033 : }
1034 : else
1035 : {
1036 16 : nNum = std::numeric_limits<unsigned int>::max();
1037 16 : nDenom = static_cast<GUInt32>(
1038 16 : std::numeric_limits<unsigned int>::max() / dfVal);
1039 : }
1040 : }
1041 7 : else if (dfVal < 0.0)
1042 : {
1043 6 : if (dfVal >= std::numeric_limits<int>::min() &&
1044 3 : dfVal == static_cast<GInt32>(dfVal))
1045 : {
1046 1 : nNum = static_cast<GInt32>(dfVal);
1047 1 : nDenom = 1;
1048 : }
1049 2 : else if (dfVal > -1.0)
1050 : {
1051 2 : nNum = -static_cast<GInt32>((-dfVal) *
1052 1 : std::numeric_limits<int>::max());
1053 1 : nDenom = std::numeric_limits<int>::max();
1054 : }
1055 : else
1056 : {
1057 1 : nNum = -std::numeric_limits<int>::max();
1058 1 : nDenom =
1059 1 : static_cast<GInt32>(std::numeric_limits<int>::max() / (-dfVal));
1060 : }
1061 : }
1062 : else
1063 : {
1064 8 : if (dfVal <= std::numeric_limits<int>::max() &&
1065 4 : dfVal == static_cast<GInt32>(dfVal))
1066 : {
1067 2 : nNum = static_cast<GInt32>(dfVal);
1068 2 : nDenom = 1;
1069 : }
1070 2 : else if (dfVal < 1.0)
1071 : {
1072 1 : nNum = static_cast<GInt32>(dfVal * std::numeric_limits<int>::max());
1073 1 : nDenom = std::numeric_limits<int>::max();
1074 : }
1075 : else
1076 : {
1077 1 : nNum = std::numeric_limits<int>::max();
1078 1 : nDenom =
1079 1 : static_cast<GInt32>(std::numeric_limits<int>::max() / dfVal);
1080 : }
1081 : }
1082 78 : return true;
1083 : }
1084 :
1085 : /************************************************************************/
1086 : /* EXIFFormatTagValue() */
1087 : /************************************************************************/
1088 :
1089 : enum class EXIFLocation
1090 : {
1091 : MAIN_IFD,
1092 : EXIF_IFD,
1093 : GPS_IFD
1094 : };
1095 :
1096 90 : static std::vector<TagValue> EXIFFormatTagValue(char **papszEXIFMetadata,
1097 : EXIFLocation location,
1098 : GUInt32 *pnOfflineSize)
1099 : {
1100 90 : std::vector<TagValue> tags;
1101 90 : int nRelOffset = 0;
1102 90 : const EXIFTagDesc *tagdescArray =
1103 90 : (location == EXIFLocation::GPS_IFD) ? gpstags : exiftags;
1104 :
1105 561 : for (char **papszIter = papszEXIFMetadata; papszIter && *papszIter;
1106 : ++papszIter)
1107 : {
1108 471 : if (!STARTS_WITH_CI(*papszIter, "EXIF_"))
1109 316 : continue;
1110 465 : if (location == EXIFLocation::GPS_IFD &&
1111 155 : !STARTS_WITH_CI(*papszIter, "EXIF_GPS"))
1112 111 : continue;
1113 354 : if (location != EXIFLocation::GPS_IFD &&
1114 310 : STARTS_WITH_CI(*papszIter, "EXIF_GPS"))
1115 88 : continue;
1116 :
1117 266 : bool bFound = false;
1118 266 : size_t i = 0; // needed after loop
1119 9985 : for (; tagdescArray[i].name[0] != '\0'; i++)
1120 : {
1121 9983 : if (STARTS_WITH_CI(*papszIter, tagdescArray[i].name) &&
1122 312 : (*papszIter)[strlen(tagdescArray[i].name)] == '=')
1123 : {
1124 264 : bFound = true;
1125 264 : break;
1126 : }
1127 : }
1128 :
1129 266 : if (location == EXIFLocation::MAIN_IFD)
1130 : {
1131 111 : if (tagdescArray[i].tag > 0x8298) // EXIF_Copyright
1132 : {
1133 70 : continue;
1134 : }
1135 : }
1136 155 : else if (location == EXIFLocation::EXIF_IFD)
1137 : {
1138 111 : if (tagdescArray[i].tag <= 0x8298) // EXIF_Copyright
1139 : {
1140 41 : continue;
1141 : }
1142 : }
1143 :
1144 155 : char *pszKey = nullptr;
1145 155 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
1146 155 : if (!bFound || pszKey == nullptr || pszValue == nullptr)
1147 : {
1148 1 : CPLError(CE_Warning, CPLE_NotSupported,
1149 1 : "Cannot write unknown %s tag", pszKey ? pszKey : "");
1150 : }
1151 154 : else if (tagdescArray[i].datatype == TIFF_NOTYPE)
1152 : {
1153 0 : CPLDebug("EXIF", "Tag %s ignored on write", tagdescArray[i].name);
1154 : }
1155 : else
1156 : {
1157 308 : TagValue tag;
1158 154 : tag.tag = tagdescArray[i].tag;
1159 154 : tag.datatype = tagdescArray[i].datatype;
1160 154 : tag.nRelOffset = -1;
1161 :
1162 154 : if (tag.datatype == TIFF_ASCII)
1163 : {
1164 50 : if (tagdescArray[i].length == 0 ||
1165 36 : strlen(pszValue) + 1 == tagdescArray[i].length)
1166 : {
1167 34 : tag.pabyVal.reset(
1168 34 : reinterpret_cast<GByte *>(CPLStrdup(pszValue)));
1169 34 : tag.nLength = 1 + static_cast<int>(strlen(pszValue));
1170 : }
1171 16 : else if (strlen(pszValue) >= tagdescArray[i].length)
1172 : {
1173 1 : CPLError(CE_Warning, CPLE_AppDefined,
1174 : "Value of %s will be truncated",
1175 1 : tagdescArray[i].name);
1176 1 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1177 1 : CPLMalloc(tagdescArray[i].length)));
1178 1 : memcpy(tag.pabyVal.get(), pszValue, tagdescArray[i].length);
1179 1 : tag.nLength = tagdescArray[i].length;
1180 1 : (tag.pabyVal.get())[tag.nLength - 1] = '\0';
1181 : }
1182 : else
1183 : {
1184 15 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1185 15 : CPLMalloc(tagdescArray[i].length)));
1186 15 : memset(tag.pabyVal.get(), ' ', tagdescArray[i].length);
1187 15 : memcpy(tag.pabyVal.get(), pszValue, strlen(pszValue));
1188 15 : tag.nLength = tagdescArray[i].length;
1189 15 : (tag.pabyVal.get())[tag.nLength - 1] = '\0';
1190 : }
1191 50 : tag.nLengthBytes = tag.nLength;
1192 : }
1193 104 : else if (tag.datatype == TIFF_BYTE ||
1194 96 : tag.datatype == TIFF_UNDEFINED)
1195 : {
1196 32 : GUInt32 nValLength = 0;
1197 : std::unique_ptr<GByte, VSIFreeReleaser> pabyVal(
1198 32 : ParseUndefined(pszValue, &nValLength));
1199 32 : if (tagdescArray[i].length == 0 ||
1200 30 : nValLength == tagdescArray[i].length)
1201 : {
1202 30 : if (tag.tag == 0x9286 &&
1203 1 : strncmp(pszValue, "0x", 2) != 0) // EXIF_UserComment
1204 : {
1205 : const char *pszRealVal =
1206 1 : reinterpret_cast<const char *>(pabyVal.get());
1207 1 : const int nValueLen =
1208 1 : static_cast<int>(strlen(pszRealVal));
1209 : // 8 first bytes are the character code
1210 : // Set them to 0 to mean undefined
1211 1 : tag.pabyVal.reset(
1212 1 : static_cast<GByte *>(CPLCalloc(1, 8 + nValueLen)));
1213 1 : tag.nLength = 8 + nValueLen;
1214 1 : memcpy(tag.pabyVal.get() + 8, pszRealVal, nValueLen);
1215 : }
1216 : else
1217 : {
1218 29 : tag.pabyVal = std::move(pabyVal);
1219 29 : tag.nLength = nValLength;
1220 30 : }
1221 : }
1222 2 : else if (nValLength > tagdescArray[i].length)
1223 : {
1224 1 : CPLError(CE_Warning, CPLE_AppDefined,
1225 : "Value of %s will be truncated",
1226 1 : tagdescArray[i].name);
1227 1 : tag.pabyVal = std::move(pabyVal);
1228 1 : tag.nLength = tagdescArray[i].length;
1229 : }
1230 : else
1231 : {
1232 1 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1233 1 : CPLRealloc(pabyVal.release(), tagdescArray[i].length)));
1234 2 : memset(tag.pabyVal.get() + nValLength, '\0',
1235 1 : tagdescArray[i].length - nValLength);
1236 1 : tag.nLength = tagdescArray[i].length;
1237 : }
1238 32 : tag.nLengthBytes = tag.nLength;
1239 : }
1240 72 : else if (tag.datatype == TIFF_SHORT || tag.datatype == TIFF_LONG)
1241 : {
1242 26 : char **papszTokens = CSLTokenizeString2(pszValue, " ", 0);
1243 26 : GUInt32 nTokens = static_cast<GUInt32>(CSLCount(papszTokens));
1244 26 : const GUInt32 nDataTypeSize =
1245 26 : (tag.datatype == TIFF_SHORT) ? 2 : 4;
1246 26 : if (tagdescArray[i].length == 0 ||
1247 25 : nTokens == tagdescArray[i].length)
1248 : {
1249 : // ok
1250 : }
1251 2 : else if (nTokens > tagdescArray[i].length)
1252 : {
1253 1 : CPLError(CE_Warning, CPLE_AppDefined,
1254 : "Value of %s will be truncated",
1255 1 : tagdescArray[i].name);
1256 : }
1257 : else
1258 : {
1259 1 : CPLError(CE_Warning, CPLE_AppDefined,
1260 : "Not enough values for %s: %d expected. "
1261 : "Filling with zeroes",
1262 1 : tagdescArray[i].name, tagdescArray[i].length);
1263 : }
1264 :
1265 52 : tag.nLength = (tagdescArray[i].length == 0)
1266 26 : ? nTokens
1267 25 : : tagdescArray[i].length;
1268 26 : tag.pabyVal.reset(reinterpret_cast<GByte *>(CPLCalloc(
1269 26 : 1, cpl::fits_on<int>(nDataTypeSize * tag.nLength))));
1270 :
1271 26 : GUInt32 nOffset = 0;
1272 55 : for (GUInt32 j = 0; j < std::min(nTokens, tag.nLength); j++)
1273 : {
1274 29 : GUInt32 nVal = atoi(papszTokens[j]);
1275 29 : if (tag.datatype == TIFF_SHORT)
1276 14 : WriteLEUInt16(tag.pabyVal.get(), nOffset,
1277 : static_cast<GUInt16>(nVal));
1278 : else
1279 15 : WriteLEUInt32(tag.pabyVal.get(), nOffset, nVal);
1280 : }
1281 26 : CSLDestroy(papszTokens);
1282 :
1283 26 : tag.nLengthBytes = tag.nLength * nDataTypeSize;
1284 : }
1285 46 : else if (tag.datatype == TIFF_RATIONAL ||
1286 7 : tag.datatype == TIFF_SRATIONAL)
1287 : {
1288 46 : char **papszTokens = CSLTokenizeString2(pszValue, " ", 0);
1289 46 : GUInt32 nTokens = static_cast<GUInt32>(CSLCount(papszTokens));
1290 46 : const GUInt32 nDataTypeSize = 8;
1291 46 : if (tagdescArray[i].length == 0 ||
1292 46 : nTokens == tagdescArray[i].length)
1293 : {
1294 : // ok
1295 : }
1296 1 : else if (nTokens > tagdescArray[i].length)
1297 : {
1298 1 : CPLError(CE_Warning, CPLE_AppDefined,
1299 : "Value of %s will be truncated",
1300 1 : tagdescArray[i].name);
1301 : }
1302 : else
1303 : {
1304 0 : CPLError(CE_Warning, CPLE_AppDefined,
1305 : "Not enough values for %s: %d expected. "
1306 : "Filling with zeroes",
1307 0 : tagdescArray[i].name, tagdescArray[i].length);
1308 : }
1309 :
1310 92 : tag.nLength = (tagdescArray[i].length == 0)
1311 46 : ? nTokens
1312 46 : : tagdescArray[i].length;
1313 46 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1314 46 : CPLCalloc(1, nDataTypeSize * tag.nLength)));
1315 :
1316 46 : GUInt32 nOffset = 0;
1317 126 : for (GUInt32 j = 0; j < std::min(nTokens, tag.nLength); j++)
1318 : {
1319 : double dfVal =
1320 98 : CPLAtof(papszTokens[j][0] == '(' ? papszTokens[j] + 1
1321 18 : : papszTokens[j]);
1322 80 : GUInt32 nNum = 1;
1323 80 : GUInt32 nDenom = 0;
1324 80 : if (!GetNumDenomFromDouble(tag.datatype, dfVal, nNum,
1325 : nDenom))
1326 : {
1327 2 : CPLError(CE_Warning, CPLE_AppDefined,
1328 : "Value %f is illegal for tag %s", dfVal,
1329 2 : tagdescArray[i].name);
1330 : }
1331 :
1332 80 : WriteLEUInt32(tag.pabyVal.get(), nOffset, nNum);
1333 80 : WriteLEUInt32(tag.pabyVal.get(), nOffset, nDenom);
1334 : }
1335 46 : CSLDestroy(papszTokens);
1336 :
1337 46 : tag.nLengthBytes = tag.nLength * nDataTypeSize;
1338 : }
1339 : else
1340 : {
1341 : // Shouldn't happen. Programming error
1342 0 : CPLError(CE_Warning, CPLE_NotSupported,
1343 0 : "Unhandled type %d for tag %s", tag.datatype,
1344 0 : tagdescArray[i].name);
1345 : }
1346 :
1347 154 : if (tag.nLengthBytes != 0)
1348 : {
1349 154 : if (tag.nLengthBytes > 4)
1350 : {
1351 68 : tag.nRelOffset = nRelOffset;
1352 68 : nRelOffset += tag.nLengthBytes + (tag.nLengthBytes % 1);
1353 : }
1354 154 : tags.push_back(std::move(tag));
1355 : }
1356 : }
1357 155 : CPLFree(pszKey);
1358 : }
1359 :
1360 : // Sort tags by ascending order
1361 90 : std::sort(tags.begin(), tags.end());
1362 :
1363 : #ifdef notdef
1364 : if (location == EXIF_IFD &&
1365 : CSLFetchNameValue(papszEXIFMetadata, "EXIF_ExifVersion") == nullptr)
1366 : {
1367 : const GUInt16 EXIF_VERSION = 0x9000;
1368 : TagValue tag;
1369 : tag.tag = EXIF_VERSION;
1370 : tag.datatype = TIFF_UNDEFINED;
1371 : tag.pabyVal.reset(reinterpret_cast<GByte *>(CPLStrdup("0231")));
1372 : tag.nLength = 4;
1373 : tag.nLengthBytes = 4;
1374 : tag.nRelOffset = -1;
1375 : tags.push_back(std::move(tag));
1376 : }
1377 : #endif
1378 :
1379 90 : *pnOfflineSize = nRelOffset;
1380 :
1381 90 : return tags;
1382 : }
1383 :
1384 : /************************************************************************/
1385 : /* WriteTag() */
1386 : /************************************************************************/
1387 :
1388 64 : static void WriteTag(GByte *pabyData, GUInt32 &nBufferOff, GUInt16 nTag,
1389 : GDALEXIFTIFFDataType nType, GUInt32 nCount, GUInt32 nVal)
1390 : {
1391 64 : WriteLEUInt16(pabyData, nBufferOff, nTag);
1392 64 : WriteLEUInt16(pabyData, nBufferOff, static_cast<GUInt16>(nType));
1393 64 : WriteLEUInt32(pabyData, nBufferOff, nCount);
1394 64 : WriteLEUInt32(pabyData, nBufferOff, nVal);
1395 64 : }
1396 :
1397 : /************************************************************************/
1398 : /* WriteTags() */
1399 : /************************************************************************/
1400 :
1401 42 : static void WriteTags(GByte *pabyData, GUInt32 &nBufferOff,
1402 : GUInt32 offsetIFDData, const std::vector<TagValue> &tags)
1403 : {
1404 195 : for (const auto &tag : tags)
1405 : {
1406 153 : WriteLEUInt16(pabyData, nBufferOff, tag.tag);
1407 153 : WriteLEUInt16(pabyData, nBufferOff, static_cast<GUInt16>(tag.datatype));
1408 153 : WriteLEUInt32(pabyData, nBufferOff, tag.nLength);
1409 153 : if (tag.nRelOffset < 0)
1410 : {
1411 86 : CPLAssert(tag.nLengthBytes <= 4);
1412 86 : memcpy(pabyData + nBufferOff, tag.pabyVal.get(), tag.nLengthBytes);
1413 86 : nBufferOff += 4;
1414 : }
1415 : else
1416 : {
1417 67 : WriteLEUInt32(pabyData, nBufferOff, tag.nRelOffset + offsetIFDData);
1418 67 : memcpy(pabyData + EXIF_HEADER_SIZE + tag.nRelOffset + offsetIFDData,
1419 67 : tag.pabyVal.get(), tag.nLengthBytes);
1420 : }
1421 : }
1422 42 : }
1423 :
1424 : /************************************************************************/
1425 : /* EXIFCreate() */
1426 : /************************************************************************/
1427 :
1428 265 : GByte *EXIFCreate(char **papszEXIFMetadata, GByte *pabyThumbnail,
1429 : GUInt32 nThumbnailSize, GUInt32 nThumbnailWidth,
1430 : GUInt32 nThumbnailHeight, GUInt32 *pnOutBufferSize)
1431 : {
1432 265 : *pnOutBufferSize = 0;
1433 :
1434 265 : bool bHasEXIFMetadata = false;
1435 295 : for (char **papszIter = papszEXIFMetadata; papszIter && *papszIter;
1436 : ++papszIter)
1437 : {
1438 55 : if (STARTS_WITH_CI(*papszIter, "EXIF_"))
1439 : {
1440 25 : bHasEXIFMetadata = true;
1441 25 : break;
1442 : }
1443 : }
1444 265 : if (!bHasEXIFMetadata && pabyThumbnail == nullptr)
1445 : {
1446 : // Nothing to do
1447 235 : return nullptr;
1448 : }
1449 :
1450 30 : GUInt32 nOfflineSizeMain = 0;
1451 : std::vector<TagValue> mainTags = EXIFFormatTagValue(
1452 60 : papszEXIFMetadata, EXIFLocation::MAIN_IFD, &nOfflineSizeMain);
1453 :
1454 30 : GUInt32 nOfflineSizeEXIF = 0;
1455 : std::vector<TagValue> exifTags = EXIFFormatTagValue(
1456 60 : papszEXIFMetadata, EXIFLocation::EXIF_IFD, &nOfflineSizeEXIF);
1457 :
1458 30 : GUInt32 nOfflineSizeGPS = 0;
1459 : std::vector<TagValue> gpsTags = EXIFFormatTagValue(
1460 60 : papszEXIFMetadata, EXIFLocation::GPS_IFD, &nOfflineSizeGPS);
1461 :
1462 30 : const GUInt16 nEXIFTags = static_cast<GUInt16>(exifTags.size());
1463 30 : const GUInt16 nGPSTags = static_cast<GUInt16>(gpsTags.size());
1464 :
1465 : // including TIFFTAG_EXIFIFD and TIFFTAG_GPSIFD
1466 30 : GUInt16 nIFD0Entries = (nEXIFTags ? 1 : 0) + (nGPSTags ? 1 : 0) +
1467 30 : static_cast<GUInt16>(mainTags.size());
1468 :
1469 30 : GUInt32 nBufferSize = EXIF_HEADER_SIZE + // Exif header
1470 : 4 + // Tiff signature
1471 : 4 + // Offset of IFD0
1472 : 2 + // Number of entries of IFD0
1473 30 : nIFD0Entries * TAG_SIZE + // Entries of IFD0
1474 : nOfflineSizeMain;
1475 :
1476 30 : if (nEXIFTags)
1477 : {
1478 24 : nBufferSize += 2 + // Number of entries of private EXIF IFD
1479 24 : nEXIFTags * TAG_SIZE + nOfflineSizeEXIF;
1480 : }
1481 :
1482 30 : if (nGPSTags)
1483 : {
1484 11 : nBufferSize += 2 + // Number of entries of private GPS IFD
1485 11 : nGPSTags * TAG_SIZE + nOfflineSizeGPS;
1486 : }
1487 :
1488 30 : GUInt16 nIFD1Entries = 0;
1489 30 : if (pabyThumbnail)
1490 : {
1491 6 : nIFD1Entries = 5;
1492 6 : nBufferSize += 4 + // Offset of IFD1
1493 : 2 + // Number of entries of IFD1
1494 6 : nIFD1Entries * TAG_SIZE + // Entries of IFD1
1495 : nThumbnailSize;
1496 : }
1497 30 : nBufferSize += 4; // Offset of next IFD
1498 :
1499 30 : GByte *pabyData = nullptr;
1500 30 : if (nBufferSize > 65536)
1501 : {
1502 1 : CPLError(CE_Warning, CPLE_AppDefined,
1503 : "Cannot write EXIF segment. "
1504 : "The size of the EXIF segment exceeds 65536 bytes");
1505 : }
1506 : else
1507 : {
1508 29 : pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBufferSize));
1509 : }
1510 30 : if (pabyData == nullptr)
1511 : {
1512 1 : return nullptr;
1513 : }
1514 :
1515 29 : memcpy(pabyData, "Exif\0\0", EXIF_HEADER_SIZE);
1516 29 : GUInt32 nBufferOff = EXIF_HEADER_SIZE;
1517 29 : GUInt32 nTIFFStartOff = nBufferOff;
1518 :
1519 : // TIFF little-endian signature.
1520 29 : const GUInt16 TIFF_LITTLEENDIAN = 0x4949;
1521 29 : WriteLEUInt16(pabyData, nBufferOff, TIFF_LITTLEENDIAN);
1522 29 : const GUInt16 TIFF_VERSION = 42;
1523 29 : WriteLEUInt16(pabyData, nBufferOff, TIFF_VERSION);
1524 :
1525 : // Offset of IFD0
1526 29 : WriteLEUInt32(pabyData, nBufferOff, nBufferOff - nTIFFStartOff + 4);
1527 :
1528 : // Number of entries of IFD0
1529 29 : WriteLEUInt16(pabyData, nBufferOff, nIFD0Entries);
1530 :
1531 29 : if (!mainTags.empty())
1532 : {
1533 8 : GUInt32 offsetIFDData =
1534 8 : nBufferOff - nTIFFStartOff + nIFD0Entries * TAG_SIZE + 4;
1535 8 : WriteTags(pabyData, nBufferOff, offsetIFDData, mainTags);
1536 : }
1537 :
1538 29 : GUInt32 nEXIFIFDOffset = 0;
1539 29 : if (nEXIFTags)
1540 : {
1541 23 : WriteTag(pabyData, nBufferOff, EXIFOFFSETTAG, TIFF_LONG, 1, 0);
1542 23 : nEXIFIFDOffset = nBufferOff - 4;
1543 : }
1544 :
1545 29 : GUInt32 nGPSIFDOffset = 0;
1546 29 : if (nGPSTags)
1547 : {
1548 11 : WriteTag(pabyData, nBufferOff, GPSOFFSETTAG, TIFF_LONG, 1, 0);
1549 11 : nGPSIFDOffset = nBufferOff - 4; // offset to patch
1550 : }
1551 :
1552 : // Offset of next IFD
1553 29 : GUInt32 nOffsetOfIFDAfterIFD0 = nBufferOff;
1554 29 : WriteLEUInt32(pabyData, nBufferOff, 0); // offset to patch
1555 :
1556 : // Space for offline tag values (already written)
1557 29 : nBufferOff += nOfflineSizeMain;
1558 :
1559 29 : if (nEXIFTags)
1560 : {
1561 : // Patch value of EXIFOFFSETTAG
1562 : {
1563 23 : GUInt32 nTmp = nEXIFIFDOffset;
1564 23 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1565 : }
1566 :
1567 : // Number of entries of EXIF IFD
1568 23 : WriteLEUInt16(pabyData, nBufferOff, nEXIFTags);
1569 :
1570 23 : GUInt32 offsetIFDData =
1571 23 : nBufferOff - nTIFFStartOff + nEXIFTags * TAG_SIZE;
1572 23 : WriteTags(pabyData, nBufferOff, offsetIFDData, exifTags);
1573 :
1574 : // Space for offline tag values (already written)
1575 23 : nBufferOff += nOfflineSizeEXIF;
1576 : }
1577 :
1578 29 : if (nGPSTags)
1579 : {
1580 : // Patch value of GPSOFFSETTAG
1581 : {
1582 11 : GUInt32 nTmp = nGPSIFDOffset;
1583 11 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1584 : }
1585 :
1586 : // Number of entries of GPS IFD
1587 11 : WriteLEUInt16(pabyData, nBufferOff, nGPSTags);
1588 :
1589 11 : GUInt32 offsetIFDData =
1590 11 : nBufferOff - nTIFFStartOff + nGPSTags * TAG_SIZE;
1591 11 : WriteTags(pabyData, nBufferOff, offsetIFDData, gpsTags);
1592 :
1593 : // Space for offline tag values (already written)
1594 11 : nBufferOff += nOfflineSizeGPS;
1595 : }
1596 :
1597 29 : if (nIFD1Entries)
1598 : {
1599 : // Patch value of offset after next IFD
1600 : {
1601 6 : GUInt32 nTmp = nOffsetOfIFDAfterIFD0;
1602 6 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1603 : }
1604 :
1605 : // Number of entries of IFD1
1606 6 : WriteLEUInt16(pabyData, nBufferOff, nIFD1Entries);
1607 :
1608 6 : const GUInt16 JPEG_TIFF_IMAGEWIDTH = 0x100;
1609 6 : const GUInt16 JPEG_TIFF_IMAGEHEIGHT = 0x101;
1610 6 : const GUInt16 JPEG_TIFF_COMPRESSION = 0x103;
1611 6 : const GUInt16 JPEG_EXIF_JPEGIFOFSET = 0x201;
1612 6 : const GUInt16 JPEG_EXIF_JPEGIFBYTECOUNT = 0x202;
1613 :
1614 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_IMAGEWIDTH, TIFF_LONG, 1,
1615 : nThumbnailWidth);
1616 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_IMAGEHEIGHT, TIFF_LONG, 1,
1617 : nThumbnailHeight);
1618 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_COMPRESSION, TIFF_SHORT, 1,
1619 : 6); // JPEG compression
1620 6 : WriteTag(pabyData, nBufferOff, JPEG_EXIF_JPEGIFOFSET, TIFF_LONG, 1,
1621 6 : nBufferSize - EXIF_HEADER_SIZE - nThumbnailSize);
1622 6 : WriteTag(pabyData, nBufferOff, JPEG_EXIF_JPEGIFBYTECOUNT, TIFF_LONG, 1,
1623 : nThumbnailSize);
1624 :
1625 : // Offset of next IFD
1626 6 : WriteLEUInt32(pabyData, nBufferOff, 0);
1627 : }
1628 :
1629 29 : CPLAssert(nBufferOff + nThumbnailSize == nBufferSize);
1630 29 : if (pabyThumbnail != nullptr && nThumbnailSize)
1631 6 : memcpy(pabyData + nBufferOff, pabyThumbnail, nThumbnailSize);
1632 :
1633 29 : *pnOutBufferSize = nBufferSize;
1634 29 : return pabyData;
1635 : }
1636 :
1637 : #ifdef DUMP_EXIF_ITEMS
1638 :
1639 : // To help generate the doc page
1640 : // g++ -DDUMP_EXIF_ITEMS gcore/gdalexif.cpp -o dumpexif -Iport -Igcore -Iogr -L.
1641 : // -lgdal
1642 :
1643 : int main()
1644 : {
1645 : printf("<table border=\"1\">\n"); /* ok */
1646 : printf("<tr><th>Metadata item name</th><th>Hex code</th>" /* ok */
1647 : "<th>Type</th><th>Number of values</th><th>Optionality</th></tr>\n");
1648 : for (size_t i = 0; exiftags[i].name[0] != '\0'; i++)
1649 : {
1650 : if (exiftags[i].datatype == TIFF_NOTYPE)
1651 : continue;
1652 : printf(/* ok */ "<tr><td>%s</td><td>0x%04X</td><td>%s</td><td>%s</"
1653 : "td><td>%s</"
1654 : "td></tr>\n",
1655 : exiftags[i].name, exiftags[i].tag,
1656 : exiftags[i].datatype == TIFF_BYTE ? "BYTE"
1657 : : exiftags[i].datatype == TIFF_ASCII ? "ASCII"
1658 : : exiftags[i].datatype == TIFF_UNDEFINED ? "UNDEFINED"
1659 : : exiftags[i].datatype == TIFF_SHORT ? "SHORT"
1660 : : exiftags[i].datatype == TIFF_LONG ? "LONG"
1661 : : exiftags[i].datatype == TIFF_RATIONAL ? "RATIONAL"
1662 : : exiftags[i].datatype == TIFF_SRATIONAL ? "SRATIONAL"
1663 : : "?????",
1664 : exiftags[i].length ? CPLSPrintf("%d", exiftags[i].length)
1665 : : "variable",
1666 : exiftags[i].comprCond == COND_MANDATORY ? "<b>Mandatory</b>"
1667 : : exiftags[i].comprCond == COND_OPTIONAL ? "Optional"
1668 : : exiftags[i].comprCond == COND_RECOMMENDED ? "Recommended"
1669 : : "?????");
1670 : }
1671 : printf("</table>\n"); /* ok */
1672 :
1673 : printf("<table border=\"1\">\n"); /* ok */
1674 : printf("<tr><th>Metadata item name</th><th>Hex code</th>" /* ok */
1675 : "<th>Type</th><th>Number of values</th><th>Optionality</th></tr>\n");
1676 : for (size_t i = 0; gpstags[i].name[0] != '\0'; i++)
1677 : {
1678 : if (gpstags[i].datatype == TIFF_NOTYPE)
1679 : continue;
1680 : printf(/* ok */
1681 : "<tr><td>%s</td><td>0x%04X</td><td>%s</td><td>%s</td><td>%s</"
1682 : "td></tr>\n",
1683 : gpstags[i].name, gpstags[i].tag,
1684 : gpstags[i].datatype == TIFF_BYTE ? "BYTE"
1685 : : gpstags[i].datatype == TIFF_ASCII ? "ASCII"
1686 : : gpstags[i].datatype == TIFF_UNDEFINED ? "UNDEFINED"
1687 : : gpstags[i].datatype == TIFF_SHORT ? "SHORT"
1688 : : gpstags[i].datatype == TIFF_LONG ? "LONG"
1689 : : gpstags[i].datatype == TIFF_RATIONAL ? "RATIONAL"
1690 : : gpstags[i].datatype == TIFF_SRATIONAL ? "SRATIONAL"
1691 : : "?????",
1692 : gpstags[i].length ? CPLSPrintf("%d", gpstags[i].length)
1693 : : "variable",
1694 : gpstags[i].comprCond == COND_MANDATORY ? "<b>Mandatory</b>"
1695 : : gpstags[i].comprCond == COND_OPTIONAL ? "Optional"
1696 : : gpstags[i].comprCond == COND_RECOMMENDED ? "Recommended"
1697 : : "?????");
1698 : }
1699 : printf("</table>\n"); /* ok */
1700 :
1701 : return 0;
1702 : }
1703 :
1704 : #endif
|