📄 tiff_filewriter.cpp
字号:
// Allocate the new block of memory for the full stream. Copy the original stream. Write the // modified IFDs and values. Finally rebuild the internal IFD info and tag map. XMP_Uns32 newLength = appendedOrigin + appendedLength; XMP_Uns8* newStream = (XMP_Uns8*) malloc ( newLength + extraSpace ); if ( newStream == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( newStream, this->memStream, this->tiffLength ); // AUDIT: Safe, malloc'ed newLength bytes above. if ( this->tiffLength < appendedOrigin ) { XMP_Assert ( appendedOrigin == (this->tiffLength + 1) ); newStream[this->tiffLength] = 0; // Clear the pad byte. } try { // We might get exceptions from the next part and must delete newStream on the way out. // Write the modified IFDs and values. Rewrite the full IFD from scratch to make sure the // tags are now unique and sorted. Copy large changed values to their appropriate location. XMP_Uns32 appendedOffset = appendedOrigin; for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); size_t tagCount = ifdInfo.tagMap.size(); if ( ! (appendAll | ifdInfo.changed) ) continue; if ( tagCount == 0 ) continue; XMP_Uns8* ifdPtr = newStream + newIFDOffsets[ifd]; if ( appendedIFDs[ifd] ) { XMP_Assert ( newIFDOffsets[ifd] == appendedOffset ); appendedOffset += 6 + (12 * tagCount); } this->PutUns16 ( (XMP_Uns16)tagCount, ifdPtr ); ifdPtr += 2; InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); for ( ; tagPos != tagEnd; ++tagPos ) { InternalTagInfo & currTag ( tagPos->second ); this->PutUns16 ( currTag.id, ifdPtr ); ifdPtr += 2; this->PutUns16 ( currTag.type, ifdPtr ); ifdPtr += 2; this->PutUns32 ( currTag.count, ifdPtr ); ifdPtr += 4; *((XMP_Uns32*)ifdPtr) = currTag.dataOrOffset; if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) { XMP_Uns32 valueOffset = this->GetUns32 ( &currTag.dataOrOffset ); if ( (currTag.dataLen <= currTag.origLen) && (! appendAll) ) { XMP_Assert ( valueOffset == currTag.origOffset ); } else { XMP_Assert ( valueOffset == appendedOffset ); appendedOffset += ((currTag.dataLen + 1) & 0xFFFFFFFEUL); } if ( currTag.dataLen > (newLength - valueOffset) ) XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); memcpy ( (newStream + valueOffset), currTag.dataPtr, currTag.dataLen ); // AUDIT: Protected by the above check. if ( (currTag.dataLen & 1) != 0 ) newStream[valueOffset+currTag.dataLen] = 0; } ifdPtr += 4; } this->PutUns32 ( ifdInfo.origNextIFD, ifdPtr ); ifdPtr += 4; } XMP_Assert ( appendedOffset == newLength ); // Back fill the offsets for the primary and thumnbail IFDs, if they are now appended. if ( appendedIFDs[kTIFF_PrimaryIFD] ) { this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], (newStream + 4) ); } if ( appendedIFDs[kTIFF_TNailIFD] ) { size_t primaryIFDCount = this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size(); XMP_Uns32 tnailRefOffset = newIFDOffsets[kTIFF_PrimaryIFD] + 2 + (12 * primaryIFDCount); this->PutUns32 ( newIFDOffsets[kTIFF_TNailIFD], (newStream + tnailRefOffset) ); } } catch ( ... ) { free ( newStream ); throw; } *newStream_out = newStream; *newLength_out = newLength; } // TIFF_FileWriter::UpdateMemByAppend// =================================================================================================// TIFF_FileWriter::DetermineVisibleLength// =======================================XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength(){ XMP_Uns32 visibleLength = 8; // Start with the TIFF header size. for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); size_t tagCount = ifdInfo.tagMap.size(); if ( tagCount == 0 ) continue; visibleLength += 6 + (12 * tagCount); InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); for ( ; tagPos != tagEnd; ++tagPos ) { InternalTagInfo & currTag ( tagPos->second ); if ( currTag.dataLen > 4 ) visibleLength += ((currTag.dataLen + 1) & 0xFFFFFFFE); // ! Round to even lengths. } } return visibleLength; } // TIFF_FileWriter::DetermineVisibleLength// =================================================================================================// TIFF_FileWriter::UpdateMemByRewrite// ===================================//// Normally we update TIFF in a conservative "by-append" manner. Changes are written in-place where// they fit, anything requiring growth is appended to the end and the old space is abandoned. The// end for memory-based TIFF is the end of the data block, the end for file-based TIFF is the end of// the file. This update-by-append model has the advantage of not perturbing any hidden offsets, a// common feature of proprietary MakerNotes.//// The condenseStream parameter can be used to rewrite the full stream instead of appending. This// will discard any MakerNote tag and risks breaking offsets that are hidden. This can be necessary// though to try to make the TIFF fit in a JPEG file.//// 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:// 273 - StripOffsets, lengths in tag 279// 288 - FreeOffsets, lengths in tag 289// 324 - TileOffsets, lengths in tag 325// 330 - SubIFDs, lengths within the IFDs (Plus subIFD values and possible chaining!)// 513 - JPEGInterchangeFormat, length in tag 514// 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.// ! 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)// ! and complain about any of the other hidden offset tags.// tag count type// 273 n short or long// 279 n short or long// 288 n long// 289 n long// 324 n long// 325 n short or long// 330 n long// 513 1 long// 514 1 long// 519 n long// 520 n long// 521 n longstatic XMP_Uns16 kNoGoTags[] = { kTIFF_StripOffsets, // 273 *** Should be handled? kTIFF_StripByteCounts, // 279 *** Should be handled? kTIFF_FreeOffsets, // 288 *** Should be handled? kTIFF_FreeByteCounts, // 289 *** Should be handled? kTIFF_TileOffsets, // 324 *** Should be handled? kTIFF_TileByteCounts, // 325 *** Should be handled? kTIFF_SubIFDs, // 330 *** Should be handled? kTIFF_JPEGQTables, // 519 kTIFF_JPEGDCTables, // 520 kTIFF_JPEGACTables, // 521 0xFFFF // Must be last as a sentinel. };static XMP_Uns16 kBanishedTags[] = { kTIFF_MakerNote, // *** Should someday support MakerNoteSafety. 0xFFFF // Must be last as a sentinel. };struct SimpleHiddenContentInfo { XMP_Uns8 ifd; XMP_Uns16 offsetTag, lengthTag;};struct SimpleHiddenContentLocations { XMP_Uns32 length, oldOffset, newOffset; SimpleHiddenContentLocations() : length(0), oldOffset(0), newOffset(0) {};};enum { kSimpleHiddenContentCount = 1 };static const SimpleHiddenContentInfo kSimpleHiddenContentInfo [kSimpleHiddenContentCount] = { { kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, kTIFF_JPEGInterchangeFormatLength } };// -------------------------------------------------------------------------------------------------void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out ) { const InternalTagInfo* tagInfo; // Check for tags that we don't tolerate because they have data we can't (or refuse to) find. for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { for ( int i = 0; kNoGoTags[i] != 0xFFFF; ++i ) { tagInfo = this->FindTagInIFD ( ifd, kNoGoTags[i] ); if ( tagInfo != 0 ) XMP_Throw ( "Tag not tolerated for TIFF rewrite", kXMPErr_Unimplemented ); } } // Delete unwanted tags. for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { for ( int i = 0; kBanishedTags[i] != 0xFFFF; ++i ) { this->DeleteTag ( ifd, kBanishedTags[i] ); } } // Make sure the "pointer" tags for the Exif, GPS, and Interop IFDs exist. The order is // important, adding the Interop pointer can cause the Exif IFD to exist. if ( ! this->containedIFDs[kTIFF_InteropIFD].tagMap.empty() ) { this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD ); } if ( ! this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.empty() ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD ); } if ( ! this->containedIFDs[kTIFF_ExifIFD].tagMap.empty() ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD ); } // Determine the offsets and additional size for the hidden offset content. Set the offset tags // to the new offset. XMP_Uns32 hiddenContentLength = 0; XMP_Uns32 hiddenContentOrigin = this->DetermineVisibleLength(); SimpleHiddenContentLocations hiddenLocations [kSimpleHiddenContentCount]; for ( int i = 0; i < kSimpleHiddenContentCount; ++i ) { const SimpleHiddenContentInfo & hiddenInfo ( kSimpleHiddenContentInfo[i] ); bool haveLength = this->GetTag_Integer ( hiddenInfo.ifd, hiddenInfo.lengthTag, &hiddenLocations[i].length ); bool haveOffset = this->GetTag_Integer ( hiddenInfo.ifd, hiddenInfo.offsetTag, &hiddenLocations[i].oldOffset ); if ( haveLength != haveOffset ) XMP_Throw ( "Unpaired simple hidden content tag", kXMPErr_BadTIFF ); if ( (! haveLength) || (hiddenLocations[i].length == 0) ) continue; hiddenLocations[i].newOffset = hiddenContentOrigin + hiddenContentLength; this->SetTag_Long ( hiddenInfo.ifd, hiddenInfo.offsetTag, hiddenLocations[i].newOffset ); hiddenContentLength += ((hiddenLocations[i].length + 1) & 0xFFFFFFFE); // ! Round up for even offsets. } // Save any old memory stream for the content behind hidden offsets. Setup a bare TIFF header. XMP_Uns8* oldStream = this->memStream; XMP_Uns8 bareTIFF [8]; if ( this->bigEndian ) { bareTIFF[0] = 0x4D; bareTIFF[1] = 0x4D; bareTIFF[2] = 0x00; bareTIFF[3] = 0x2A; } else { bareTIFF[0] = 0x49; bareTIFF[1] = 0x49; bareTIFF[2] = 0x2A; bareTIFF[3] = 0x00; } *((XMP_Uns32*)&bareTIFF[4]) = 0; this->memStream = &bareTIFF[0]; this->tiffLength = sizeof ( bareTIFF ); this->ownedStream = false; // Call UpdateMemByAppend to write the new stream, telling it to append everything. this->UpdateMemByAppend ( newStream_out, newLength_out, true, hiddenContentLength ); // Copy the hidden content and update the output stream length; XMP_Assert ( *newLength_out == hiddenContentOrigin ); *newLength_out += hiddenContentLength; for ( int i = 0; i < kSimpleHiddenContentCount; ++i ) { if ( hiddenLocations[i].length == 0 ) continue; XMP_Uns8* srcPtr = oldStream + hiddenLocations[i].oldOffset; XMP_Uns8* destPtr = *newStream_out + hiddenLocations[i].newOffset; memcpy ( destPtr, srcPtr, hiddenLocations[i].length ); // AUDIT: Safe copy, not user data, computed length. } } // TIFF_FileWriter::UpdateMemByRewrite// =================================================================================================// TIFF_FileWriter::UpdateMemoryStream// ===================================//// Normally we update TIFF in a conservative "by-append" manner. Changes are written in-place where// they fit, anything requiring growth is appended to the end and the old space is abandoned. The// end for memory-based TIFF is the end of the data block, the end for file-based TIFF is
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -