📄 jpeg_handler.cpp
字号:
this->exifContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen ); ioBuf.ptr += segLen; continue; // Move on to the next marker. } // Check for the main XMP APP1 marker segment. ok = CheckFileSpace ( fileRef, &ioBuf, kMainXMPSignatureLength ); if ( ok && (segLen >= kMainXMPSignatureLength) && CheckBytes ( ioBuf.ptr, kMainXMPSignatureString, kMainXMPSignatureLength ) ) { // This is the main XMP, cache the contents. ioBuf.ptr += kMainXMPSignatureLength; // Move ioBuf.ptr to the XMP Packet. segLen -= kMainXMPSignatureLength; // Adjust segLen to count just the XMP Packet. ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. if ( ! ok ) return; // Must be a truncated file. this->packetInfo.offset = ioBuf.filePos + (ioBuf.ptr - &ioBuf.data[0]); this->packetInfo.length = segLen; this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP. this->packetInfo.charForm = kXMP_CharUnknown; this->packetInfo.writeable = true; this->xmpPacket.assign ( (XMP_StringPtr)ioBuf.ptr, segLen ); ioBuf.ptr += segLen; // ! Set this->packetInfo.offset first! this->containsXMP = true; // Found the standard XMP packet. continue; // Move on to the next marker. } // Check for an extension XMP APP1 marker segment. ok = CheckFileSpace ( fileRef, &ioBuf, kExtXMPPrefixLength ); // ! The signature, GUID, length, and offset. if ( ok && (segLen >= kExtXMPPrefixLength) && CheckBytes ( ioBuf.ptr, kExtXMPSignatureString, kExtXMPSignatureLength ) ) { // This is a portion of the extended XMP, cache the contents. This is complicated by // the need to tolerate files where the extension portions are not in order. The // local ExtendedXMPInfo map uses the GUID as the key and maps that to a struct that // has the full length and a map of the known portions. This known portion map uses // the offset of the portion as the key and maps that to a string. Only fully seen // extended XMP streams are kept, the right one gets picked in ProcessXMP. segLen -= kExtXMPPrefixLength; // Adjust segLen to count just the XMP stream portion. ioBuf.ptr += kExtXMPSignatureLength; // Move ioBuf.ptr to the GUID. GUID_32 guid; XMP_Assert ( sizeof(guid.data) == 32 ); memcpy ( &guid.data[0], ioBuf.ptr, sizeof(guid.data) ); // AUDIT: Use of sizeof(guid.data) is safe. ioBuf.ptr += 32; // Move ioBuf.ptr to the length and offset. XMP_Uns32 fullLen = GetUns32BE ( ioBuf.ptr ); XMP_Uns32 offset = GetUns32BE ( ioBuf.ptr+4 ); ioBuf.ptr += 8; // Move ioBuf.ptr to the XMP stream portion. #if Trace_UnlimitedJPEG printf ( "New extended XMP portion: fullLen %d, offset %d, GUID %.32s\n", fullLen, offset, guid.data ); #endif // Find the ExtXMPContent for this GUID, and the string for this portion's offset. ExtendedXMPInfo::iterator guidPos = extXMP.find ( guid ); if ( guidPos == extXMP.end() ) { ExtXMPContent newExtContent ( fullLen ); guidPos = extXMP.insert ( extXMP.begin(), ExtendedXMPInfo::value_type ( guid, newExtContent ) ); } ExtXMPPortions::iterator offsetPos; ExtXMPContent & extContent = guidPos->second; if ( extContent.portions.empty() ) { // When new create a full size offset 0 string, to which all in-order portions will get appended. offsetPos = extContent.portions.insert ( extContent.portions.begin(), ExtXMPPortions::value_type ( 0, std::string() ) ); offsetPos->second.reserve ( extContent.length ); } // Try to append this portion to a logically contiguous preceeding one. if ( offset == 0 ) { offsetPos = extContent.portions.begin(); XMP_Assert ( (offsetPos->first == 0) && (offsetPos->second.size() == 0) ); } else { offsetPos = extContent.portions.lower_bound ( offset ); --offsetPos; // Back up to the portion whose offset is less than the new offset. if ( (offsetPos->first + offsetPos->second.size()) != offset ) { // Can't append, create a new portion. offsetPos = extContent.portions.insert ( extContent.portions.begin(), ExtXMPPortions::value_type ( offset, std::string() ) ); } } // Cache this portion of the extended XMP. std::string & extPortion = offsetPos->second; ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion. if ( ! ok ) return; // Must be a truncated file. extPortion.append ( (XMP_StringPtr)ioBuf.ptr, segLen ); ioBuf.ptr += segLen; continue; // Move on to the next marker. } // If we get here this is some other uninteresting APP1 marker segment, skip it. if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) { ioBuf.ptr += segLen; // The next marker is in this buffer. } else { // The next marker is beyond this buffer, RefillBuffer assumes we're doing sequential reads. size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer. ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR ); ioBuf.ptr = ioBuf.limit; // No data left in the buffer. } } else if ( TableOrDataMarker ( marker ) ) { // This is a non-terminating but uninteresting marker segment. Skip it. ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field. if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return; segLen = GetUns16BE ( ioBuf.ptr ); // Remember that the length includes itself. if ( segLen < 2 ) return; // Invalid JPEG. if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) { ioBuf.ptr += segLen; // The next marker is in this buffer. } else { // The next marker is beyond this buffer, RefillBuffer assumes we're doing sequential reads. size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer. ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR ); ioBuf.ptr = ioBuf.limit; // No data left in the buffer. } continue; // Move on to the next marker. } else { break; // This is a terminating marker of some sort. } } if ( ! extXMP.empty() ) { // We have extended XMP. Find out which ones are complete, collapse them into a single // string, and save them for ProcessXMP. ExtendedXMPInfo::iterator guidPos = extXMP.begin(); ExtendedXMPInfo::iterator guidEnd = extXMP.end(); for ( ; guidPos != guidEnd; ++guidPos ) { ExtXMPContent & thisContent = guidPos->second; ExtXMPPortions::iterator partZero = thisContent.portions.begin(); ExtXMPPortions::iterator partEnd = thisContent.portions.end(); ExtXMPPortions::iterator partPos = partZero; #if Trace_UnlimitedJPEG printf ( "Extended XMP portions for GUID %.32s, full length %d\n", guidPos->first.data, guidPos->second.length ); printf ( " Offset %d, length %d, next offset %d\n", partZero->first, partZero->second.size(), (partZero->first + partZero->second.size()) ); #endif for ( ++partPos; partPos != partEnd; ++partPos ) { #if Trace_UnlimitedJPEG printf ( " Offset %d, length %d, next offset %d\n", partPos->first, partPos->second.size(), (partPos->first + partPos->second.size()) ); #endif if ( partPos->first != partZero->second.size() ) break; // Quit if not contiguous. partZero->second.append ( partPos->second ); } if ( (partPos == partEnd) && (partZero->first == 0) && (partZero->second.size() == thisContent.length) ) { // This is a complete extended XMP stream. this->extendedXMP.insert ( ExtendedXMPMap::value_type ( guidPos->first, partZero->second ) ); #if Trace_UnlimitedJPEG printf ( "Full extended XMP for GUID %.32s, full length %d\n", guidPos->first.data, partZero->second.size() ); #endif } } } } // JPEG_MetaHandler::CacheFileData// =================================================================================================// JPEG_MetaHandler::ProcessTNail// ==============================void JPEG_MetaHandler::ProcessTNail(){ XMP_Assert ( ! this->processedTNail ); this->processedTNail = true; // Make sure we only come through here once. this->containsTNail = false; // Set it to true after all of the info is gathered. if ( this->exifMgr == 0 ) { // Thumbnails only need the Exif, not the PSIR or IPTC. bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); if ( readOnly ) { // *** Could reduce heap usage by not copying in TIFF_MemoryReader. this->exifMgr = new TIFF_MemoryReader(); } else { this->exifMgr = new TIFF_FileWriter(); } this->exifMgr->ParseMemoryStream ( this->exifContents.c_str(), this->exifContents.size() ); } this->containsTNail = this->exifMgr->GetTNailInfo ( &this->tnailInfo ); if ( this->containsTNail ) this->tnailInfo.fileFormat = this->parent->format;} // JPEG_MetaHandler::ProcessTNail// =================================================================================================// JPEG_MetaHandler::ProcessXMP// ============================//// Process the raw XMP and legacy metadata that was previously cached.void JPEG_MetaHandler::ProcessXMP(){ XMP_Assert ( ! this->processedXMP ); this->processedXMP = true; // Make sure we only come through here once. // Create the PSIR and IPTC handlers, even if there is no legacy. They might be needed for updates. XMP_Assert ( (this->psirMgr == 0) && (this->iptcMgr == 0) ); // ProcessTNail might create the exifMgr. bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); if ( readOnly ) { if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_MemoryReader(); this->psirMgr = new PSIR_MemoryReader(); this->iptcMgr = new IPTC_Reader(); // ! Parse it later. } else { if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_FileWriter(); this->psirMgr = new PSIR_FileWriter(); this->iptcMgr = new IPTC_Writer(); // ! Parse it later. } // Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy // import if the XMP packet gets parsing errors. bool found; bool haveExif = (! this->exifContents.empty()); bool haveIPTC = false; RecJTP_LegacyPriority lastLegacy = kLegacyJTP_None; TIFF_Manager & exif = *this->exifMgr; // Give the compiler help in recognizing non-aliases. PSIR_Manager & psir = *this->psirMgr; IPTC_Manager & iptc = *this->iptcMgr; if ( haveExif ) { exif.ParseMemoryStream ( this->exifContents.c_str(), this->exifContents.size() ); } if ( ! this->psirContents.empty() ) { psir.ParseMemoryResources ( this->psirContents.c_str(), this->psirContents.size() ); } // Determine the last-legacy priority and do the reconciliation. For JPEG files, the relevant // legacy priorities (ignoring Mac pnot and ANPA resources) are: // kLegacyJTP_PSIR_OldCaption - highest // kLegacyJTP_PSIR_IPTC // kLegacyJTP_JPEG_TIFF_Tags // kLegacyJTP_None - lowest found = psir.GetImgRsrc ( kPSIR_OldCaption, 0 ); if ( ! found ) found = psir.GetImgRsrc ( kPSIR_OldCaptionPStr, 0 ); if ( found ) { haveIPTC = true; lastLegacy = kLegacyJTP_PSIR_OldCaption; } PSIR_Manager::ImgRsrcInfo iptcInfo; found = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo ); if ( found ) { haveIPTC = true; iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); if ( lastLegacy < kLegacyJTP_PSIR_IPTC ) lastLegacy = kLegacyJTP_PSIR_IPTC; } if ( lastLegacy < kLegacyJTP_JPEG_TIFF_Tags ) { found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_ImageDescription, 0 ); if ( ! found ) found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_Artist, 0 ); if ( ! found ) found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_Copyright, 0 ); if ( found ) lastLegacy = kLegacyJTP_JPEG_TIFF_Tags; } XMP_OptionBits options = 0; if ( this->containsXMP ) options |= k2XMP_FileHadXMP; if ( haveExif ) options |= k2XMP_FileHadExif; if ( haveIPTC ) options |= k2XMP_FileHadIPTC; // Process the main XMP packet. If it fails to parse, do a forced legacy import but still throw // an exception. This tells the caller that an error happened, but gives them recovered legacy // should they want to proceed with that. if ( ! this->xmpPacket.empty() ) { XMP_Assert ( this->containsXMP ); // Common code takes care of packetInfo.charForm, .padSize, and .writeable. XMP_StringPtr packetStr = this->xmpPacket.c_str(); XMP_StringLen packetLen = this->xmpPacket.size(); try { this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); } catch ( ... ) { XMP_ClearOption ( options, k2XMP_FileHadXMP ); ImportJTPtoXMP ( kXMP_JPEGFile, lastLegacy, &exif, psir, &iptc, &this->xmpObj, options ); throw; // ! Rethrow the exception, don't absorb it. } } // Process the extended XMP if it has a matching GUID. if ( ! this->extendedXMP.empty() ) { bool found; GUID_32 g32; std::string extGUID, extPacket; ExtendedXMPMap::iterator guidPos = this->extendedXMP.end(); found = this->xmpObj.GetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", &extGUID, 0 ); if ( found && (extGUID.size() == sizeof(g32.data)) ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -