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(char **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 (char **papszIter = papszEXIFMetadata; papszIter && *papszIter;
1099 : ++papszIter)
1100 : {
1101 471 : if (!STARTS_WITH_CI(*papszIter, "EXIF_"))
1102 316 : continue;
1103 465 : if (location == EXIFLocation::GPS_IFD &&
1104 155 : !STARTS_WITH_CI(*papszIter, "EXIF_GPS"))
1105 111 : continue;
1106 354 : if (location != EXIFLocation::GPS_IFD &&
1107 310 : STARTS_WITH_CI(*papszIter, "EXIF_GPS"))
1108 88 : continue;
1109 :
1110 266 : bool bFound = false;
1111 266 : size_t i = 0; // needed after loop
1112 9985 : for (; tagdescArray[i].name[0] != '\0'; i++)
1113 : {
1114 9983 : if (STARTS_WITH_CI(*papszIter, tagdescArray[i].name) &&
1115 312 : (*papszIter)[strlen(tagdescArray[i].name)] == '=')
1116 : {
1117 264 : bFound = true;
1118 264 : break;
1119 : }
1120 : }
1121 :
1122 266 : if (location == EXIFLocation::MAIN_IFD)
1123 : {
1124 111 : if (tagdescArray[i].tag > 0x8298) // EXIF_Copyright
1125 : {
1126 70 : continue;
1127 : }
1128 : }
1129 155 : else if (location == EXIFLocation::EXIF_IFD)
1130 : {
1131 111 : if (tagdescArray[i].tag <= 0x8298) // EXIF_Copyright
1132 : {
1133 41 : continue;
1134 : }
1135 : }
1136 :
1137 155 : char *pszKey = nullptr;
1138 155 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
1139 155 : if (!bFound || pszKey == nullptr || pszValue == nullptr)
1140 : {
1141 1 : CPLError(CE_Warning, CPLE_NotSupported,
1142 1 : "Cannot write unknown %s tag", pszKey ? pszKey : "");
1143 : }
1144 154 : else if (tagdescArray[i].datatype == TIFF_NOTYPE)
1145 : {
1146 0 : CPLDebug("EXIF", "Tag %s ignored on write", tagdescArray[i].name);
1147 : }
1148 : else
1149 : {
1150 308 : TagValue tag;
1151 154 : tag.tag = tagdescArray[i].tag;
1152 154 : tag.datatype = tagdescArray[i].datatype;
1153 154 : tag.nRelOffset = -1;
1154 :
1155 154 : if (tag.datatype == TIFF_ASCII)
1156 : {
1157 50 : if (tagdescArray[i].length == 0 ||
1158 36 : strlen(pszValue) + 1 == tagdescArray[i].length)
1159 : {
1160 34 : tag.pabyVal.reset(
1161 34 : reinterpret_cast<GByte *>(CPLStrdup(pszValue)));
1162 34 : tag.nLength = 1 + static_cast<int>(strlen(pszValue));
1163 : }
1164 16 : else if (strlen(pszValue) >= tagdescArray[i].length)
1165 : {
1166 1 : CPLError(CE_Warning, CPLE_AppDefined,
1167 : "Value of %s will be truncated",
1168 1 : tagdescArray[i].name);
1169 1 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1170 1 : CPLMalloc(tagdescArray[i].length)));
1171 1 : memcpy(tag.pabyVal.get(), pszValue, tagdescArray[i].length);
1172 1 : tag.nLength = tagdescArray[i].length;
1173 1 : (tag.pabyVal.get())[tag.nLength - 1] = '\0';
1174 : }
1175 : else
1176 : {
1177 15 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1178 15 : CPLMalloc(tagdescArray[i].length)));
1179 15 : memset(tag.pabyVal.get(), ' ', tagdescArray[i].length);
1180 15 : memcpy(tag.pabyVal.get(), pszValue, strlen(pszValue));
1181 15 : tag.nLength = tagdescArray[i].length;
1182 15 : (tag.pabyVal.get())[tag.nLength - 1] = '\0';
1183 : }
1184 50 : tag.nLengthBytes = tag.nLength;
1185 : }
1186 104 : else if (tag.datatype == TIFF_BYTE ||
1187 96 : tag.datatype == TIFF_UNDEFINED)
1188 : {
1189 32 : GUInt32 nValLength = 0;
1190 : std::unique_ptr<GByte, VSIFreeReleaser> pabyVal(
1191 32 : ParseUndefined(pszValue, &nValLength));
1192 32 : if (tagdescArray[i].length == 0 ||
1193 30 : nValLength == tagdescArray[i].length)
1194 : {
1195 30 : if (tag.tag == 0x9286 &&
1196 1 : strncmp(pszValue, "0x", 2) != 0) // EXIF_UserComment
1197 : {
1198 : const char *pszRealVal =
1199 1 : reinterpret_cast<const char *>(pabyVal.get());
1200 1 : const int nValueLen =
1201 1 : static_cast<int>(strlen(pszRealVal));
1202 : // 8 first bytes are the character code
1203 : // Set them to 0 to mean undefined
1204 1 : tag.pabyVal.reset(
1205 1 : static_cast<GByte *>(CPLCalloc(1, 8 + nValueLen)));
1206 1 : tag.nLength = 8 + nValueLen;
1207 1 : memcpy(tag.pabyVal.get() + 8, pszRealVal, nValueLen);
1208 : }
1209 : else
1210 : {
1211 29 : tag.pabyVal = std::move(pabyVal);
1212 29 : tag.nLength = nValLength;
1213 30 : }
1214 : }
1215 2 : else if (nValLength > tagdescArray[i].length)
1216 : {
1217 1 : CPLError(CE_Warning, CPLE_AppDefined,
1218 : "Value of %s will be truncated",
1219 1 : tagdescArray[i].name);
1220 1 : tag.pabyVal = std::move(pabyVal);
1221 1 : tag.nLength = tagdescArray[i].length;
1222 : }
1223 : else
1224 : {
1225 1 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1226 1 : CPLRealloc(pabyVal.release(), tagdescArray[i].length)));
1227 2 : memset(tag.pabyVal.get() + nValLength, '\0',
1228 1 : tagdescArray[i].length - nValLength);
1229 1 : tag.nLength = tagdescArray[i].length;
1230 : }
1231 32 : tag.nLengthBytes = tag.nLength;
1232 : }
1233 72 : else if (tag.datatype == TIFF_SHORT || tag.datatype == TIFF_LONG)
1234 : {
1235 26 : char **papszTokens = CSLTokenizeString2(pszValue, " ", 0);
1236 26 : GUInt32 nTokens = static_cast<GUInt32>(CSLCount(papszTokens));
1237 26 : const GUInt32 nDataTypeSize =
1238 26 : (tag.datatype == TIFF_SHORT) ? 2 : 4;
1239 26 : if (tagdescArray[i].length == 0 ||
1240 25 : nTokens == tagdescArray[i].length)
1241 : {
1242 : // ok
1243 : }
1244 2 : else if (nTokens > tagdescArray[i].length)
1245 : {
1246 1 : CPLError(CE_Warning, CPLE_AppDefined,
1247 : "Value of %s will be truncated",
1248 1 : tagdescArray[i].name);
1249 : }
1250 : else
1251 : {
1252 1 : CPLError(CE_Warning, CPLE_AppDefined,
1253 : "Not enough values for %s: %d expected. "
1254 : "Filling with zeroes",
1255 1 : tagdescArray[i].name, tagdescArray[i].length);
1256 : }
1257 :
1258 52 : tag.nLength = (tagdescArray[i].length == 0)
1259 26 : ? nTokens
1260 25 : : tagdescArray[i].length;
1261 26 : tag.pabyVal.reset(static_cast<GByte *>(CPLCalloc(
1262 26 : 1, cpl::fits_on<int>(nDataTypeSize * tag.nLength))));
1263 :
1264 26 : GUInt32 nOffset = 0;
1265 55 : for (GUInt32 j = 0; j < std::min(nTokens, tag.nLength); j++)
1266 : {
1267 29 : GUInt32 nVal = atoi(papszTokens[j]);
1268 29 : if (tag.datatype == TIFF_SHORT)
1269 14 : WriteLEUInt16(tag.pabyVal.get(), nOffset,
1270 : static_cast<GUInt16>(nVal));
1271 : else
1272 15 : WriteLEUInt32(tag.pabyVal.get(), nOffset, nVal);
1273 : }
1274 26 : CSLDestroy(papszTokens);
1275 :
1276 26 : tag.nLengthBytes = tag.nLength * nDataTypeSize;
1277 : }
1278 46 : else if (tag.datatype == TIFF_RATIONAL ||
1279 7 : tag.datatype == TIFF_SRATIONAL)
1280 : {
1281 46 : char **papszTokens = CSLTokenizeString2(pszValue, " ", 0);
1282 46 : GUInt32 nTokens = static_cast<GUInt32>(CSLCount(papszTokens));
1283 46 : const GUInt32 nDataTypeSize = 8;
1284 46 : if (tagdescArray[i].length == 0 ||
1285 46 : nTokens == tagdescArray[i].length)
1286 : {
1287 : // ok
1288 : }
1289 1 : else if (nTokens > tagdescArray[i].length)
1290 : {
1291 1 : CPLError(CE_Warning, CPLE_AppDefined,
1292 : "Value of %s will be truncated",
1293 1 : tagdescArray[i].name);
1294 : }
1295 : else
1296 : {
1297 0 : CPLError(CE_Warning, CPLE_AppDefined,
1298 : "Not enough values for %s: %d expected. "
1299 : "Filling with zeroes",
1300 0 : tagdescArray[i].name, tagdescArray[i].length);
1301 : }
1302 :
1303 92 : tag.nLength = (tagdescArray[i].length == 0)
1304 46 : ? nTokens
1305 46 : : tagdescArray[i].length;
1306 46 : tag.pabyVal.reset(reinterpret_cast<GByte *>(
1307 46 : CPLCalloc(1, nDataTypeSize * tag.nLength)));
1308 :
1309 46 : GUInt32 nOffset = 0;
1310 126 : for (GUInt32 j = 0; j < std::min(nTokens, tag.nLength); j++)
1311 : {
1312 : double dfVal =
1313 98 : CPLAtof(papszTokens[j][0] == '(' ? papszTokens[j] + 1
1314 18 : : papszTokens[j]);
1315 80 : GUInt32 nNum = 1;
1316 80 : GUInt32 nDenom = 0;
1317 80 : if (!GetNumDenomFromDouble(tag.datatype, dfVal, nNum,
1318 : nDenom))
1319 : {
1320 2 : CPLError(CE_Warning, CPLE_AppDefined,
1321 : "Value %f is illegal for tag %s", dfVal,
1322 2 : tagdescArray[i].name);
1323 : }
1324 :
1325 80 : WriteLEUInt32(tag.pabyVal.get(), nOffset, nNum);
1326 80 : WriteLEUInt32(tag.pabyVal.get(), nOffset, nDenom);
1327 : }
1328 46 : CSLDestroy(papszTokens);
1329 :
1330 46 : tag.nLengthBytes = tag.nLength * nDataTypeSize;
1331 : }
1332 : else
1333 : {
1334 : // Shouldn't happen. Programming error
1335 0 : CPLError(CE_Warning, CPLE_NotSupported,
1336 0 : "Unhandled type %d for tag %s", tag.datatype,
1337 0 : tagdescArray[i].name);
1338 : }
1339 :
1340 154 : if (tag.nLengthBytes != 0)
1341 : {
1342 154 : if (tag.nLengthBytes > 4)
1343 : {
1344 68 : tag.nRelOffset = nRelOffset;
1345 68 : nRelOffset += tag.nLengthBytes + (tag.nLengthBytes % 1);
1346 : }
1347 154 : tags.push_back(std::move(tag));
1348 : }
1349 : }
1350 155 : CPLFree(pszKey);
1351 : }
1352 :
1353 : // Sort tags by ascending order
1354 90 : std::sort(tags.begin(), tags.end());
1355 :
1356 : #ifdef notdef
1357 : if (location == EXIF_IFD &&
1358 : CSLFetchNameValue(papszEXIFMetadata, "EXIF_ExifVersion") == nullptr)
1359 : {
1360 : const GUInt16 EXIF_VERSION = 0x9000;
1361 : TagValue tag;
1362 : tag.tag = EXIF_VERSION;
1363 : tag.datatype = TIFF_UNDEFINED;
1364 : tag.pabyVal.reset(reinterpret_cast<GByte *>(CPLStrdup("0231")));
1365 : tag.nLength = 4;
1366 : tag.nLengthBytes = 4;
1367 : tag.nRelOffset = -1;
1368 : tags.push_back(std::move(tag));
1369 : }
1370 : #endif
1371 :
1372 90 : *pnOfflineSize = nRelOffset;
1373 :
1374 90 : return tags;
1375 : }
1376 :
1377 : /************************************************************************/
1378 : /* WriteTag() */
1379 : /************************************************************************/
1380 :
1381 64 : static void WriteTag(GByte *pabyData, GUInt32 &nBufferOff, GUInt16 nTag,
1382 : GDALEXIFTIFFDataType nType, GUInt32 nCount, GUInt32 nVal)
1383 : {
1384 64 : WriteLEUInt16(pabyData, nBufferOff, nTag);
1385 64 : WriteLEUInt16(pabyData, nBufferOff, static_cast<GUInt16>(nType));
1386 64 : WriteLEUInt32(pabyData, nBufferOff, nCount);
1387 64 : WriteLEUInt32(pabyData, nBufferOff, nVal);
1388 64 : }
1389 :
1390 : /************************************************************************/
1391 : /* WriteTags() */
1392 : /************************************************************************/
1393 :
1394 42 : static void WriteTags(GByte *pabyData, GUInt32 &nBufferOff,
1395 : GUInt32 offsetIFDData, const std::vector<TagValue> &tags)
1396 : {
1397 195 : for (const auto &tag : tags)
1398 : {
1399 153 : WriteLEUInt16(pabyData, nBufferOff, tag.tag);
1400 153 : WriteLEUInt16(pabyData, nBufferOff, static_cast<GUInt16>(tag.datatype));
1401 153 : WriteLEUInt32(pabyData, nBufferOff, tag.nLength);
1402 153 : if (tag.nRelOffset < 0)
1403 : {
1404 86 : CPLAssert(tag.nLengthBytes <= 4);
1405 86 : memcpy(pabyData + nBufferOff, tag.pabyVal.get(), tag.nLengthBytes);
1406 86 : nBufferOff += 4;
1407 : }
1408 : else
1409 : {
1410 67 : WriteLEUInt32(pabyData, nBufferOff, tag.nRelOffset + offsetIFDData);
1411 67 : memcpy(pabyData + EXIF_HEADER_SIZE + tag.nRelOffset + offsetIFDData,
1412 67 : tag.pabyVal.get(), tag.nLengthBytes);
1413 : }
1414 : }
1415 42 : }
1416 :
1417 : /************************************************************************/
1418 : /* EXIFCreate() */
1419 : /************************************************************************/
1420 :
1421 263 : GByte *EXIFCreate(char **papszEXIFMetadata, GByte *pabyThumbnail,
1422 : GUInt32 nThumbnailSize, GUInt32 nThumbnailWidth,
1423 : GUInt32 nThumbnailHeight, GUInt32 *pnOutBufferSize)
1424 : {
1425 263 : *pnOutBufferSize = 0;
1426 :
1427 263 : bool bHasEXIFMetadata = false;
1428 292 : for (char **papszIter = papszEXIFMetadata; papszIter && *papszIter;
1429 : ++papszIter)
1430 : {
1431 54 : if (STARTS_WITH_CI(*papszIter, "EXIF_"))
1432 : {
1433 25 : bHasEXIFMetadata = true;
1434 25 : break;
1435 : }
1436 : }
1437 263 : if (!bHasEXIFMetadata && pabyThumbnail == nullptr)
1438 : {
1439 : // Nothing to do
1440 233 : return nullptr;
1441 : }
1442 :
1443 30 : GUInt32 nOfflineSizeMain = 0;
1444 : std::vector<TagValue> mainTags = EXIFFormatTagValue(
1445 60 : papszEXIFMetadata, EXIFLocation::MAIN_IFD, &nOfflineSizeMain);
1446 :
1447 30 : GUInt32 nOfflineSizeEXIF = 0;
1448 : std::vector<TagValue> exifTags = EXIFFormatTagValue(
1449 60 : papszEXIFMetadata, EXIFLocation::EXIF_IFD, &nOfflineSizeEXIF);
1450 :
1451 30 : GUInt32 nOfflineSizeGPS = 0;
1452 : std::vector<TagValue> gpsTags = EXIFFormatTagValue(
1453 60 : papszEXIFMetadata, EXIFLocation::GPS_IFD, &nOfflineSizeGPS);
1454 :
1455 30 : const GUInt16 nEXIFTags = static_cast<GUInt16>(exifTags.size());
1456 30 : const GUInt16 nGPSTags = static_cast<GUInt16>(gpsTags.size());
1457 :
1458 : // including TIFFTAG_EXIFIFD and TIFFTAG_GPSIFD
1459 30 : GUInt16 nIFD0Entries = (nEXIFTags ? 1 : 0) + (nGPSTags ? 1 : 0) +
1460 30 : static_cast<GUInt16>(mainTags.size());
1461 :
1462 30 : GUInt32 nBufferSize = EXIF_HEADER_SIZE + // Exif header
1463 : 4 + // Tiff signature
1464 : 4 + // Offset of IFD0
1465 : 2 + // Number of entries of IFD0
1466 30 : nIFD0Entries * TAG_SIZE + // Entries of IFD0
1467 : nOfflineSizeMain;
1468 :
1469 30 : if (nEXIFTags)
1470 : {
1471 24 : nBufferSize += 2 + // Number of entries of private EXIF IFD
1472 24 : nEXIFTags * TAG_SIZE + nOfflineSizeEXIF;
1473 : }
1474 :
1475 30 : if (nGPSTags)
1476 : {
1477 11 : nBufferSize += 2 + // Number of entries of private GPS IFD
1478 11 : nGPSTags * TAG_SIZE + nOfflineSizeGPS;
1479 : }
1480 :
1481 30 : GUInt16 nIFD1Entries = 0;
1482 30 : if (pabyThumbnail)
1483 : {
1484 6 : nIFD1Entries = 5;
1485 6 : nBufferSize += 4 + // Offset of IFD1
1486 : 2 + // Number of entries of IFD1
1487 6 : nIFD1Entries * TAG_SIZE + // Entries of IFD1
1488 : nThumbnailSize;
1489 : }
1490 30 : nBufferSize += 4; // Offset of next IFD
1491 :
1492 30 : GByte *pabyData = nullptr;
1493 30 : if (nBufferSize > 65536)
1494 : {
1495 1 : CPLError(CE_Warning, CPLE_AppDefined,
1496 : "Cannot write EXIF segment. "
1497 : "The size of the EXIF segment exceeds 65536 bytes");
1498 : }
1499 : else
1500 : {
1501 29 : pabyData = static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBufferSize));
1502 : }
1503 30 : if (pabyData == nullptr)
1504 : {
1505 1 : return nullptr;
1506 : }
1507 :
1508 29 : memcpy(pabyData, "Exif\0\0", EXIF_HEADER_SIZE);
1509 29 : GUInt32 nBufferOff = EXIF_HEADER_SIZE;
1510 29 : GUInt32 nTIFFStartOff = nBufferOff;
1511 :
1512 : // TIFF little-endian signature.
1513 29 : const GUInt16 TIFF_LITTLEENDIAN = 0x4949;
1514 29 : WriteLEUInt16(pabyData, nBufferOff, TIFF_LITTLEENDIAN);
1515 29 : const GUInt16 TIFF_VERSION = 42;
1516 29 : WriteLEUInt16(pabyData, nBufferOff, TIFF_VERSION);
1517 :
1518 : // Offset of IFD0
1519 29 : WriteLEUInt32(pabyData, nBufferOff, nBufferOff - nTIFFStartOff + 4);
1520 :
1521 : // Number of entries of IFD0
1522 29 : WriteLEUInt16(pabyData, nBufferOff, nIFD0Entries);
1523 :
1524 29 : if (!mainTags.empty())
1525 : {
1526 8 : GUInt32 offsetIFDData =
1527 8 : nBufferOff - nTIFFStartOff + nIFD0Entries * TAG_SIZE + 4;
1528 8 : WriteTags(pabyData, nBufferOff, offsetIFDData, mainTags);
1529 : }
1530 :
1531 29 : GUInt32 nEXIFIFDOffset = 0;
1532 29 : if (nEXIFTags)
1533 : {
1534 23 : WriteTag(pabyData, nBufferOff, EXIFOFFSETTAG, TIFF_LONG, 1, 0);
1535 23 : nEXIFIFDOffset = nBufferOff - 4;
1536 : }
1537 :
1538 29 : GUInt32 nGPSIFDOffset = 0;
1539 29 : if (nGPSTags)
1540 : {
1541 11 : WriteTag(pabyData, nBufferOff, GPSOFFSETTAG, TIFF_LONG, 1, 0);
1542 11 : nGPSIFDOffset = nBufferOff - 4; // offset to patch
1543 : }
1544 :
1545 : // Offset of next IFD
1546 29 : GUInt32 nOffsetOfIFDAfterIFD0 = nBufferOff;
1547 29 : WriteLEUInt32(pabyData, nBufferOff, 0); // offset to patch
1548 :
1549 : // Space for offline tag values (already written)
1550 29 : nBufferOff += nOfflineSizeMain;
1551 :
1552 29 : if (nEXIFTags)
1553 : {
1554 : // Patch value of EXIFOFFSETTAG
1555 : {
1556 23 : GUInt32 nTmp = nEXIFIFDOffset;
1557 23 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1558 : }
1559 :
1560 : // Number of entries of EXIF IFD
1561 23 : WriteLEUInt16(pabyData, nBufferOff, nEXIFTags);
1562 :
1563 23 : GUInt32 offsetIFDData =
1564 23 : nBufferOff - nTIFFStartOff + nEXIFTags * TAG_SIZE;
1565 23 : WriteTags(pabyData, nBufferOff, offsetIFDData, exifTags);
1566 :
1567 : // Space for offline tag values (already written)
1568 23 : nBufferOff += nOfflineSizeEXIF;
1569 : }
1570 :
1571 29 : if (nGPSTags)
1572 : {
1573 : // Patch value of GPSOFFSETTAG
1574 : {
1575 11 : GUInt32 nTmp = nGPSIFDOffset;
1576 11 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1577 : }
1578 :
1579 : // Number of entries of GPS IFD
1580 11 : WriteLEUInt16(pabyData, nBufferOff, nGPSTags);
1581 :
1582 11 : GUInt32 offsetIFDData =
1583 11 : nBufferOff - nTIFFStartOff + nGPSTags * TAG_SIZE;
1584 11 : WriteTags(pabyData, nBufferOff, offsetIFDData, gpsTags);
1585 :
1586 : // Space for offline tag values (already written)
1587 11 : nBufferOff += nOfflineSizeGPS;
1588 : }
1589 :
1590 29 : if (nIFD1Entries)
1591 : {
1592 : // Patch value of offset after next IFD
1593 : {
1594 6 : GUInt32 nTmp = nOffsetOfIFDAfterIFD0;
1595 6 : WriteLEUInt32(pabyData, nTmp, nBufferOff - nTIFFStartOff);
1596 : }
1597 :
1598 : // Number of entries of IFD1
1599 6 : WriteLEUInt16(pabyData, nBufferOff, nIFD1Entries);
1600 :
1601 6 : const GUInt16 JPEG_TIFF_IMAGEWIDTH = 0x100;
1602 6 : const GUInt16 JPEG_TIFF_IMAGEHEIGHT = 0x101;
1603 6 : const GUInt16 JPEG_TIFF_COMPRESSION = 0x103;
1604 6 : const GUInt16 JPEG_EXIF_JPEGIFOFSET = 0x201;
1605 6 : const GUInt16 JPEG_EXIF_JPEGIFBYTECOUNT = 0x202;
1606 :
1607 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_IMAGEWIDTH, TIFF_LONG, 1,
1608 : nThumbnailWidth);
1609 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_IMAGEHEIGHT, TIFF_LONG, 1,
1610 : nThumbnailHeight);
1611 6 : WriteTag(pabyData, nBufferOff, JPEG_TIFF_COMPRESSION, TIFF_SHORT, 1,
1612 : 6); // JPEG compression
1613 6 : WriteTag(pabyData, nBufferOff, JPEG_EXIF_JPEGIFOFSET, TIFF_LONG, 1,
1614 6 : nBufferSize - EXIF_HEADER_SIZE - nThumbnailSize);
1615 6 : WriteTag(pabyData, nBufferOff, JPEG_EXIF_JPEGIFBYTECOUNT, TIFF_LONG, 1,
1616 : nThumbnailSize);
1617 :
1618 : // Offset of next IFD
1619 6 : WriteLEUInt32(pabyData, nBufferOff, 0);
1620 : }
1621 :
1622 29 : CPLAssert(nBufferOff + nThumbnailSize == nBufferSize);
1623 29 : if (pabyThumbnail != nullptr && nThumbnailSize)
1624 6 : memcpy(pabyData + nBufferOff, pabyThumbnail, nThumbnailSize);
1625 :
1626 29 : *pnOutBufferSize = nBufferSize;
1627 29 : return pabyData;
1628 : }
1629 :
1630 : #ifdef DUMP_EXIF_ITEMS
1631 :
1632 : // To help generate the doc page
1633 : // g++ -DDUMP_EXIF_ITEMS gcore/gdalexif.cpp -o dumpexif -Iport -Igcore -Iogr -L.
1634 : // -lgdal
1635 :
1636 : int main()
1637 : {
1638 : printf("<table border=\"1\">\n"); /* ok */
1639 : printf("<tr><th>Metadata item name</th><th>Hex code</th>" /* ok */
1640 : "<th>Type</th><th>Number of values</th><th>Optionality</th></tr>\n");
1641 : for (size_t i = 0; exiftags[i].name[0] != '\0'; i++)
1642 : {
1643 : if (exiftags[i].datatype == TIFF_NOTYPE)
1644 : continue;
1645 : printf(/* ok */ "<tr><td>%s</td><td>0x%04X</td><td>%s</td><td>%s</"
1646 : "td><td>%s</"
1647 : "td></tr>\n",
1648 : exiftags[i].name, exiftags[i].tag,
1649 : exiftags[i].datatype == TIFF_BYTE ? "BYTE"
1650 : : exiftags[i].datatype == TIFF_ASCII ? "ASCII"
1651 : : exiftags[i].datatype == TIFF_UNDEFINED ? "UNDEFINED"
1652 : : exiftags[i].datatype == TIFF_SHORT ? "SHORT"
1653 : : exiftags[i].datatype == TIFF_LONG ? "LONG"
1654 : : exiftags[i].datatype == TIFF_RATIONAL ? "RATIONAL"
1655 : : exiftags[i].datatype == TIFF_SRATIONAL ? "SRATIONAL"
1656 : : "?????",
1657 : exiftags[i].length ? CPLSPrintf("%d", exiftags[i].length)
1658 : : "variable",
1659 : exiftags[i].comprCond == COND_MANDATORY ? "<b>Mandatory</b>"
1660 : : exiftags[i].comprCond == COND_OPTIONAL ? "Optional"
1661 : : exiftags[i].comprCond == COND_RECOMMENDED ? "Recommended"
1662 : : "?????");
1663 : }
1664 : printf("</table>\n"); /* ok */
1665 :
1666 : printf("<table border=\"1\">\n"); /* ok */
1667 : printf("<tr><th>Metadata item name</th><th>Hex code</th>" /* ok */
1668 : "<th>Type</th><th>Number of values</th><th>Optionality</th></tr>\n");
1669 : for (size_t i = 0; gpstags[i].name[0] != '\0'; i++)
1670 : {
1671 : if (gpstags[i].datatype == TIFF_NOTYPE)
1672 : continue;
1673 : printf(/* ok */
1674 : "<tr><td>%s</td><td>0x%04X</td><td>%s</td><td>%s</td><td>%s</"
1675 : "td></tr>\n",
1676 : gpstags[i].name, gpstags[i].tag,
1677 : gpstags[i].datatype == TIFF_BYTE ? "BYTE"
1678 : : gpstags[i].datatype == TIFF_ASCII ? "ASCII"
1679 : : gpstags[i].datatype == TIFF_UNDEFINED ? "UNDEFINED"
1680 : : gpstags[i].datatype == TIFF_SHORT ? "SHORT"
1681 : : gpstags[i].datatype == TIFF_LONG ? "LONG"
1682 : : gpstags[i].datatype == TIFF_RATIONAL ? "RATIONAL"
1683 : : gpstags[i].datatype == TIFF_SRATIONAL ? "SRATIONAL"
1684 : : "?????",
1685 : gpstags[i].length ? CPLSPrintf("%d", gpstags[i].length)
1686 : : "variable",
1687 : gpstags[i].comprCond == COND_MANDATORY ? "<b>Mandatory</b>"
1688 : : gpstags[i].comprCond == COND_OPTIONAL ? "Optional"
1689 : : gpstags[i].comprCond == COND_RECOMMENDED ? "Recommended"
1690 : : "?????");
1691 : }
1692 : printf("</table>\n"); /* ok */
1693 :
1694 : return 0;
1695 : }
1696 :
1697 : #endif
|