📄 tiff_filewriter.cpp
字号:
static XMP_Uns8* CaptureJPEGTNail ( LFA_FileRef fileRef, IOBuffer* ioBuf, const TIFF_Manager& tiff ){ bool ok; XMP_Uns8* jpegPtr = 0; XMP_Uns32 jpegOffset, jpegLen; ok = tiff.GetTag_Integer ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, &jpegOffset ); if ( ok ) ok = tiff.GetTag_Integer ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormatLength, &jpegLen ); if ( ! ok ) return 0; if ( jpegLen > 1024*1024 ) return 0; // ? XMP_Throw ( "Outrageous JPEG TNail length", kXMPErr_BadTIFF ); jpegPtr = (XMP_Uns8*) malloc ( jpegLen ); if ( jpegPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); try { if ( jpegLen > kIOBufferSize ) { // This value is bigger than the I/O buffer, read it directly and restore the file position. LFA_Seek ( fileRef, jpegOffset, SEEK_SET ); LFA_Read ( fileRef, jpegPtr, jpegLen, kLFA_RequireAll ); LFA_Seek ( fileRef, (ioBuf->filePos + ioBuf->len), SEEK_SET ); } else { // This value can fit in the I/O buffer, so use that. MoveToOffset ( fileRef, jpegOffset, ioBuf ); ok = CheckFileSpace ( fileRef, ioBuf, jpegLen ); if ( ! ok ) XMP_Throw ( "EOF in data block", kXMPErr_BadTIFF ); memcpy ( jpegPtr, ioBuf->ptr, jpegLen ); // AUDIT: Safe, malloc'ed jpegLen bytes above. } } catch ( ... ) { free ( jpegPtr ); throw; } return jpegPtr;} // CaptureJPEGTNail// =================================================================================================// TIFF_FileWriter::ParseFileStream// ================================//// The buffered I/O model is worth the logic complexity - as opposed to a simple seek/read for each// part of the TIFF stream. The vast majority of real-world TIFFs have the primary IFD, Exif IFD,// and all of their interesting tag values within the first 64K of the file. Well, at least before// we get around to our edit-by-append approach.void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef ) { bool ok; IOBuffer ioBuf; this->DeleteExistingInfo(); this->fileParsed = true; this->tiffLength = (XMP_Uns32) LFA_Measure ( fileRef ); if ( this->tiffLength == 0 ) return; // Find and process the primary, Exif, GPS, and Interoperability IFDs. ioBuf.filePos = LFA_Seek ( fileRef, 0, SEEK_SET ); ok = CheckFileSpace ( fileRef, &ioBuf, 8 ); if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF ); XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength ); XMP_Uns32 tnailIFDOffset = 0; if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf ); 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->dataOrOffset ); (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef, &ioBuf ); } 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->dataOrOffset ); (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef, &ioBuf ); } 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->dataOrOffset ); (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef, &ioBuf ); } // Process the thumbnail IFD. We only do this for Exif-compliant TIFF streams. Do this after // the others since they are often within the first 64K of the file and the thumbnail is not. if ( (tnailIFDOffset != 0) && (! this->containedIFDs[kTIFF_ExifIFD].tagMap.empty()) ) { (void) this->ProcessFileIFD ( kTIFF_TNailIFD, tnailIFDOffset, fileRef, &ioBuf ); this->jpegTNailPtr = CaptureJPEGTNail ( fileRef, &ioBuf, *this ); } #if 0 { printf ( "\nExiting TIFF_FileWriter::ParseFileStream\n" ); for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; printf ( "\n IFD %d, count %d, mapped %d, offset %d (0x%X), next IFD %d (0x%X)\n", ifd, thisIFD.origCount, thisIFD.tagMap.size(), thisIFD.origOffset, thisIFD.origOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); InternalTagMap::iterator tagPos; InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { InternalTagInfo & thisTag = tagPos->second; printf ( " Tag %d, dataOrOffset 0x%X, origLen %d, origOffset %d (0x%X)\n", thisTag.id, thisTag.dataOrOffset, thisTag.origLen, thisTag.origOffset, thisTag.origOffset ); } } printf ( "\n" ); } #endif} // TIFF_FileWriter::ParseFileStream// =================================================================================================// TIFF_FileWriter::ProcessFileIFD// ===============================XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, LFA_FileRef fileRef, IOBuffer* ioBuf ) { InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); MoveToOffset ( fileRef, ifdOffset, ioBuf ); // Move to the start of the IFD. bool ok = CheckFileSpace ( fileRef, ioBuf, 2 ); if ( ! ok ) XMP_Throw ( "IFD count missing", kXMPErr_BadTIFF ); XMP_Uns16 tagCount = this->GetUns16 ( ioBuf->ptr ); if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); if ( (ifdOffset + 2 + tagCount*12 + 4) > this->tiffLength ) XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); ifdInfo.origOffset = ifdOffset; ifdInfo.origCount = tagCount; // --------------------------------------------------------------------------------------------- // First create all of the IFD map entries, capturing short values, and get the next IFD offset. // We're using a std::map for storage, it automatically eliminates duplicates and provides // 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. for ( XMP_Uns16 i = 0; i < tagCount; ++i, ioBuf->ptr += 12 ) { if ( ! CheckFileSpace ( fileRef, ioBuf, 12 ) ) XMP_Throw ( "EOF within IFD", kXMPErr_BadTIFF ); RawIFDEntry* rawTag = (RawIFDEntry*)ioBuf->ptr; InternalTagInfo mapTag ( this->GetUns16(&rawTag->id), this->GetUns16(&rawTag->type), this->GetUns32(&rawTag->count) ); if ( (mapTag.type < kTIFF_ByteType) || (mapTag.type > kTIFF_LastType) ) continue; // Bad type, skip this tag. mapTag.dataLen = mapTag.origLen = mapTag.count * kTIFF_TypeSizes[mapTag.type]; mapTag.dataOrOffset = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. if ( mapTag.dataLen <= 4 ) { mapTag.dataPtr = (XMP_Uns8*) &mapTag.dataOrOffset; mapTag.origOffset = ifdOffset + 2 + (12 * i); // Compute the data offset. } else { mapTag.origOffset = this->GetUns32 ( &rawTag->dataOrOffset ); // Extract the data offset. } ifdInfo.tagMap[mapTag.id] = mapTag; } 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 ); 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 + ioBuf->len; for ( ; tagPos != tagEnd; ++tagPos ) { InternalTagInfo* currTag = &tagPos->second; if ( currTag->dataLen <= 4 ) continue; // Short values are already in the dataOrOffset field. while ( *knownTagPtr < currTag->id ) ++knownTagPtr; if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. if ( currTag->dataLen > 1024*1024 ) XMP_Throw ( "Outrageous data length", kXMPErr_BadTIFF ); if ( (bufBegin <= currTag->origOffset) && ((currTag->origOffset + currTag->dataLen) <= bufEnd) ) { // This value is already fully within the current I/O buffer, copy it. MoveToOffset ( fileRef, currTag->origOffset, 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. if ( currTag->dataLen > 1024*1024 ) XMP_Throw ( "Outrageous data length", kXMPErr_BadTIFF ); 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. LFA_Seek ( fileRef, currTag->origOffset, SEEK_SET ); LFA_Read ( fileRef, currTag->dataPtr, currTag->dataLen, kLFA_RequireAll ); LFA_Seek ( fileRef, (ioBuf->filePos + ioBuf->len), SEEK_SET ); } else { // This value can fit in the I/O buffer, so use that. MoveToOffset ( fileRef, currTag->origOffset, 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. } } // Done, return the next IFD offset. return ifdInfo.origNextIFD;} // TIFF_FileWriter::ProcessFileIFD// =================================================================================================// TIFF_FileWriter::IntegrateFromPShop6// ====================================//// See comments for ProcessPShop6IFD.void TIFF_FileWriter::IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) { TIFF_MemoryReader buriedExif; buriedExif.ParseMemoryStream ( buriedPtr, buriedLen ); this->ProcessPShop6IFD ( buriedExif, kTIFF_PrimaryIFD ); this->ProcessPShop6IFD ( buriedExif, kTIFF_TNailIFD ); this->ProcessPShop6IFD ( buriedExif, kTIFF_ExifIFD ); this->ProcessPShop6IFD ( buriedExif, kTIFF_GPSInfoIFD );} // TIFF_FileWriter::IntegrateFromPShop6// =================================================================================================// TIFF_FileWriter::CopyTagToMasterIFD// ===================================//// Create a new master IFD entry from a buried Photoshop 6 IFD entry. Don't try to get clever with// large values, just create a new copy. This preserves a clean separation between the memory-based// and file-based TIFF processing.void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDInfo * masterIFD ){ InternalTagInfo newTag ( ps6Tag.id, ps6Tag.type, ps6Tag.count ); newTag.dataLen = ps6Tag.dataLen; if ( newTag.dataLen <= 4 ) { newTag.dataPtr = (XMP_Uns8*) &newTag.dataOrOffset; newTag.dataOrOffset = *((XMP_Uns32*)ps6Tag.dataPtr); } else { XMP_Assert ( newTag.dataOrOffset == 0 ); newTag.dataPtr = (XMP_Uns8*) malloc ( newTag.dataLen ); if ( newTag.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( newTag.dataPtr, ps6Tag.dataPtr, newTag.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. } newTag.changed = true; // ! See comments with ProcessPShop6IFD. XMP_Assert ( (newTag.origLen == 0) && (newTag.origOffset == 0) ); masterIFD->tagMap[newTag.id] = newTag; masterIFD->changed = true; return masterIFD->tagMap[newTag.id].dataPtr; // ! Return the address within the map entry for small values.} // TIFF_FileWriter::CopyTagToMasterIFD// =================================================================================================// FlipCFATable// ============//// The CFA pattern table is trivial, a pair of short counts followed by n*m bytes.static bool FlipCFATable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 ){ if ( tagLen < 4 ) return false; XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; Flip2 ( &u16Ptr[0] ); // Flip the counts to match the master TIFF. Flip2 ( &u16Ptr[1] ); XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine. XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] ); if ( tagLen != (XMP_Uns32)(4 + columns*rows) ) return false; return true;} // FlipCFATable// =================================================================================================// FlipDSDTable// ============//// The device settings description table is trivial, a pair of short counts followed by UTF-16// strings. So the whole value should be flipped as a sequence of 16 bit items.// ! The Exif 2.2 description is a bit garbled. It might be wrong. It would be nice to have a real example.static bool FlipDSDTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 ){ if ( tagLen < 4 ) return false; XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; for ( size_t i = tagLen/2; i > 0; --i, ++u16Ptr ) Flip2 ( u16Ptr );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -