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
Esempio n. 4
0
	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;
	}
Esempio n. 5
0
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
Esempio n. 6
0
    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
Esempio n. 7
0
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