📄 reconcileiptc.cpp
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2006-2007 Adobe Systems Incorporated// All Rights Reserved//// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms// of the Adobe license agreement accompanying it.// =================================================================================================#include "XMP_Environment.h" // ! This must be the first include.#include "Reconcile_Impl.hpp"#if XMP_WinBuild #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) #pragma warning ( disable : 4996 ) // '...' was declared deprecated#endif// =================================================================================================/// \file ReconcileIPTC.cpp/// \brief Utilities to reconcile between XMP and legacy IPTC and PSIR metadata.///// =================================================================================================// =================================================================================================// NormalizeToCR// =============static inline void NormalizeToCR ( std::string * value ){ char * strPtr = (char*) value->data(); char * strEnd = strPtr + value->size(); for ( ; strPtr < strEnd; ++strPtr ) { if ( *strPtr == kLF ) *strPtr = kCR; } } // NormalizeToCR// =================================================================================================// NormalizeToLF// =============static inline void NormalizeToLF ( std::string * value ){ char * strPtr = (char*) value->data(); char * strEnd = strPtr + value->size(); for ( ; strPtr < strEnd; ++strPtr ) { if ( *strPtr == kCR ) *strPtr = kLF; } } // NormalizeToLF// =================================================================================================// ComputeIPTCDigest// =================//// Compute a 128 bit (16 byte) MD5 digest of the full IPTC block.static inline void ComputeIPTCDigest ( IPTC_Manager * iptc, MD5_Digest * digest ){ MD5_CTX context; void * iptcData; XMP_Uns32 iptcLen; iptcLen = iptc->UpdateMemoryDataSets ( &iptcData ); MD5Init ( &context ); MD5Update ( &context, (XMP_Uns8*)iptcData, iptcLen ); MD5Final ( *digest, &context );} // ComputeIPTCDigest;// =================================================================================================// ReconcileUtils::CheckIPTCDigest// ===============================int ReconcileUtils::CheckIPTCDigest ( IPTC_Manager * iptc, const PSIR_Manager & psir ){ MD5_Digest newDigest; PSIR_Manager::ImgRsrcInfo ir1061; ComputeIPTCDigest ( iptc, &newDigest ); bool found = psir.GetImgRsrc ( kPSIR_IPTCDigest, &ir1061 ); if ( ! found ) return kDigestMissing; if ( ir1061.dataLen != 16 ) return kDigestMissing; if ( memcmp ( newDigest, ir1061.dataPtr, 16 ) == 0 ) return kDigestMatches; return kDigestDiffers; } // ReconcileUtils::CheckIPTCDigest// =================================================================================================// ReconcileUtils::SetIPTCDigest// ===============================void ReconcileUtils::SetIPTCDigest ( IPTC_Manager * iptc, PSIR_Manager * psir ){ MD5_Digest newDigest; ComputeIPTCDigest ( iptc, &newDigest ); psir->SetImgRsrc ( kPSIR_IPTCDigest, &newDigest, sizeof(newDigest) ); } // ReconcileUtils::SetIPTCDigest// =================================================================================================// =================================================================================================// =================================================================================================// ImportIPTC_Simple// =================static void ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState, XMP_Uns8 id, const char * xmpNS, const char * xmpProp ){ if ( digestState == kDigestDiffers ) { xmp->DeleteProperty ( xmpNS, xmpProp ); } else { XMP_Assert ( digestState == kDigestMissing ); if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; } std::string utf8Str; size_t count = iptc.GetDataSet_UTF8 ( id, &utf8Str ); if ( count != 0 ) { NormalizeToLF ( &utf8Str ); xmp->SetProperty ( xmpNS, xmpProp, utf8Str.c_str() ); }} // ImportIPTC_Simple// =================================================================================================// ImportIPTC_LangAlt// ==================static void ImportIPTC_LangAlt ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState, XMP_Uns8 id, const char * xmpNS, const char * xmpProp ){ if ( digestState == kDigestDiffers ) { std::string xdItemPath = xmpProp; // Delete just the x-default item, not the whole array. xdItemPath += "[?xml:lang='x-default']"; xmp->DeleteProperty ( xmpNS, xdItemPath.c_str() ); } else { XMP_Assert ( digestState == kDigestMissing ); if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; // Check the entire array here. } std::string utf8Str; size_t count = iptc.GetDataSet_UTF8 ( id, &utf8Str ); if ( count != 0 ) { NormalizeToLF ( &utf8Str ); xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", utf8Str.c_str() ); }} // ImportIPTC_LangAlt// =================================================================================================// ImportIPTC_Array// ================static void ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState, XMP_Uns8 id, const char * xmpNS, const char * xmpProp ){ if ( digestState == kDigestDiffers ) { xmp->DeleteProperty ( xmpNS, xmpProp ); } else { XMP_Assert ( digestState == kDigestMissing ); if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; } std::string utf8Str; size_t count = iptc.GetDataSet ( id, 0 ); for ( size_t ds = 0; ds < count; ++ds ) { (void) iptc.GetDataSet_UTF8 ( id, &utf8Str, ds ); NormalizeToLF ( &utf8Str ); xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsUnordered, utf8Str.c_str() ); }} // ImportIPTC_Array// =================================================================================================// ImportIPTC_IntellectualGenre// ============================//// Import DataSet 2:04. In the IIM this is a 3 digit number, a colon, and an optional text name.// Even though the number is the more formal part, the IPTC4XMP rule is that the name is imported to// XMP and the number is dropped. Also, even though IIMv4.1 says that 2:04 is repeatable, the XMP// property to which it is mapped is simple.static void ImportIPTC_IntellectualGenre ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState, const char * xmpNS, const char * xmpProp ){ if ( digestState == kDigestDiffers ) { xmp->DeleteProperty ( xmpNS, xmpProp ); } else { XMP_Assert ( digestState == kDigestMissing ); if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; } std::string utf8Str; size_t count = iptc.GetDataSet_UTF8 ( kIPTC_IntellectualGenre, &utf8Str ); if ( count == 0 ) return; NormalizeToLF ( &utf8Str ); XMP_StringPtr namePtr = utf8Str.c_str() + 4; if ( utf8Str.size() <= 4 ) { // No name in the IIM. Look up the number in our list of known genres. int i; XMP_StringPtr numPtr = utf8Str.c_str(); for ( i = 0; kIntellectualGenreMappings[i].refNum != 0; ++i ) { if ( strncmp ( numPtr, kIntellectualGenreMappings[i].refNum, 3 ) == 0 ) break; } if ( kIntellectualGenreMappings[i].refNum == 0 ) return; namePtr = kIntellectualGenreMappings[i].name; } xmp->SetProperty ( xmpNS, xmpProp, namePtr );} // ImportIPTC_IntellectualGenre// =================================================================================================// ImportIPTC_SubjectCode// ======================//// Import all 2:12 DataSets into an unordered array. In the IIM each DataSet is composed of 5 colon// separated sections: a provider name, an 8 digit reference number, and 3 optional names for the// levels of the reference number hierarchy. The IPTC4XMP mapping rule is that only the reference// number is imported to XMP.static void ImportIPTC_SubjectCode ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState, const char * xmpNS, const char * xmpProp ){ if ( digestState == kDigestDiffers ) { xmp->DeleteProperty ( xmpNS, xmpProp ); } else { XMP_Assert ( digestState == kDigestMissing ); if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; } std::string utf8Str; size_t count = iptc.GetDataSet_UTF8 ( kIPTC_SubjectCode, 0 ); for ( size_t ds = 0; ds < count; ++ds ) { (void) iptc.GetDataSet_UTF8 ( kIPTC_SubjectCode, &utf8Str, ds ); char * refNumPtr = (char*) utf8Str.c_str(); for ( ; (*refNumPtr != ':') && (*refNumPtr != 0); ++refNumPtr ) {} if ( *refNumPtr == 0 ) continue; // This DataSet is ill-formed. char * refNumEnd = refNumPtr + 1; for ( ; (*refNumEnd != ':') && (*refNumEnd != 0); ++refNumEnd ) {} if ( (refNumEnd - refNumPtr) != 8 ) continue; // This DataSet is ill-formed. *refNumEnd = 0; // Ensure a terminating nul for the reference number portion. xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsUnordered, refNumPtr ); }} // ImportIPTC_SubjectCode// =================================================================================================// ImportIPTC_DateCreated// ======================//// An IPTC (IIM) date is 8 charcters YYYYMMDD. Include the time portion from 2:60 if it is present.// The IPTC time is HHMMSSxHHMM, where 'x' is '+' or '-'. Be tolerant of some ill-formed dates and// times. Apparently some non-Adobe apps put strings like "YYYY-MM-DD" or "HH:MM:SSxHH:MM" in the// IPTC. Allow a missing time zone portion to mean UTC.static void ImportIPTC_DateCreated ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState, const char * xmpNS, const char * xmpProp ){ if ( digestState == kDigestDiffers ) { xmp->DeleteProperty ( xmpNS, xmpProp ); } else { XMP_Assert ( digestState == kDigestMissing ); if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; } // First gather the date portion. IPTC_Manager::DataSetInfo dsInfo; size_t count = iptc.GetDataSet ( kIPTC_DateCreated, &dsInfo ); if ( count == 0 ) return; size_t chPos, digits; XMP_DateTime xmpDate; memset ( &xmpDate, 0, sizeof(xmpDate) ); for ( chPos = 0, digits = 0; digits < 4; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.year = (xmpDate.year * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos; for ( digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.month = (xmpDate.month * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.month < 1 ) xmpDate.month = 1; if ( xmpDate.month > 12 ) xmpDate.month = 12; if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos; for ( digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.day = (xmpDate.day * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.day < 1 ) xmpDate.day = 1; if ( xmpDate.day > 31 ) xmpDate.day = 28; // Close enough. if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed. // Now add the time portion if present. count = iptc.GetDataSet ( kIPTC_TimeCreated, &dsInfo ); if ( count != 0 ) { for ( chPos = 0, digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.hour = (xmpDate.hour * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.hour < 0 ) xmpDate.hour = 0; if ( xmpDate.hour > 23 ) xmpDate.hour = 23; if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; for ( digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.minute = (xmpDate.minute * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.minute < 0 ) xmpDate.minute = 0; if ( xmpDate.minute > 59 ) xmpDate.minute = 59; if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; for ( digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.second = (xmpDate.second * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.second < 0 ) xmpDate.second = 0; if ( xmpDate.second > 59 ) xmpDate.second = 59; if ( dsInfo.dataPtr[chPos] == '+' ) { xmpDate.tzSign = kXMP_TimeEastOfUTC; } else if ( dsInfo.dataPtr[chPos] == '-' ) { xmpDate.tzSign = kXMP_TimeWestOfUTC; } else if ( chPos != dsInfo.dataLen ) { return; // The DataSet is ill-formed. } ++chPos; // Move past the time zone sign. for ( chPos = 0, digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.tzHour = (xmpDate.tzHour * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.tzHour < 0 ) xmpDate.tzHour = 0; if ( xmpDate.tzHour > 23 ) xmpDate.tzHour = 23; if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; for ( digits = 0; digits < 2; ++digits, ++chPos ) { if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break; xmpDate.tzMinute = (xmpDate.tzMinute * 10) + (dsInfo.dataPtr[chPos] - '0'); } if ( xmpDate.tzMinute < 0 ) xmpDate.tzMinute = 0; if ( xmpDate.tzMinute > 59 ) xmpDate.tzMinute = 59; if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed. } // Finally, set the XMP property. xmp->SetProperty_Date ( xmpNS, xmpProp, xmpDate ); } // ImportIPTC_DateCreated// =================================================================================================// ReconcileUtils::ImportIPTC// ==========================void ReconcileUtils::ImportIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState ){ if ( digestState == kDigestMatches ) return; for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) { const DataSetCharacteristics & thisDS = kKnownDataSets[i]; try { // Don't let errors with one stop the others. switch ( thisDS.mapForm ) { case kIPTC_MapSimple : ImportIPTC_Simple ( iptc, xmp, digestState, thisDS.id, thisDS.xmpNS, thisDS.xmpProp ); break; case kIPTC_MapLangAlt : ImportIPTC_LangAlt ( iptc, xmp, digestState, thisDS.id, thisDS.xmpNS, thisDS.xmpProp ); break; case kIPTC_MapArray : ImportIPTC_Array ( iptc, xmp, digestState, thisDS.id, thisDS.xmpNS, thisDS.xmpProp ); break; case kIPTC_MapSpecial : if ( thisDS.id == kIPTC_IntellectualGenre ) { ImportIPTC_IntellectualGenre ( iptc, xmp, digestState, thisDS.xmpNS, thisDS.xmpProp ); } else if ( thisDS.id == kIPTC_SubjectCode ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -