EVOLUTION-MANAGER
Edit File: gdalexif.cpp
/****************************************************************************** * * Project: GDAL * Purpose: Implements a EXIF directory reader * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam * Copyright (c) 2012, Even Rouault <even dot rouault at mines-paris dot org> * * Portions Copyright (c) Her majesty the Queen in right of Canada as * represented by the Minister of National Defence, 2006. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "gdal_priv.h" #include "gdalexif.h" #include <climits> #include <cstddef> #include <cstdio> #include <cstring> #if HAVE_FCNTL_H # include <fcntl.h> #endif #include <vector> #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_string.h" #include "cpl_vsi.h" using std::vector; CPL_CVSID("$Id: gdalexif.cpp 36523 2016-11-27 04:13:26Z goatbar $"); static const int MAXSTRINGLENGTH = 65535; static const int EXIFOFFSETTAG = 0x8769; static const int INTEROPERABILITYOFFSET = 0xA005; static const int GPSOFFSETTAG = 0x8825; /************************************************************************/ /* EXIFPrintData() */ /************************************************************************/ static void EXIFPrintData(char* pszData, GUInt16 type, GUInt32 count, const unsigned char* data) { const char* sep = ""; char szTemp[128]; char* pszDataEnd = pszData; pszData[0]='\0'; switch (type) { case TIFF_UNDEFINED: case TIFF_BYTE: for(;count>0;count--) { snprintf(szTemp, sizeof(szTemp), "%s%#02x", sep, *data++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; case TIFF_SBYTE: for(;count>0;count--) { snprintf(szTemp, sizeof(szTemp), "%s%d", sep, *(const char *)data++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; case TIFF_ASCII: memcpy( pszData, data, count ); pszData[count] = '\0'; break; case TIFF_SHORT: { const GUInt16 *wp = (const GUInt16*)data; for(;count>0;count--) { snprintf(szTemp, sizeof(szTemp), "%s%u", sep, *wp++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_SSHORT: { const GInt16 *wp = (const GInt16*)data; for(;count>0;count--) { snprintf(szTemp, sizeof(szTemp), "%s%d", sep, *wp++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_LONG: { const GUInt32 *lp = (const GUInt32*)data; for(;count>0;count--) { snprintf(szTemp, sizeof(szTemp), "%s%lu", sep, (unsigned long) *lp++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_SLONG: { const GInt32 *lp = (const GInt32*)data; for(;count>0;count--) { snprintf(szTemp, sizeof(szTemp), "%s%ld", sep, (long) *lp++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_RATIONAL: { const GUInt32 *lp = (const GUInt32*)data; // if(bSwabflag) // TIFFSwabArrayOfLong((GUInt32*) data, 2*count); for(;count>0;count--) { if( (lp[0]==0) && (lp[1] == 0) ) { snprintf(szTemp, sizeof(szTemp), "%s(0)",sep); } else{ CPLsnprintf(szTemp, sizeof(szTemp), "%s(%g)", sep, (double) lp[0]/ (double)lp[1]); } sep = " "; lp += 2; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_SRATIONAL: { const GInt32 *lp = (const GInt32*)data; for(;count>0;count--) { CPLsnprintf(szTemp, sizeof(szTemp), "%s(%g)", sep, (float) lp[0]/ (float) lp[1]); sep = " "; lp += 2; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_FLOAT: { const float *fp = (const float *)data; for(;count>0;count--) { CPLsnprintf(szTemp, sizeof(szTemp), "%s%g", sep, *fp++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } case TIFF_DOUBLE: { const double *dp = (const double *)data; for(;count>0;count--) { CPLsnprintf(szTemp, sizeof(szTemp), "%s%g", sep, *dp++); sep = " "; if (strlen(szTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) break; strcat(pszDataEnd,szTemp); pszDataEnd += strlen(pszDataEnd); } break; } default: return; } if (type != TIFF_ASCII && count != 0) { CPLError(CE_Warning, CPLE_AppDefined, "EXIF metadata truncated"); } } /************************************************************************/ /* EXIFExtractMetadata() */ /* */ /* Extract all entry from a IFD */ /************************************************************************/ CPLErr EXIFExtractMetadata(char**& papszMetadata, void *fpInL, int nOffset, int bSwabflag, int nTIFFHEADER, int& nExifOffset, int& nInterOffset, int& nGPSOffset) { /* -------------------------------------------------------------------- */ /* Read number of entry in directory */ /* -------------------------------------------------------------------- */ GUInt16 nEntryCount; VSILFILE * const fp = static_cast<VSILFILE *>(fpInL); if( nOffset > INT_MAX - nTIFFHEADER || VSIFSeekL(fp, nOffset+nTIFFHEADER, SEEK_SET) != 0 || VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fp) != sizeof(GUInt16) ) { CPLError( CE_Failure, CPLE_AppDefined, "Error reading EXIF Directory count at " CPL_FRMT_GUIB, static_cast<vsi_l_offset>(nOffset) + nTIFFHEADER ); return CE_Failure; } if (bSwabflag) TIFFSwabShort(&nEntryCount); // Some apps write empty directories - see bug 1523. if( nEntryCount == 0 ) return CE_None; // Some files are corrupt, a large entry count is a sign of this. if( nEntryCount > 125 ) { CPLError( CE_Warning, CPLE_AppDefined, "Ignoring EXIF directory with unlikely entry count (%d).", nEntryCount ); return CE_Warning; } GDALEXIFTIFFDirEntry *poTIFFDir = static_cast<GDALEXIFTIFFDirEntry *>( CPLMalloc(nEntryCount * sizeof(GDALEXIFTIFFDirEntry)) ); /* -------------------------------------------------------------------- */ /* Read all directory entries */ /* -------------------------------------------------------------------- */ { const unsigned int n = static_cast<int>(VSIFReadL( poTIFFDir, 1,nEntryCount*sizeof(GDALEXIFTIFFDirEntry),fp)); if (n != nEntryCount*sizeof(GDALEXIFTIFFDirEntry)) { CPLError( CE_Failure, CPLE_AppDefined, "Could not read all directories"); CPLFree(poTIFFDir); return CE_Failure; } } /* -------------------------------------------------------------------- */ /* Parse all entry information in this directory */ /* -------------------------------------------------------------------- */ vector<char> oTempStorage(MAXSTRINGLENGTH+1, 0); char * const szTemp = &oTempStorage[0]; char szName[128]; GDALEXIFTIFFDirEntry *poTIFFDirEntry = poTIFFDir; for( unsigned int i = nEntryCount; i > 0; i--,poTIFFDirEntry++ ) { if (bSwabflag) { TIFFSwabShort(&poTIFFDirEntry->tdir_tag); TIFFSwabShort(&poTIFFDirEntry->tdir_type); TIFFSwabLong (&poTIFFDirEntry->tdir_count); TIFFSwabLong (&poTIFFDirEntry->tdir_offset); } /* -------------------------------------------------------------------- */ /* Find Tag name in table */ /* -------------------------------------------------------------------- */ szName[0] = '\0'; szTemp[0] = '\0'; for ( const struct tagname *poExifTags = tagnames; poExifTags->tag; poExifTags++) { if(poExifTags->tag == poTIFFDirEntry->tdir_tag) { CPLAssert( NULL != poExifTags->name ); CPLStrlcpy(szName, poExifTags->name, sizeof(szName)); break; } } if( nOffset == nGPSOffset) { for( const struct gpsname *poGPSTags = gpstags; poGPSTags->tag != 0xffff; poGPSTags++ ) { if( poGPSTags->tag == poTIFFDirEntry->tdir_tag ) { CPLAssert( NULL != poGPSTags->name ); CPLStrlcpy(szName, poGPSTags->name, sizeof(szName)); break; } } } /* -------------------------------------------------------------------- */ /* If the tag was not found, look into the interoperability table */ /* -------------------------------------------------------------------- */ if( nOffset == nInterOffset ) { const struct intr_tag *poInterTags = intr_tags; for( ; poInterTags->tag; poInterTags++) if(poInterTags->tag == poTIFFDirEntry->tdir_tag) { CPLAssert( NULL != poInterTags->name ); CPLStrlcpy(szName, poInterTags->name, sizeof(szName)); break; } } /* -------------------------------------------------------------------- */ /* Save important directory tag offset */ /* -------------------------------------------------------------------- */ // Our current API uses int32 and not uint32 if( poTIFFDirEntry->tdir_offset < INT_MAX ) { if( poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG ) nExifOffset=poTIFFDirEntry->tdir_offset; else if( poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET ) nInterOffset=poTIFFDirEntry->tdir_offset; else if( poTIFFDirEntry->tdir_tag == GPSOFFSETTAG ) nGPSOffset=poTIFFDirEntry->tdir_offset; } /* -------------------------------------------------------------------- */ /* If we didn't recognise the tag just ignore it. To see all */ /* tags comment out the continue. */ /* -------------------------------------------------------------------- */ if( szName[0] == '\0' ) { snprintf( szName, sizeof(szName), "EXIF_%u", poTIFFDirEntry->tdir_tag ); continue; } /* -------------------------------------------------------------------- */ /* For UserComment we need to ignore the language binding and */ /* just return the actual contents. */ /* -------------------------------------------------------------------- */ if( EQUAL(szName,"EXIF_UserComment") ) { poTIFFDirEntry->tdir_type = TIFF_ASCII; if( poTIFFDirEntry->tdir_count >= 8 ) { poTIFFDirEntry->tdir_count -= 8; poTIFFDirEntry->tdir_offset += 8; } } /* -------------------------------------------------------------------- */ /* Make some UNDEFINED or BYTE fields ASCII for readability. */ /* -------------------------------------------------------------------- */ if( EQUAL(szName,"EXIF_ExifVersion") || EQUAL(szName,"EXIF_FlashPixVersion") || EQUAL(szName,"EXIF_MakerNote") || EQUAL(szName,"GPSProcessingMethod") ) poTIFFDirEntry->tdir_type = TIFF_ASCII; /* -------------------------------------------------------------------- */ /* Print tags */ /* -------------------------------------------------------------------- */ const int nDataWidth = TIFFDataWidth((GDALEXIFTIFFDataType) poTIFFDirEntry->tdir_type); const int space = poTIFFDirEntry->tdir_count * nDataWidth; /* Previous multiplication could overflow, hence this additional check */ if( poTIFFDirEntry->tdir_count > static_cast<GUInt32>(MAXSTRINGLENGTH) ) { CPLError( CE_Warning, CPLE_AppDefined, "Too many bytes in tag: %u, ignoring tag.", poTIFFDirEntry->tdir_count ); } else if (nDataWidth == 0 || poTIFFDirEntry->tdir_type >= TIFF_IFD ) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid or unhandled EXIF data type: %d, ignoring tag.", poTIFFDirEntry->tdir_type ); } /* -------------------------------------------------------------------- */ /* This is at most 4 byte data so we can read it from tdir_offset */ /* -------------------------------------------------------------------- */ else if (space >= 0 && space <= 4) { unsigned char data[4]; memcpy(data, &poTIFFDirEntry->tdir_offset, 4); if (bSwabflag) { // Unswab 32bit value, and reswab per data type. TIFFSwabLong((GUInt32*) data); switch (poTIFFDirEntry->tdir_type) { case TIFF_LONG: case TIFF_SLONG: case TIFF_FLOAT: TIFFSwabLong((GUInt32*) data); break; case TIFF_SSHORT: case TIFF_SHORT: TIFFSwabArrayOfShort((GUInt16*) data, poTIFFDirEntry->tdir_count); break; default: break; } } /* coverity[overrun-buffer-arg] */ EXIFPrintData(szTemp, poTIFFDirEntry->tdir_type, poTIFFDirEntry->tdir_count, data); } /* -------------------------------------------------------------------- */ /* The data is being read where tdir_offset point to in the file */ /* -------------------------------------------------------------------- */ else if (space > 0 && space < MAXSTRINGLENGTH) { unsigned char *data = (unsigned char *)VSIMalloc(space); if (data) { CPL_IGNORE_RET_VAL(VSIFSeekL(fp,poTIFFDirEntry->tdir_offset+nTIFFHEADER,SEEK_SET)); CPL_IGNORE_RET_VAL(VSIFReadL(data, 1, space, fp)); if (bSwabflag) { switch (poTIFFDirEntry->tdir_type) { case TIFF_SHORT: case TIFF_SSHORT: TIFFSwabArrayOfShort((GUInt16*) data, poTIFFDirEntry->tdir_count); break; case TIFF_LONG: case TIFF_SLONG: case TIFF_FLOAT: TIFFSwabArrayOfLong((GUInt32*) data, poTIFFDirEntry->tdir_count); break; case TIFF_RATIONAL: case TIFF_SRATIONAL: TIFFSwabArrayOfLong((GUInt32*) data, 2*poTIFFDirEntry->tdir_count); break; case TIFF_DOUBLE: TIFFSwabArrayOfDouble((double*) data, poTIFFDirEntry->tdir_count); break; default: break; } } EXIFPrintData(szTemp, poTIFFDirEntry->tdir_type, poTIFFDirEntry->tdir_count, data); CPLFree(data); } } else { CPLError( CE_Warning, CPLE_AppDefined, "Invalid EXIF header size: %ld, ignoring tag.", (long) space ); } papszMetadata = CSLSetNameValue(papszMetadata, szName, szTemp); } CPLFree(poTIFFDir); return CE_None; }