diff options
Diffstat (limited to 'XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp')
-rw-r--r-- | XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp | 267 |
1 files changed, 157 insertions, 110 deletions
diff --git a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp index 40cb9bd..a3bf808 100644 --- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp @@ -11,8 +11,11 @@ #include "public/include/XMP_Const.h" #include "XMPFiles/source/FormatSupport/TIFF_Support.hpp" + #include "source/XIO.hpp" +#include "source/EndianUtils.hpp" + // ================================================================================================= /// \file TIFF_FileWriter.cpp /// \brief TIFF_FileWriter is used for memory-based read-write access and all file-based access. @@ -586,15 +589,26 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo memcpy ( this->memStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above. this->ownedStream = true; } - + this->tiffLength = length; XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset. - // Find and process the primary, Exif, GPS, and Interoperability IFDs. + // Find and process the primary, thumbnail, Exif, GPS, and Interoperability IFDs. XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->memStream, length ); - if ( primaryIFDOffset != 0 ) (void) this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); + if ( primaryIFDOffset != 0 ) { + XMP_Uns32 tnailOffset = this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); + if ( tnailOffset != 0 ) { + if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) { // Remove a bad Thumbnail IFD Offset + ( void ) this->ProcessMemoryIFD ( tnailOffset, kTIFF_TNailIFD ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); + this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_TNailIFD ); + } + } + } const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->dataLen == 4) ) { @@ -605,9 +619,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->dataLen == 4) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); - if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset. + if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset. (void) this->ProcessMemoryIFD ( gpsOffset, kTIFF_GPSInfoIFD ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); } } @@ -615,9 +631,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); - if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset. + if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset. (void) this->ProcessMemoryIFD ( interopOffset, kTIFF_InteropIFD ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); } } @@ -653,16 +671,22 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { - XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); } XMP_Uns8* ifdPtr = this->memStream + ifdOffset; XMP_Uns16 tagCount = this->GetUns16 ( ifdPtr ); RawIFDEntry* ifdEntries = (RawIFDEntry*)(ifdPtr+2); - if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); + if ( tagCount >= 0x8000 ) { + XMP_Error error ( kXMPErr_BadTIFF, "Outrageous IFD count" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); + } + if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) { - XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); + XMP_Error error ( kXMPErr_BadTIFF, "Out of bounds IFD" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); } ifdInfo.origIFDOffset = ifdOffset; @@ -682,7 +706,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd InternalTagInfo& mapTag = newPos->second; mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type]; - mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. +#if SUNOS_SPARC + mapTag.smallValue = IE.getUns32(&rawTag->dataOrOffset); +#else + mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering. +#endif //#if SUNOS_SPARC if ( mapTag.dataLen <= 4 ) { mapTag.origDataOffset = ifdOffset + 2 + (12 * (XMP_Uns32)i) + 8; // Compute the data offset. @@ -720,39 +748,50 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) { - bool ok; - IOBuffer ioBuf; this->DeleteExistingInfo(); this->fileParsed = true; this->tiffLength = (XMP_Uns32) fileRef->Length(); - if ( this->tiffLength == 0 ) return; + if ( this->tiffLength < 8 ) return; // Ignore empty or impossibly short. + fileRef->Rewind ( ); XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset. // Find and process the primary, Exif, GPS, and Interoperability IFDs. - ioBuf.filePos = 0; - fileRef->Rewind ( ); - ok = CheckFileSpace ( fileRef, &ioBuf, 8 ); - if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF ); + XMP_Uns8 tiffHeader [8]; + fileRef->ReadAll ( tiffHeader, sizeof(tiffHeader) ); + XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( tiffHeader, this->tiffLength ); - XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength ); - - if ( primaryIFDOffset != 0 ) (void) this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf ); + if ( primaryIFDOffset == 0 ) { + return; + } else { + XMP_Uns32 tnailOffset = this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef ); + if ( tnailOffset != 0 ) { + if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) { + ( void ) this->ProcessFileIFD ( kTIFF_TNailIFD, tnailOffset, fileRef ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); + this->DeleteTag( kTIFF_PrimaryIFD, kTIFF_TNailIFD ); + } + } + } const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) { XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr ); - (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef, &ioBuf ); + (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef ); } const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->count == 1) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); - if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset. - (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef, &ioBuf ); + if ( IsOffsetValid (gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset. + (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); } } @@ -760,9 +799,11 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); - if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset. - (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef, &ioBuf ); + if ( IsOffsetValid (interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset. + (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); } } @@ -792,25 +833,36 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) // ================================================================================================= // TIFF_FileWriter::ProcessFileIFD // =============================== +// +// Each IFD has a UInt16 count of IFD entries, a sequence of 12 byte IFD entries, then a UInt32 +// offset to the next IFD. The integer byte order is determined by the II or MM at the TIFF start. -XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef, void* _ioBuf ) +XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef ) { - IOBuffer* ioBuf = (IOBuffer*)_ioBuf; // *** Temporary hack, _ioBuf is IOBuffer* but don't want to include XIO.hpp. + static const size_t ifdBufferSize = 12*65536; // Enough for the largest possible IFD. + std::vector<XMP_Uns8> ifdBuffer(ifdBufferSize); + XMP_Uns8 intBuffer [4]; // For the IFD count and offset to next IFD. + InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); } - MoveToOffset ( fileRef, ifdOffset, ioBuf ); // Move to the start of the IFD. + fileRef->Seek ( ifdOffset, kXMP_SeekFromStart ); + if ( ! XIO::CheckFileSpace ( fileRef, 2 ) ) return 0; // Bail for a truncated file. + fileRef->ReadAll ( intBuffer, 2 ); - bool ok = CheckFileSpace ( fileRef, ioBuf, 2 ); - if ( ! ok ) XMP_Throw ( "IFD count missing", kXMPErr_BadTIFF ); - XMP_Uns16 tagCount = this->GetUns16 ( ioBuf->ptr ); + XMP_Uns16 tagCount = this->GetUns16 ( intBuffer ); + if ( tagCount >= 0x8000 ) return 0; // Maybe wrong byte order. + if ( ! XIO::CheckFileSpace ( fileRef, 12*tagCount ) ) return 0; // Bail for a truncated file. + fileRef->ReadAll ( &ifdBuffer[0], 12*tagCount ); - if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); - if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) { - XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); + if ( ! XIO::CheckFileSpace ( fileRef, 4 ) ) { + ifdInfo.origNextIFD = 0; // Tolerate a trncated file, do the remaining processing. + } else { + fileRef->ReadAll ( intBuffer, 4 ); + ifdInfo.origNextIFD = this->GetUns32 ( intBuffer ); } ifdInfo.origIFDOffset = ifdOffset; @@ -822,13 +874,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X // sorted output. Plus the "map[key] = value" assignment conveniently keeps the last encountered // value, following Photoshop's behavior. - ioBuf->ptr += 2; // Move to the first IFD entry. + XMP_Uns8* ifdPtr = &ifdBuffer[0]; // Move to the first IFD entry. - for ( XMP_Uns16 i = 0; i < tagCount; ++i, ioBuf->ptr += 12 ) { + for ( XMP_Uns16 i = 0; i < tagCount; ++i, ifdPtr += 12 ) { - if ( ! CheckFileSpace ( fileRef, ioBuf, 12 ) ) XMP_Throw ( "EOF within IFD", kXMPErr_BadTIFF ); - - RawIFDEntry* rawTag = (RawIFDEntry*)ioBuf->ptr; + RawIFDEntry* rawTag = (RawIFDEntry*)ifdPtr; XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type ); if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag. @@ -840,7 +890,7 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X InternalTagInfo& mapTag = newPos->second; mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type]; - mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. + mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering. if ( mapTag.dataLen <= 4 ) { mapTag.dataPtr = (XMP_Uns8*) &mapTag.smallValue; @@ -861,25 +911,14 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X } - if ( ! CheckFileSpace ( fileRef, ioBuf, 4 ) ) XMP_Throw ( "EOF at next IFD offset", kXMPErr_BadTIFF ); - ifdInfo.origNextIFD = this->GetUns32 ( ioBuf->ptr ); - - // --------------------------------------------------------------------------------------------- - // Go back over the tag map and extract the data for large recognized tags. This is done in 2 - // passes, in order to lessen the typical amount of I/O. On the first pass make sure we have at - // least 32K of data following the IFD in the buffer, and extract all of the values in that - // portion. This should cover an original file, or the appended values with an appended IFD. - - if ( (ioBuf->limit - ioBuf->ptr) < 32*1024 ) RefillBuffer ( fileRef, ioBuf ); + // ------------------------------------------------------------------------ + // Go back over the tag map and extract the data for large recognized tags. InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); const XMP_Uns16* knownTagPtr = sKnownTags[ifd]; // Points into the ordered recognized tag list. - XMP_Uns32 bufBegin = (XMP_Uns32)ioBuf->filePos; // TIFF stream bounds for the current buffer. - XMP_Uns32 bufEnd = bufBegin + (XMP_Uns32)ioBuf->len; - for ( ; tagPos != tagEnd; ++tagPos ) { InternalTagInfo* currTag = &tagPos->second; @@ -889,46 +928,10 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X while ( *knownTagPtr < currTag->id ) ++knownTagPtr; if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. - if ( (bufBegin <= currTag->origDataOffset) && ((currTag->origDataOffset + currTag->dataLen) <= bufEnd) ) { - // This value is already fully within the current I/O buffer, copy it. - MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf ); - currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen ); - if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory ); - memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above. - } - - } - - // --------------------------------------------------------------------------------------------- - // Now the second large value pass. This will reposition the I/O buffer as necessary. Hopefully - // just once, to pick up the span of data not covered in the first pass. - - tagPos = ifdInfo.tagMap.begin(); // Reset both map/array positions. - knownTagPtr = sKnownTags[ifd]; - - for ( ; tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo* currTag = &tagPos->second; - - if ( (currTag->dataLen <= 4) || (currTag->dataPtr != 0) ) continue; // Done this tag? - while ( *knownTagPtr < currTag->id ) ++knownTagPtr; - if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. - + fileRef->Seek ( currTag->origDataOffset, kXMP_SeekFromStart ); currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen ); if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory ); - - if ( currTag->dataLen > kIOBufferSize ) { - // This value is bigger than the I/O buffer, read it directly and restore the file position. - fileRef->Seek ( currTag->origDataOffset, kXMP_SeekFromStart ); - fileRef->ReadAll ( currTag->dataPtr, currTag->dataLen ); - fileRef->Seek ( (ioBuf->filePos + ioBuf->len), kXMP_SeekFromStart ); - } else { - // This value can fit in the I/O buffer, so use that. - MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf ); - ok = CheckFileSpace ( fileRef, ioBuf, currTag->dataLen ); - if ( ! ok ) XMP_Throw ( "EOF in data block", kXMPErr_BadTIFF ); - memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above. - } + fileRef->ReadAll ( currTag->dataPtr, currTag->dataLen ); } @@ -1262,6 +1265,9 @@ XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength() #define Trace_DetermineAppendInfo 0 #endif +// An IFD grows if it has more tags than before. +#define DoesIFDGrow(ifd) (this->containedIFDs[ifd].origCount < this->containedIFDs[ifd].tagMap.size()) + XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin, bool appendedIFDs[kTIFF_KnownIFDCount], XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount], @@ -1306,40 +1312,38 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin, for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = (this->containedIFDs[i].tagMap.size() > 0); } - appendedIFDs[kTIFF_InteropIFD] |= (this->containedIFDs[kTIFF_InteropIFD].origCount < - this->containedIFDs[kTIFF_InteropIFD].tagMap.size()); + appendedIFDs[kTIFF_InteropIFD] |= DoesIFDGrow ( kTIFF_InteropIFD ); if ( appendedIFDs[kTIFF_InteropIFD] ) { this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD ); } - appendedIFDs[kTIFF_GPSInfoIFD] |= (this->containedIFDs[kTIFF_GPSInfoIFD].origCount < - this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size()); + appendedIFDs[kTIFF_GPSInfoIFD] |= DoesIFDGrow ( kTIFF_GPSInfoIFD ); if ( appendedIFDs[kTIFF_GPSInfoIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD ); } - appendedIFDs[kTIFF_ExifIFD] |= (this->containedIFDs[kTIFF_ExifIFD].origCount < - this->containedIFDs[kTIFF_ExifIFD].tagMap.size()); + appendedIFDs[kTIFF_ExifIFD] |= DoesIFDGrow ( kTIFF_ExifIFD ); if ( appendedIFDs[kTIFF_ExifIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD ); } - appendedIFDs[kTIFF_PrimaryIFD] |= (this->containedIFDs[kTIFF_PrimaryIFD].origCount < - this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size()); + appendedIFDs[kTIFF_PrimaryIFD] |= DoesIFDGrow ( kTIFF_PrimaryIFD ); // The appended data (if any) will be a sequence of an IFD followed by its large values. // Determine the new offsets for the appended IFDs and tag values, and the total amount of - // appended stuff. + // appended stuff. The final IFD offset is set in newIFDOffsets for all IFDs, changed or not. + // This makes it easier to set the offsets to the primary and thumbnail IFDs when writing. for ( int ifd = 0; ifd < kTIFF_KnownIFDCount ;++ifd ) { InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); size_t tagCount = ifdInfo.tagMap.size(); + newIFDOffsets[ifd] = ifdInfo.origIFDOffset; // Make the new offset valid for unchanged IFDs. + if ( ! (appendAll | ifdInfo.changed) ) continue; if ( tagCount == 0 ) continue; - newIFDOffsets[ifd] = ifdInfo.origIFDOffset; if ( appendedIFDs[ifd] ) { newIFDOffsets[ifd] = appendedOrigin + appendedLength; appendedLength += (XMP_Uns32)( 6 + (12 * tagCount) ); @@ -1488,7 +1492,7 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n this->PutUns32 ( currTag.count, ifdPtr ); ifdPtr += 4; - *((XMP_Uns32*)ifdPtr) = currTag.smallValue; + PutUns32AsIs ( currTag.smallValue, ifdPtr ); if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) { @@ -1525,6 +1529,14 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], (newStream + 4) ); } + if ( appendedIFDs[kTIFF_TNailIFD] ) { + size_t primaryTagCount = this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size(); + if ( primaryTagCount > 0 ) { + XMP_Uns32 tnailLinkOffset = newIFDOffsets[kTIFF_PrimaryIFD] + 2 + (12 * primaryTagCount); + this->PutUns32 ( newIFDOffsets[kTIFF_TNailIFD], (newStream + tnailLinkOffset) ); + } + } + } catch ( ... ) { free ( newStream ); @@ -1554,7 +1566,11 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n // We don't do most of the actual rewrite here. We set things up so that UpdateMemByAppend can be // called to append onto a bare TIFF header. Additional hidden offsets are then handled here. // -// These tags are recognized as being hidden offsets when composing a condensed stream: +// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965) +// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is +// called from here. +// +// These other tags are recognized as being hidden offsets when composing a condensed stream: // 273 - StripOffsets, lengths in tag 279 // 288 - FreeOffsets, lengths in tag 289 // 324 - TileOffsets, lengths in tag 325 @@ -1563,11 +1579,11 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n // 519 - JPEGQTables, each table is 64 bytes // 520 - JPEGDCTables, lengths ??? // 521 - JPEGACTables, lengths ??? -// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail. // -// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965) -// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is -// called from here. +// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail. +// At this time only the JPEG thumbnail tags, 513 and 514, contain hidden data that is kept. The +// final stream layout is whatever UpdateMemByAppend does for the visible content, followed by the +// hidden offset data. The Exif, GPS, and Interoperability IFDs are visible to UpdateMemByAppend. // ! So far, a memory-based TIFF rewrite would only be done for the Exif portion of a JPEG file. // ! In which case we're probably OK to handle JPEGInterchangeFormat (used for compressed thumbnails) @@ -1653,7 +1669,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* } // Determine the offsets and additional size for the hidden offset content. Set the offset tags - // to the new offset. + // to the new offset so that UpdateMemByAppend writes the new offsets. XMP_Uns32 hiddenContentLength = 0; XMP_Uns32 hiddenContentOrigin = this->DetermineVisibleLength(); @@ -1678,6 +1694,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* // Save any old memory stream for the content behind hidden offsets. Setup a bare TIFF header. XMP_Uns8* oldStream = this->memStream; + bool ownedOldStream = this->ownedStream; XMP_Uns8 bareTIFF [8]; if ( this->bigEndian ) { @@ -1709,6 +1726,10 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* memcpy ( destPtr, srcPtr, hiddenLocations[i].length ); // AUDIT: Safe copy, not user data, computed length. } + + // Delete the old stream if appropriate. + + if ( ownedOldStream ) delete ( oldStream ); } // TIFF_FileWriter::UpdateMemByRewrite @@ -1730,6 +1751,7 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr { if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure ); + this->changed |= condenseStream; // Make sure a compaction request takes effect. if ( ! this->changed ) { if ( dataPtr != 0 ) *dataPtr = this->memStream; return this->tiffLength; @@ -1758,8 +1780,6 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr condenseStream = true; // Makes "conjured" TIFF take the full rewrite path. } - if ( condenseStream ) this->changed = true; // A prior regular call would have cleared this->changed. - if ( condenseStream ) { this->UpdateMemByRewrite ( &newStream, &newLength ); } else { @@ -1805,7 +1825,7 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr #define Trace_UpdateFileStream 0 #endif -void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef ) +void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) { if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure ); if ( ! this->changed ) return; @@ -1832,6 +1852,33 @@ void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef ) XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets ); if ( appendedLength > (0xFFFFFFFFUL - appendedOrigin) ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF ); + if ( progressTracker != 0 ) { + + float filesize=0; + + for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { + + InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; + if ( ! thisIFD.changed ) continue; + + filesize += (thisIFD.tagMap.size() * sizeof(RawIFDEntry) + 6); + + InternalTagMap::iterator tagPos; + InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); + + for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { + InternalTagInfo & thisTag = tagPos->second; + if ( (! thisTag.changed) || (thisTag.dataLen <= 4) ) continue; + filesize += (thisTag.dataLen) ; + } + } + + if ( appendedIFDs[kTIFF_PrimaryIFD] ) filesize += 4; + XMP_Assert ( progressTracker->WorkInProgress() ); + progressTracker->AddTotalWork ( filesize ); + + } + // Do the in-place update for the IFDs and tag values that fit. This part does separate seeks // and writes for the IFDs and values. Things to be updated can be anywhere in the file. |