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