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