Coverage Report

Created: 2025-06-13 06:18

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