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