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

Generated by: LCOV version 1.14