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