LCOV - code coverage report
Current view: top level - gcore - gdalexif.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 548 676 81.1 %
Date: 2025-07-05 13:22:42 Functions: 16 16 100.0 %

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

Generated by: LCOV version 1.14