Image metadata library and tools
Example 5: xmpsample.cpp
Sample (test) usage of high level XMP classes. This example shows various aspects of setting XMP metadata, including complex types. See also addmoddel.cpp
// ***************************************************************** -*- C++ -*- // xmpsample.cpp, $Rev: 1305 $ // Sample/test for high level XMP classes. See also addmoddel.cpp #include <exiv2/xmp.hpp> #include <exiv2/error.hpp> #include <string> #include <iostream> #include <iomanip> int main() try { // The XMP property container Exiv2::XmpData xmpData; // ------------------------------------------------------------------------- // Teaser: Setting XMP properties doesn't get much easier than this: xmpData["Xmp.dc.source"] = "xmpsample.cpp"; // a simple text value xmpData["Xmp.dc.subject"] = "Palmtree"; // an array item xmpData["Xmp.dc.subject"] = "Rubbertree"; // add a 2nd array item // a language alternative with two entries and without default xmpData["Xmp.dc.title"] = "lang=de-DE Sonnenuntergang am Strand"; xmpData["Xmp.dc.title"] = "lang=en-US Sunset on the beach"; // ------------------------------------------------------------------------- // Any properties can be set provided the namespace is known. Values of any // type can be assigned to an Xmpdatum, if they have an output operator. The // default XMP value type for unknown properties is a simple text value. xmpData["Xmp.dc.one"] = -1; xmpData["Xmp.dc.two"] = 3.1415; xmpData["Xmp.dc.three"] = Exiv2::Rational(5, 7); xmpData["Xmp.dc.four"] = uint16_t(255); xmpData["Xmp.dc.five"] = int32_t(256); xmpData["Xmp.dc.six"] = false; // In addition, there is a dedicated assignment operator for Exiv2::Value Exiv2::XmpTextValue val("Seven"); xmpData["Xmp.dc.seven"] = val; // ------------------------------------------------------------------------- // Exiv2 has specialized values for simple XMP properties, arrays of simple // properties and language alternatives. // Add a simple XMP property in a known namespace Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpText); v->read("image/jpeg"); xmpData.add(Exiv2::XmpKey("Xmp.dc.format"), v.get()); // Add an ordered array of text values. v = Exiv2::Value::create(Exiv2::xmpSeq); // or xmpBag or xmpAlt. v->read("1) The first creator"); // The sequence in which the array v->read("2) The second creator"); // elements are added is their v->read("3) And another one"); // order in the array. xmpData.add(Exiv2::XmpKey("Xmp.dc.creator"), v.get()); // Add a language alternative property v = Exiv2::Value::create(Exiv2::langAlt); v->read("lang=de-DE Hallo, Welt"); // The default doesn't need a v->read("Hello, World"); // qualifier xmpData.add(Exiv2::XmpKey("Xmp.dc.description"), v.get()); // According to the XMP specification, Xmp.tiff.ImageDescription is an // alias for Xmp.dc.description. Exiv2 treats an alias just like any // other property and leaves it to the application to implement specific // behaviour if desired. xmpData["Xmp.tiff.ImageDescription"] = "TIFF image description"; xmpData["Xmp.tiff.ImageDescription"] = "lang=de-DE TIFF Bildbeschreibung"; // ------------------------------------------------------------------------- // Register a namespace which Exiv2 doesn't know yet. This is only needed // when properties are added manually. If the XMP metadata is read from an // image, namespaces are decoded and registered at the same time. Exiv2::XmpProperties::registerNs("myNamespace/", "ns"); // ------------------------------------------------------------------------- // There are no specialized values for structures, qualifiers and nested // types. However, these can be added by using an XmpTextValue and a path as // the key. // Add a structure Exiv2::XmpTextValue tv("16"); xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:w"), &tv); tv.read("9"); xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:h"), &tv); tv.read("inch"); xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:unit"), &tv); // Add an element with a qualifier (using the namespace registered above) xmpData["Xmp.dc.publisher"] = "James Bond"; // creates an unordered array xmpData["Xmp.dc.publisher[1]/?ns:role"] = "secret agent"; // Add a qualifer to an array element of Xmp.dc.creator (added above) tv.read("programmer"); xmpData.add(Exiv2::XmpKey("Xmp.dc.creator[2]/?ns:role"), &tv); // Add an array of structures tv.read(""); // Clear the value tv.setXmpArrayType(Exiv2::XmpValue::xaBag); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef"), &tv); // Set the array type. tv.setXmpArrayType(Exiv2::XmpValue::xaNone); tv.read("Birthday party"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:name"), &tv); tv.read("Photographer"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:role"), &tv); tv.read("Wedding ceremony"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:name"), &tv); tv.read("Best man"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:role"), &tv); // ------------------------------------------------------------------------- // Output XMP properties for (Exiv2::XmpData::const_iterator md = xmpData.begin(); md != xmpData.end(); ++md) { std::cout << std::setfill(' ') << std::left << std::setw(44) << md->key() << " " << std::setw(9) << std::setfill(' ') << std::left << md->typeName() << " " << std::dec << std::setw(3) << std::setfill(' ') << std::right << md->count() << " " << std::dec << md->value() << std::endl; } // ------------------------------------------------------------------------- // Serialize the XMP data and output the XMP packet std::string xmpPacket; if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) { throw Exiv2::Error(1, "Failed to serialize XMP data"); } std::cout << xmpPacket << "\n"; // Cleanup Exiv2::XmpParser::terminate(); return 0; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return -1; }
The resulting XMP Exiv2 metadata and XMP packet is below. The same can be achieved with a set of commands to the exiv2 command line tool.
$ xmpsample
Xmp.dc.source XmpText 13 xmpsample.cpp
Xmp.dc.subject XmpBag 2 Palmtree, Rubbertree
Xmp.dc.title LangAlt 2 lang="de-DE" Sonnenuntergang am Strand, lang="en-US" Sunset on the beach
Xmp.dc.one XmpText 2 -1
Xmp.dc.two XmpText 6 3.1415
Xmp.dc.three XmpText 3 5/7
Xmp.dc.four XmpText 3 255
Xmp.dc.five XmpText 3 256
Xmp.dc.six XmpText 5 false
Xmp.dc.seven XmpText 5 Seven
Xmp.dc.format XmpText 10 image/jpeg
Xmp.dc.creator XmpSeq 3 1) The first creator, 2) The second creator, 3) And another one
Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt
Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung
Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16
Xmp.xmpDM.videoFrameSize/stDim:h XmpText 1 9
Xmp.xmpDM.videoFrameSize/stDim:unit XmpText 4 inch
Xmp.dc.publisher XmpBag 1 James Bond
Xmp.dc.publisher[1]/?ns:role XmpText 12 secret agent
Xmp.dc.creator[2]/?ns:role XmpText 10 programmer
Xmp.xmpBJ.JobRef XmpText 0 type="Bag"
Xmp.xmpBJ.JobRef[1]/stJob:name XmpText 14 Birthday party
Xmp.xmpBJ.JobRef[1]/stJob:role XmpText 12 Photographer
Xmp.xmpBJ.JobRef[2]/stJob:name XmpText 16 Wedding ceremony
Xmp.xmpBJ.JobRef[2]/stJob:role XmpText 8 Best man
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.1.1-Exiv2">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:ns="myNamespace/"
xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
xmlns:xmpDM="http://ns.adobe.com/xmp/1.0/DynamicMedia/"
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
xmlns:xapBJ="http://ns.adobe.com/xap/1.0/bj/"
xmlns:stJob="http://ns.adobe.com/xap/1.0/sType/Job#"
dc:source="xmpsample.cpp"
dc:one="-1"
dc:two="3.1415"
dc:three="5/7"
dc:four="255"
dc:five="256"
dc:six="false"
dc:seven="Seven"
dc:format="image/jpeg">
<dc:subject>
<rdf:Bag>
<rdf:li>Palmtree</rdf:li>
<rdf:li>Rubbertree</rdf:li>
</rdf:Bag>
</dc:subject>
<dc:title>
<rdf:Alt>
<rdf:li xml:lang="de-DE">Sonnenuntergang am Strand</rdf:li>
<rdf:li xml:lang="en-US">Sunset on the beach</rdf:li>
</rdf:Alt>
</dc:title>
<dc:creator>
<rdf:Seq>
<rdf:li>1) The first creator</rdf:li>
<rdf:li rdf:parseType="Resource">
<rdf:value>2) The second creator</rdf:value>
<ns:role>programmer</ns:role>
</rdf:li>
<rdf:li>3) And another one</rdf:li>
</rdf:Seq>
</dc:creator>
<dc:description>
<rdf:Alt>
<rdf:li xml:lang="x-default">Hello, World</rdf:li>
<rdf:li xml:lang="de-DE">Hallo, Welt</rdf:li>
</rdf:Alt>
</dc:description>
<dc:publisher>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<rdf:value>James Bond</rdf:value>
<ns:role>secret agent</ns:role>
</rdf:li>
</rdf:Bag>
</dc:publisher>
<tiff:ImageDescription>
<rdf:Alt>
<rdf:li xml:lang="x-default">TIFF image description</rdf:li>
<rdf:li xml:lang="de-DE">TIFF Bildbeschreibung</rdf:li>
</rdf:Alt>
</tiff:ImageDescription>
<xmpDM:videoFrameSize
stDim:w="16"
stDim:h="9"
stDim:unit="inch"/>
<xapBJ:JobRef>
<rdf:Bag>
<rdf:li
stJob:name="Birthday party"
stJob:role="Photographer"/>
<rdf:li
stJob:name="Wedding ceremony"
stJob:role="Best man"/>
</rdf:Bag>
</xapBJ:JobRef>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>