📄 reconcileiptc.cpp
字号:
ImportIPTC_SubjectCode ( iptc, xmp, digestState, thisDS.xmpNS, thisDS.xmpProp ); } else if ( thisDS.id == kIPTC_DateCreated ) { ImportIPTC_DateCreated ( iptc, xmp, digestState, thisDS.xmpNS, thisDS.xmpProp ); } break; } } catch ( ... ) { // Do nothing, let other imports proceed. // ? Notify client? } } } // ReconcileUtils::ImportIPTC;// =================================================================================================// ReconcileUtils::ImportPSIR// ==========================//// There are only 2 standalone Photoshop image resources for XMP properties:// 1034 - Copyright Flag - 0/1 Boolean mapped to xmpRights:Marked.// 1035 - Copyright URL - Local OS text mapped to xmpRights:WebStatement.// ! Photoshop does not use a true/false/missing model for PSIR 1034. Instead it essentially uses a// ! yes/don't-know model when importing. A missing or 0 value for PSIR 1034 cause xmpRights:Marked// ! to be deleted.// **** What about 1008 and 1020?void ReconcileUtils::ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int digestState ){ PSIR_Manager::ImgRsrcInfo rsrcInfo; bool import; if ( digestState == kDigestMatches ) return; if ( digestState == kDigestDiffers ) { // Delete the mapped XMP. This forces replacement and catches legacy deletions. xmp->DeleteProperty ( kXMP_NS_XMP_Rights, "Marked" ); xmp->DeleteProperty ( kXMP_NS_XMP_Rights, "WebStatement" ); } try { // Don't let errors with one stop the others. import = psir.GetImgRsrc ( kPSIR_CopyrightFlag, &rsrcInfo ); if ( import ) import = (! xmp->DoesPropertyExist ( kXMP_NS_XMP_Rights, "Marked" )); if ( import && (rsrcInfo.dataLen == 1) && (*((XMP_Uns8*)rsrcInfo.dataPtr) != 0) ) { xmp->SetProperty_Bool ( kXMP_NS_XMP_Rights, "Marked", true ); } } catch ( ... ) { // Do nothing, let other imports proceed. // ? Notify client? } try { // Don't let errors with one stop the others. import = psir.GetImgRsrc ( kPSIR_CopyrightURL, &rsrcInfo ); if ( import ) import = (! xmp->DoesPropertyExist ( kXMP_NS_XMP_Rights, "WebStatement" )); if ( import ) { std::string utf8; ReconcileUtils::LocalToUTF8 ( rsrcInfo.dataPtr, rsrcInfo.dataLen, &utf8 ); xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", utf8.c_str() ); } } catch ( ... ) { // Do nothing, let other imports proceed. // ? Notify client? } } // ReconcileUtils::ImportPSIR;// =================================================================================================// =================================================================================================// =================================================================================================// ExportIPTC_Simple// =================static void ExportIPTC_Simple ( SXMPMeta * xmp, IPTC_Manager * iptc, const char * xmpNS, const char * xmpProp, XMP_Uns8 id ){ std::string value; XMP_OptionBits xmpFlags; bool found = xmp->GetProperty ( xmpNS, xmpProp, &value, &xmpFlags ); if ( ! found ) { iptc->DeleteDataSet ( id ); return; } if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? NormalizeToCR ( &value ); size_t iptcCount = iptc->GetDataSet ( id, 0 ); if ( iptcCount > 1 ) iptc->DeleteDataSet ( id ); iptc->SetDataSet_UTF8 ( id, value.c_str(), value.size(), 0 ); // ! Don't append a 2nd DataSet!} // ExportIPTC_Simple// =================================================================================================// ExportIPTC_LangAlt// ==================static void ExportIPTC_LangAlt ( SXMPMeta * xmp, IPTC_Manager * iptc, const char * xmpNS, const char * xmpProp, XMP_Uns8 id ){ std::string value; XMP_OptionBits xmpFlags; bool found = xmp->GetProperty ( xmpNS, xmpProp, 0, &xmpFlags ); if ( ! found ) { iptc->DeleteDataSet ( id ); return; } if ( ! XMP_ArrayIsAltText ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? found = xmp->GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &value, 0 ); if ( ! found ) { iptc->DeleteDataSet ( id ); return; } NormalizeToCR ( &value ); size_t iptcCount = iptc->GetDataSet ( id, 0 ); if ( iptcCount > 1 ) iptc->DeleteDataSet ( id ); iptc->SetDataSet_UTF8 ( id, value.c_str(), value.size(), 0 ); // ! Don't append a 2nd DataSet!} // ExportIPTC_LangAlt// =================================================================================================// ExportIPTC_Array// ================//// Array exporting needs a bit of care to preserve the detection of XMP-only updates. If the current// XMP and IPTC array sizes differ, delete the entire IPTC and append all new values. If they match,// set the individual values in order - which lets SetDataSet apply its no-change optimization.static void ExportIPTC_Array ( SXMPMeta * xmp, IPTC_Manager * iptc, const char * xmpNS, const char * xmpProp, XMP_Uns8 id ){ std::string value; XMP_OptionBits xmpFlags; bool found = xmp->GetProperty ( xmpNS, xmpProp, 0, &xmpFlags ); if ( ! found ) { iptc->DeleteDataSet ( id ); return; } if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? size_t xmpCount = xmp->CountArrayItems ( xmpNS, xmpProp ); size_t iptcCount = iptc->GetDataSet ( id, 0 ); if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( id ); for ( size_t ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0. (void) xmp->GetArrayItem ( xmpNS, xmpProp, ds+1, &value, &xmpFlags ); if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain? NormalizeToCR ( &value ); iptc->SetDataSet_UTF8 ( id, value.c_str(), value.size(), ds ); // ! Appends if necessary. }} // ExportIPTC_Array// =================================================================================================// ExportIPTC_IntellectualGenre// ============================//// Export DataSet 2:04. In the IIM this is a 3 digit number, a colon, and a 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. Look up the XMP value in a list of known genres to get the number.static void ExportIPTC_IntellectualGenre ( SXMPMeta * xmp, IPTC_Manager * iptc, const char * xmpNS, const char * xmpProp ){ std::string xmpValue; XMP_OptionBits xmpFlags; bool found = xmp->GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags ); if ( ! found ) { iptc->DeleteDataSet ( kIPTC_IntellectualGenre ); return; } if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? NormalizeToCR ( &xmpValue ); int i; XMP_StringPtr namePtr = xmpValue.c_str(); for ( i = 0; kIntellectualGenreMappings[i].name != 0; ++i ) { if ( strcmp ( namePtr, kIntellectualGenreMappings[i].name ) == 0 ) break; } if ( kIntellectualGenreMappings[i].name == 0 ) return; // Not a known genre, don't export it. std::string iimValue = kIntellectualGenreMappings[i].refNum; iimValue += ':'; iimValue += xmpValue; size_t iptcCount = iptc->GetDataSet ( kIPTC_IntellectualGenre, 0 ); if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_IntellectualGenre ); iptc->SetDataSet_UTF8 ( kIPTC_IntellectualGenre, iimValue.c_str(), iimValue.size(), 0 ); // ! Don't append a 2nd DataSet!} // ExportIPTC_IntellectualGenre// =================================================================================================// ExportIPTC_SubjectCode// ======================//// Export 2:12 DataSets from 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. We export with a fixed provider of "IPTC" and no optional names.static void ExportIPTC_SubjectCode ( SXMPMeta * xmp, IPTC_Manager * iptc, const char * xmpNS, const char * xmpProp ){ std::string xmpValue, iimValue; XMP_OptionBits xmpFlags; bool found = xmp->GetProperty ( xmpNS, xmpProp, 0, &xmpFlags ); if ( ! found ) { iptc->DeleteDataSet ( kIPTC_SubjectCode ); return; } if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? size_t xmpCount = xmp->CountArrayItems ( xmpNS, xmpProp ); size_t iptcCount = iptc->GetDataSet ( kIPTC_SubjectCode, 0 ); if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( kIPTC_SubjectCode ); for ( size_t ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0. (void) xmp->GetArrayItem ( xmpNS, xmpProp, ds+1, &xmpValue, &xmpFlags ); if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain? if ( xmpValue.size() != 8 ) continue; // ? Complain? iimValue = "IPTC:"; iimValue += xmpValue; iimValue += ":::"; // Add the separating colons for the empty name portions. iptc->SetDataSet_UTF8 ( kIPTC_SubjectCode, iimValue.c_str(), iimValue.size(), ds ); // ! Appends if necessary. }} // ExportIPTC_SubjectCode// =================================================================================================// ExportIPTC_DateCreated// ======================//// The IPTC date and time are "YYYYMMDD" and "HHMMSSxHHMM" where 'x' is '+' or '-'. Export the IPTC// time only if already present, or if the XMP has a time portion.static void ExportIPTC_DateCreated ( SXMPMeta * xmp, IPTC_Manager * iptc, const char * xmpNS, const char * xmpProp ){ std::string xmpStr; XMP_DateTime xmpValue; XMP_OptionBits xmpFlags; bool xmpHasTime = false; bool found = xmp->GetProperty ( xmpNS, xmpProp, &xmpStr, &xmpFlags ); if ( found ) { SXMPUtils::ConvertToDate ( xmpStr.c_str(), &xmpValue ); if ( xmpStr.size() > 10 ) xmpHasTime = true; // Date-only values are up to "YYYY-MM-DD". } else { iptc->DeleteDataSet ( kIPTC_DateCreated ); iptc->DeleteDataSet ( kIPTC_TimeCreated ); return; } char iimValue[16]; // Set the IIM date portion. snprintf ( iimValue, sizeof(iimValue), "%.4d%.2d%.2d", // AUDIT: Use of sizeof(iimValue) is safe. xmpValue.year, xmpValue.month, xmpValue.day ); if ( iimValue[8] != 0 ) return; // ? Complain? Delete the DataSet? size_t iptcCount = iptc->GetDataSet ( kIPTC_DateCreated, 0 ); if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_DateCreated ); iptc->SetDataSet_UTF8 ( kIPTC_DateCreated, iimValue, 8, 0 ); // ! Don't append a 2nd DataSet! // Set the IIM time portion. iptcCount = iptc->GetDataSet ( kIPTC_TimeCreated, 0 ); if ( (iptcCount > 0) || xmpHasTime ) { snprintf ( iimValue, sizeof(iimValue), "%.2d%.2d%.2d%c%.2d%.2d", // AUDIT: Use of sizeof(iimValue) is safe. xmpValue.hour, xmpValue.minute, xmpValue.second, ((xmpValue.tzSign == kXMP_TimeWestOfUTC) ? '-' : '+'), xmpValue.tzHour, xmpValue.tzMinute ); if ( iimValue[11] != 0 ) return; // ? Complain? Delete the DataSet? if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_TimeCreated ); iptc->SetDataSet_UTF8 ( kIPTC_TimeCreated, iimValue, 11, 0 ); // ! Don't append a 2nd DataSet! }} // ExportIPTC_DateCreated// =================================================================================================// ReconcileUtils::ExportIPTC// ==========================void ReconcileUtils::ExportIPTC ( SXMPMeta * xmp, IPTC_Manager * iptc ){ for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) { try { // Don't let errors with one stop the others. const DataSetCharacteristics & thisDS = kKnownDataSets[i]; switch ( thisDS.mapForm ) { case kIPTC_MapSimple : ExportIPTC_Simple ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id ); break; case kIPTC_MapLangAlt : ExportIPTC_LangAlt ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id ); break; case kIPTC_MapArray : ExportIPTC_Array ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id ); break; case kIPTC_MapSpecial : if ( thisDS.id == kIPTC_IntellectualGenre ) { ExportIPTC_IntellectualGenre ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp ); } else if ( thisDS.id == kIPTC_SubjectCode ) { ExportIPTC_SubjectCode ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp ); } else if ( thisDS.id == kIPTC_DateCreated ) { ExportIPTC_DateCreated ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp ); } break; } } catch ( ... ) { // Do nothing, let other exports proceed. // ? Notify client? } } } // ReconcileUtils::ExportIPTC;// =================================================================================================// ReconcileUtils::ExportPSIR// ==========================//// There are only 2 standalone Photoshop image resources for XMP properties:// 1034 - Copyright Flag - 0/1 Boolean mapped to xmpRights:Marked.// 1035 - Copyright URL - Local OS text mapped to xmpRights:WebStatement.// ! Photoshop does not use a true/false/missing model for PSIR 1034. Instead it is always written,// ! a missing xmpRights:Marked results in 0 for PSIR 1034.// ! We don't bother with the CR<->LF normalization for xmpRights:WebStatement. Very little chance// ! of having a raw CR character in a URI.void ReconcileUtils::ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir ){ bool found; std::string utf8Value; try { // Don't let errors with one stop the others. bool copyrighted = false; found = xmp.GetProperty ( kXMP_NS_XMP_Rights, "Marked", &utf8Value, 0 ); if ( found ) copyrighted = SXMPUtils::ConvertToBool ( utf8Value ); psir->SetImgRsrc ( kPSIR_CopyrightFlag, ©righted, 1 ); } catch ( ... ) { // Do nothing, let other exports proceed. // ? Notify client? } try { // Don't let errors with one stop the others. found = xmp.GetProperty ( kXMP_NS_XMP_Rights, "WebStatement", &utf8Value, 0 ); if ( ! found ) { psir->DeleteImgRsrc ( kPSIR_CopyrightURL ); } else { std::string localValue; ReconcileUtils::UTF8ToLocal ( utf8Value.c_str(), utf8Value.size(), &localValue ); psir->SetImgRsrc ( kPSIR_CopyrightURL, localValue.c_str(), localValue.size() ); } } catch ( ... ) { // Do nothing, let other exports proceed. // ? Notify client? }} // ReconcileUtils::ExportPSIR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -