📄 iptc_support.cpp
字号:
// ============================void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32 utf8Len, long which /* = -1 */ ){ const DataSetCharacteristics* knownDS = FindKnownDataSet ( id ); if ( knownDS == 0 ) XMP_Throw ( "Can only set known IPTC DataSets", kXMPErr_InternalFailure ); // Decide which character encoding to use and get a temporary pointer to the value. XMP_Uns8 * tempPtr; XMP_Uns32 dataLen; std::string localStr, rtStr; if ( this->utf8Encoding ) { // We're already using UTF-8. tempPtr = (XMP_Uns8*) utf8Ptr; dataLen = utf8Len; } else {// *** Disable the round trip loss checking for now. We only use UTF-8 if the input had it. ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr );// ReconcileUtils::LocalToUTF8 ( localStr.data(), localStr.size(), &rtStr ); // if ( (rtStr.size() == utf8Len) && (memcmp ( rtStr.data(), utf8Ptr, utf8Len ) == 0) ) { // It round-tripped without loss, keep local encoding. tempPtr = (XMP_Uns8*) localStr.data(); dataLen = localStr.size();// } else {// // Had round-trip loss, change to UTF-8 for all text DataSets.// this->ConvertToUTF8();// XMP_Assert ( this->utf8Encoding );// tempPtr = (XMP_Uns8*) utf8Ptr;// dataLen = utf8Len;// } } // Set the value for this DataSet, making a non-transient copy of the value. Respect UTF-8 character // boundaries when truncating. This is easy to check. If the first truncated byte has 10 in the // high order 2 bits then we are in the middle of a UTF-8 multi-byte character. // Back up to just before a byte with 11 in the high order 2 bits. if ( dataLen > knownDS->maxLen ) { dataLen = knownDS->maxLen; if ( this->utf8Encoding && ((tempPtr[dataLen] >> 6) == 2) ) { for ( ; (dataLen > 0) && ((tempPtr[dataLen] >> 6) != 3); --dataLen ) {} } } DataSetMap::iterator dsPos = this->dataSets.find ( id ); long currCount = (long) this->dataSets.count ( id ); bool repeatable = false; if ( knownDS->mapForm == kIPTC_MapArray ) { repeatable = true; } else if ( id == kIPTC_SubjectCode ) { repeatable = true; } if ( ! repeatable ) { if ( which > 0 ) XMP_Throw ( "Non-repeatable IPTC DataSet", kXMPErr_BadParam ); } else { if ( which < 0 ) which = currCount; // The default is to append. if ( which > currCount ) { XMP_Throw ( "Invalid index for IPTC DataSet", kXMPErr_BadParam ); } else if ( which == currCount ) { dsPos = this->dataSets.end(); // To make later checks do the right thing. } else { dsPos = this->dataSets.lower_bound ( id ); for ( ; which > 0; --which ) ++dsPos; } } if ( dsPos != this->dataSets.end() ) { if ( (dsPos->second.dataLen == dataLen) && (memcmp ( dsPos->second.dataPtr, tempPtr, dataLen ) == 0) ) { return; // ! New value matches the old, don't update. } } XMP_Uns8 * dataPtr = (XMP_Uns8*) malloc ( dataLen ); if ( dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( dataPtr, tempPtr, dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. DataSetInfo dsInfo ( id, dataLen, dataPtr ); if ( dsPos != this->dataSets.end() ) { this->DisposeLooseValue ( dsPos->second ); dsPos->second = dsInfo; } else { DataSetMap::value_type mapValue ( id, dsInfo ); (void) this->dataSets.insert ( this->dataSets.upper_bound ( id ), mapValue ); } this->changed = true;} // IPTC_Writer::SetDataSet_UTF8// =================================================================================================// IPTC_Writer::DeleteDataSet// ==========================void IPTC_Writer::DeleteDataSet ( XMP_Uns8 id, long which /* = -1 */ ){ DataSetMap::iterator dsBegin = this->dataSets.lower_bound ( id ); // Set for which == -1. DataSetMap::iterator dsEnd = this->dataSets.upper_bound ( id ); if ( dsBegin == dsEnd ) return; // Nothing to delete. if ( which >= 0 ) { long currCount = (long) this->dataSets.count ( id ); if ( which >= currCount ) return; // Nothing to delete. for ( ; which > 0; --which ) ++dsBegin; dsEnd = dsBegin; ++dsEnd; // ! Can't do "dsEnd = dsBegin+1"! } for ( DataSetMap::iterator dsPos = dsBegin; dsPos != dsEnd; ++dsPos ) { this->DisposeLooseValue ( dsPos->second ); } this->dataSets.erase ( dsBegin, dsEnd ); this->changed = true;} // IPTC_Writer::DeleteDataSet// =================================================================================================// IPTC_Writer::UpdateMemoryDataSets// =================================//// Reconstruct the entire IIM block. Start with DataSet 1:0 and 1:90 if UTF-8 encoding is used,// then 2:0, then 2:xx DataSets that have values. This does not include any alignment padding, that// is an artifact of some specific wrappers such as Photoshop image resources.XMP_Uns32 IPTC_Writer::UpdateMemoryDataSets ( void** dataPtr ){ if ( ! this->changed ) { if ( dataPtr != 0 ) *dataPtr = this->iptcContent; return this->iptcLength; } DataSetMap::iterator dsPos; DataSetMap::iterator dsEnd = this->dataSets.end(); // if ( this->utf8Encoding ) { *** Disable round trip loss checking for now. ***// if ( ! this->CheckRoundTripLoss() ) this->ConvertToLocal();// } // Compute the length of the new IIM block, including space for records other than 2. All other // records are preserved as-is, including 1:90. If we ever start changing the encoding, we will // have to remove any existing 1:90 and insert a new one. XMP_Uns32 newLength = (5+2); // For 2:0. newLength += (this->iptcLength - rec2Length); // For records other than 2. for ( dsPos = this->dataSets.begin(); dsPos != dsEnd; ++dsPos ) { XMP_Uns32 dsLen = dsPos->second.dataLen; newLength += (5 + dsLen); if ( dsLen > 0x7FFF ) newLength += 4; // We always use a 4 byte extended length. } // Allocate the new IIM block. XMP_Uns8* newContent = (XMP_Uns8*) malloc ( newLength ); if ( newContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); XMP_Uns8* dsPtr = newContent; XMP_Uns32 prefixLength = this->rec2Offset; XMP_Uns32 suffixOffset = this->rec2Offset + this->rec2Length; XMP_Uns32 suffixLength = this->iptcLength - suffixOffset; if ( prefixLength > 0 ) { // Write the records before 2. memcpy ( dsPtr, this->iptcContent, prefixLength ); // AUDIT: Within range of allocation. dsPtr += prefixLength; } if ( ! this->utf8Encoding ) { // Start with 2:00 for version 2. // *** We should probably write version 4 all the time. This is a late CS3 change, don't want // *** to risk breaking other apps that might be strict about version checking. memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x02", (5+2) ); // AUDIT: Within range of allocation. dsPtr += (5+2); } else { // Start with 2:00 for version 4. memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x04", (5+2) ); // AUDIT: Within range of allocation. dsPtr += (5+2); } // Fill in the record 2 DataSets that have values. for ( dsPos = this->dataSets.begin(); dsPos != dsEnd; ++dsPos ) { DataSetInfo & dsInfo = dsPos->second; dsPtr[0] = 0x1C; dsPtr[1] = 2; dsPtr[2] = dsInfo.id; dsPtr += 3; XMP_Uns32 dsLen = dsInfo.dataLen; if ( dsLen <= 0x7FFF ) { PutUns16BE ( (XMP_Uns16)dsLen, dsPtr ); dsPtr += 2; } else { PutUns16BE ( 0x8004, dsPtr ); PutUns32BE ( dsLen, dsPtr+2 ); dsPtr += 6; } if ( dsLen > (newLength - (dsPtr - newContent)) ) { XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); } memcpy ( dsPtr, dsInfo.dataPtr, dsLen ); // AUDIT: Protected by above check. dsPtr += dsLen; } if ( suffixLength > 0 ) { // Write the records after 2. memcpy ( dsPtr, (this->iptcContent + suffixOffset), suffixLength ); // AUDIT: Within range of allocation. dsPtr += suffixLength; } XMP_Assert ( dsPtr == (newContent + newLength) ); // Parse the new block, it is the best way to reset internal info and rebuild the map. this->ParseMemoryDataSets ( newContent, newLength, false ); // Don't make another copy of the content. XMP_Assert ( this->iptcLength == newLength ); this->ownedContent = true; // We really do own the new content. // Done. if ( dataPtr != 0 ) *dataPtr = this->iptcContent; return this->iptcLength; } // IPTC_Writer::UpdateMemoryDataSets#if 0 // *** Disable the round trip loss checking for now.// =================================================================================================// IPTC_Writer::ConvertToUTF8// ==========================//// Convert the values of existing text DataSets to UTF-8. For now we only accept text DataSets.void IPTC_Writer::ConvertToUTF8(){ XMP_Assert ( ! this->utf8Encoding ); std::string utf8Str; DataSetMap::iterator dsPos = this->dataSets.begin(); DataSetMap::iterator dsEnd = this->dataSets.end(); for ( ; dsPos != dsEnd; ++dsPos ) { DataSetInfo & dsInfo = dsPos->second; ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, &utf8Str ); this->DisposeLooseValue ( dsInfo ); dsInfo.dataLen = utf8Str.size(); dsInfo.dataPtr = (XMP_Uns8*) malloc ( dsInfo.dataLen ); if ( dsInfo.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( dsInfo.dataPtr, utf8Str.data(), dsInfo.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. } this->utf8Encoding = true;} // IPTC_Writer::ConvertToUTF8// =================================================================================================// IPTC_Writer::ConvertToLocal// ===========================//// Convert the values of existing text DataSets to local. For now we only accept text DataSets.void IPTC_Writer::ConvertToLocal(){ XMP_Assert ( this->utf8Encoding ); std::string localStr; DataSetMap::iterator dsPos = this->dataSets.begin(); DataSetMap::iterator dsEnd = this->dataSets.end(); for ( ; dsPos != dsEnd; ++dsPos ) { DataSetInfo & dsInfo = dsPos->second; ReconcileUtils::UTF8ToLocal ( dsInfo.dataPtr, dsInfo.dataLen, &localStr ); this->DisposeLooseValue ( dsInfo ); dsInfo.dataLen = localStr.size(); dsInfo.dataPtr = (XMP_Uns8*) malloc ( dsInfo.dataLen ); if ( dsInfo.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( dsInfo.dataPtr, localStr.data(), dsInfo.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. } this->utf8Encoding = false;} // IPTC_Writer::ConvertToLocal// =================================================================================================// IPTC_Writer::CheckRoundTripLoss// ===============================//// See if we still need UTF-8 because of round-trip loss. Returns true if there is loss.bool IPTC_Writer::CheckRoundTripLoss(){ XMP_Assert ( this->utf8Encoding ); std::string localStr, rtStr; DataSetMap::iterator dsPos = this->dataSets.begin(); DataSetMap::iterator dsEnd = this->dataSets.end(); for ( ; dsPos != dsEnd; ++dsPos ) { DataSetInfo & dsInfo = dsPos->second; XMP_StringPtr utf8Ptr = (XMP_StringPtr) dsInfo.dataPtr; XMP_StringLen utf8Len = dsInfo.dataLen; ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr ); ReconcileUtils::LocalToUTF8 ( localStr.data(), localStr.size(), &rtStr ); if ( (rtStr.size() != utf8Len) || (memcmp ( rtStr.data(), utf8Ptr, utf8Len ) != 0) ) { return true; // Had round-trip loss, keep UTF-8. } } return false; // No loss.} // IPTC_Writer::CheckRoundTripLoss#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -