static void ExportIPTC_SubjectCode ( const SXMPMeta & xmp, IPTC_Manager * iptc ) { std::string xmpValue, iimValue; XMP_OptionBits xmpFlags; bool found = xmp.GetProperty ( kXMP_NS_IPTCCore, "SubjectCode", 0, &xmpFlags ); if ( ! found ) { iptc->DeleteDataSet ( kIPTC_SubjectCode ); return; } if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet? XMP_Index xmpCount = xmp.CountArrayItems ( kXMP_NS_IPTCCore, "SubjectCode" ); XMP_Index iptcCount = (XMP_Index) iptc->GetDataSet ( kIPTC_SubjectCode, 0 ); if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( kIPTC_SubjectCode ); for ( XMP_Index ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0. (void) xmp.GetArrayItem ( kXMP_NS_IPTCCore, "SubjectCode", 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(), (XMP_Uns32)iimValue.size(), ds ); // ! Appends if necessary. } } // ExportIPTC_SubjectCode
static void ExportIPTC_IntellectualGenre ( const SXMPMeta & xmp, IPTC_Manager * iptc ) { std::string xmpValue; XMP_OptionBits xmpFlags; bool found = xmp.GetProperty ( kXMP_NS_IPTCCore, "IntellectualGenre", &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(), (XMP_Uns32)iimValue.size(), 0 ); // ! Don't append a 2nd DataSet! } // ExportIPTC_IntellectualGenre
static void ExportIPTC_Array ( const 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? XMP_Index xmpCount = xmp.CountArrayItems ( xmpNS, xmpProp ); XMP_Index iptcCount = (XMP_Index) iptc->GetDataSet ( id, 0 ); if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( id ); for ( XMP_Index 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(), (XMP_Uns32)value.size(), ds ); // ! Appends if necessary. } } // ExportIPTC_Array
spINode CreateTerminalNode( const char* nameSpace, const char * name, XMP_OptionBits options, const spcINode & nodeToBeCloned = spINode() ) { spINode newNode; if ( nodeToBeCloned ) { newNode = nodeToBeCloned->Clone(); } else if ( XMP_PropIsSimple( options ) ) { newNode = ISimpleNode::CreateSimpleNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); } else if ( XMP_PropIsStruct( options ) ) { newNode = IStructureNode_v1::CreateStructureNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); } else if ( XMP_PropIsArray( options ) ) { if ( options & kXMP_PropArrayIsAltText ) newNode = IArrayNode_v1::CreateAlternativeArrayNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); else if ( options & kXMP_PropArrayIsOrdered ) newNode = IArrayNode_v1::CreateOrderedArrayNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); else newNode = IArrayNode_v1::CreateUnorderedArrayNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); } return newNode; }
static void ExportIPTC_Simple ( const 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(), (XMP_Uns32)value.size(), 0 ); // ! Don't append a 2nd DataSet! } // ExportIPTC_Simple
int XmpParser::decode( XmpData& xmpData, const std::string& xmpPacket) { try { xmpData.clear(); if (xmpPacket.empty()) return 0; if (!initialize()) { #ifndef SUPPRESS_WARNINGS std::cerr << "XMP Toolkit initialization failed.\n"; #endif return 2; } SXMPMeta meta(xmpPacket.data(), static_cast<XMP_StringLen>(xmpPacket.size())); SXMPIterator iter(meta); std::string schemaNs, propPath, propValue; XMP_OptionBits opt; while (iter.Next(&schemaNs, &propPath, &propValue, &opt)) { #ifdef DEBUG printNode(schemaNs, propPath, propValue, opt); #endif if (XMP_PropIsAlias(opt)) { throw Error(47, schemaNs, propPath, propValue); continue; } if (XMP_NodeIsSchema(opt)) { // Register unknown namespaces with Exiv2 // (Namespaces are automatically registered with the XMP Toolkit) if (XmpProperties::prefix(schemaNs).empty()) { std::string prefix; bool ret = meta.GetNamespacePrefix(schemaNs.c_str(), &prefix); if (!ret) throw Error(45, schemaNs); prefix = prefix.substr(0, prefix.size() - 1); XmpProperties::registerNs(schemaNs, prefix); } continue; } XmpKey::AutoPtr key = makeXmpKey(schemaNs, propPath); if (XMP_ArrayIsAltText(opt)) { // Read Lang Alt property LangAltValue::AutoPtr val(new LangAltValue); XMP_Index count = meta.CountArrayItems(schemaNs.c_str(), propPath.c_str()); while (count-- > 0) { // Get the text bool haveNext = iter.Next(&schemaNs, &propPath, &propValue, &opt); #ifdef DEBUG printNode(schemaNs, propPath, propValue, opt); #endif if ( !haveNext || !XMP_PropIsSimple(opt) || !XMP_PropHasLang(opt)) { throw Error(41, propPath, opt); } const std::string text = propValue; // Get the language qualifier haveNext = iter.Next(&schemaNs, &propPath, &propValue, &opt); #ifdef DEBUG printNode(schemaNs, propPath, propValue, opt); #endif if ( !haveNext || !XMP_PropIsSimple(opt) || !XMP_PropIsQualifier(opt) || propPath.substr(propPath.size() - 8, 8) != "xml:lang") { throw Error(42, propPath, opt); } val->value_[propValue] = text; } xmpData.add(*key.get(), val.get()); continue; } if ( XMP_PropIsArray(opt) && !XMP_PropHasQualifiers(opt) && !XMP_ArrayIsAltText(opt)) { // Check if all elements are simple bool simpleArray = true; SXMPIterator aIter(meta, schemaNs.c_str(), propPath.c_str()); std::string aSchemaNs, aPropPath, aPropValue; XMP_OptionBits aOpt; while (aIter.Next(&aSchemaNs, &aPropPath, &aPropValue, &aOpt)) { if (propPath == aPropPath) continue; if ( !XMP_PropIsSimple(aOpt) || XMP_PropHasQualifiers(aOpt) || XMP_PropIsQualifier(aOpt) || XMP_NodeIsSchema(aOpt) || XMP_PropIsAlias(aOpt)) { simpleArray = false; break; } } if (simpleArray) { // Read the array into an XmpArrayValue XmpArrayValue::AutoPtr val(new XmpArrayValue(arrayValueTypeId(opt))); XMP_Index count = meta.CountArrayItems(schemaNs.c_str(), propPath.c_str()); while (count-- > 0) { iter.Next(&schemaNs, &propPath, &propValue, &opt); #ifdef DEBUG printNode(schemaNs, propPath, propValue, opt); #endif val->read(propValue); } xmpData.add(*key.get(), val.get()); continue; } } XmpTextValue::AutoPtr val(new XmpTextValue); if ( XMP_PropIsStruct(opt) || XMP_PropIsArray(opt)) { // Create a metadatum with only XMP options val->setXmpArrayType(xmpArrayType(opt)); val->setXmpStruct(xmpStruct(opt)); xmpData.add(*key.get(), val.get()); continue; } if ( XMP_PropIsSimple(opt) || XMP_PropIsQualifier(opt)) { val->read(propValue); xmpData.add(*key.get(), val.get()); continue; } // Don't let any node go by unnoticed throw Error(39, key->key(), opt); } // iterate through all XMP nodes return 0; } catch (const XMP_Error& e) { #ifndef SUPPRESS_WARNINGS std::cerr << Error(40, e.GetID(), e.GetErrMsg()) << "\n"; #endif xmpData.clear(); return 3; }} // XmpParser::decode
static void AppendSubtree ( const XMP_Node * sourceNode, XMP_Node * destParent, const bool replaceOld, const bool deleteEmpty ) { XMP_NodePtrPos destPos; XMP_Node * destNode = FindChildNode ( destParent, sourceNode->name.c_str(), kXMP_ExistingOnly, &destPos ); bool valueIsEmpty = false; if ( deleteEmpty ) { if ( XMP_PropIsSimple ( sourceNode->options ) ) { valueIsEmpty = sourceNode->value.empty(); } else { valueIsEmpty = sourceNode->children.empty(); } } if ( deleteEmpty & valueIsEmpty ) { if ( destNode != 0 ) { delete ( destNode ); destParent->children.erase ( destPos ); } } else if ( destNode == 0 ) { // The one easy case, the destination does not exist. CloneSubtree ( sourceNode, destParent ); } else if ( replaceOld ) { // The destination exists and should be replaced. destNode->value = sourceNode->value; // *** Should use SetNode. destNode->options = sourceNode->options; destNode->RemoveChildren(); destNode->RemoveQualifiers(); CloneOffspring ( sourceNode, destNode ); #if 0 // *** XMP_DebugBuild destNode->_valuePtr = destNode->value.c_str(); #endif } else { // The destination exists and is not totally replaced. Structs and arrays are merged. XMP_OptionBits sourceForm = sourceNode->options & kXMP_PropCompositeMask; XMP_OptionBits destForm = destNode->options & kXMP_PropCompositeMask; if ( sourceForm != destForm ) return; if ( sourceForm == kXMP_PropValueIsStruct ) { // To merge a struct process the fields recursively. E.g. add simple missing fields. The // recursive call to AppendSubtree will handle deletion for fields with empty values. for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) { const XMP_Node * sourceField = sourceNode->children[sourceNum]; AppendSubtree ( sourceField, destNode, replaceOld, deleteEmpty ); if ( deleteEmpty && destNode->children.empty() ) { delete ( destNode ); destParent->children.erase ( destPos ); } } } else if ( sourceForm & kXMP_PropArrayIsAltText ) { // Merge AltText arrays by the xml:lang qualifiers. Make sure x-default is first. Make a // special check for deletion of empty values. Meaningful in AltText arrays because the // xml:lang qualifier provides unambiguous source/dest correspondence. for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) { const XMP_Node * sourceItem = sourceNode->children[sourceNum]; if ( sourceItem->qualifiers.empty() || (sourceItem->qualifiers[0]->name != "xml:lang") ) continue; XMP_Index destIndex = LookupLangItem ( destNode, sourceItem->qualifiers[0]->value ); if ( deleteEmpty && sourceItem->value.empty() ) { if ( destIndex != -1 ) { delete ( destNode->children[destIndex] ); destNode->children.erase ( destNode->children.begin() + destIndex ); if ( destNode->children.empty() ) { delete ( destNode ); destParent->children.erase ( destPos ); } } } else { if ( destIndex != -1 ) continue; // Not replacing, keep the existing item. if ( (sourceItem->qualifiers[0]->value != "x-default") || destNode->children.empty() ) { CloneSubtree ( sourceItem, destNode ); } else { XMP_Node * destItem = new XMP_Node ( destNode, sourceItem->name, sourceItem->value, sourceItem->options ); CloneOffspring ( sourceItem, destItem ); destNode->children.insert ( destNode->children.begin(), destItem ); } } } } else if ( sourceForm & kXMP_PropValueIsArray ) { // Merge other arrays by item values. Don't worry about order or duplicates. Source // items with empty values do not cause deletion, that conflicts horribly with merging. for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) { const XMP_Node * sourceItem = sourceNode->children[sourceNum]; size_t destNum, destLim; for ( destNum = 0, destLim = destNode->children.size(); destNum != destLim; ++destNum ) { const XMP_Node * destItem = destNode->children[destNum]; if ( ItemValuesMatch ( sourceItem, destItem ) ) break; } if ( destNum == destLim ) CloneSubtree ( sourceItem, destNode ); } } } } // AppendSubtree