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