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