The new API is totally different in detail, the names of all include files, functions, types, and constants have changed. Many of the changes though are fairly cosmetic and trivial to port. In most other cases the new API should be more convenient to use. The iteration/enumeration functions are the only ones with significant visible semantic change.
This guide does not describe all of the old XMP functions, only those which have some non-trivial aspect for porting. If something is not described here, look at the old and new function signatures. They should be similar enough to make the transition obvious.
This is particularly true for the old UtilityXAP functions, they all have obvious mappings. Some of the UtilityXAP functions have been moved to TXMPMeta, mainly the binary get/set functions (UtilityXAP::GetBoolean has become TXMPMeta::GetProperty_Bool). Low value functions like UtilityXAP::AnalyzeStep or UtilityXAP::FilterPropPath have been removed.
Classes
The client API is defined by 3 C++ template classes: TXMPMeta, TXMPIterator, and TXMPUtils. These correspond to the old MetaXAP, XAPPaths, and UtilityXAP classes. The templates are instantiated with a string class which is used for the return of UTF-8 strings. The XMP headers will declare instantiated classes with the names SXMPMeta, SXMPIterator, and SXMPUtils.
The associated string class must provide these member functions, identical to those of std::string:
assign ( const char * str, size_t len ) const char * c_str() size_t size()
The result of assign does not matter, it is only used in a "void" manner.
Clients should only include XMP.hpp to access XMP, and XMP.incl_cpp to compile the template instantiations and client-side glue. The template string class is specified by defining TXMP_STRING_TYPE before the #include of either. A simple example is:
#define TXMP_STRING_TYPE std::string #include "XMP.hpp" #include "XMP.incl_cpp"
Basic Property Access
The old functions MetaXAP::get and MetaXAP::set are replaced by TXMPMeta::GetProperty and TXMPMeta::SetProperty. The basic mapping for these is very simple.
The old way to set a simple property:
meta.set ( XAP_NS_XAP, "CreatorTool", "Adobe Illustrator" );
The new way to set a simple property:
meta.SetProperty ( kXMP_NS_XMP, "CreatorTool", "Adobe Illustrator" );
The old way to get a simple property:
bool found; std::string value; XAPFeatures features; found = meta.get ( XAP_NS_XAP, "CreatorTool", value, features );
The new way to get a simple property:
bool found; std::string value; XMP_OptionBits options; found = meta.GetProperty ( kXMP_NS_XMP, "CreatorTool", &value, &options );
As before, you can pass XPath expressions for the second parameter to access elements of arrays or fields of structs. But there are new routines to make that easier. In addition, the new API allows you to eliminate the annoying '/*' for array items, reducing "Array/*[1]" to "Array[1]".
The options parameter returns considerably more information than the old features parameter. See the enum constants in XMP_Const.h for details.
Accessing Array Items
There are now special get/set functions for array items that take the index as an integer. This relieves you of the burden of formatting a path expression with the index as a decimal string. The examples below for the first element generalizes to any index in the obvious ways.
meta.set ( XAP_NS_XAP, "Authors/*[1]", "Frank Zappa" ); found = meta.get ( XAP_NS_XAP, "Authors/*[1]", value, features );
The new way to access the first element of an array:
meta.SetArrayItem ( kXMP_NS_XMP, "Authors", 1, "Frank Zappa" ); meta.GetArrayItem ( kXMP_NS_XMP, "Authors", 1, &value, &options );
The old way to access the last element of an array:
meta.set ( XAP_NS_XAP, "Authors/*[last()]", "Frank Zappa" ); found = meta.get ( XAP_NS_XAP, "Authors/*[last()]", value, features );
The new way to access the last element of an array:
meta.SetArrayItem ( kXMP_NS_XMP, "Authors", kXMP_ArrayLastItem, "Frank Zappa" ); meta.GetArrayItem ( kXMP_NS_XMP, "Authors", kXMP_ArrayLastItem, &value, &options );
Creating Array Items
The old API had rather cumbersome createFirstItem and append functions to use when adding an item to an array. The new API has a more convenient AppendArrayItem function.
The old way to create an array item, appending to the end:
size_t count; try { count = meta.count ( XAP_NS_XAP, "Authors/*" ); } catch ( ... ) { count = 0 } if ( count == 0 ) { meta.createFirstItem ( XAP_NS_XAP, "Authors", "Frank Zappa", xap_seq ); } else { meta.append ( XAP_NS_XAP, "Authors/*[last()]", "Frank Zappa" ) }
The new way to create an array item, appending to the end:
meta.AppendArrayItem ( kXMP_NS_XMP, "Authors", kXMP_PropArrayIsOrdered, "Frank Zappa" );
Here's how to see if an array exists or how big it is:
bool exists = meta.DoesPropertyExist ( kXMP_NS_XMP, "Authors" ); size_t count = meta.CountArrayItems ( kXMP_NS_XMP, "Authors" );
Accessing Struct Fields
In the old API you accessed a field in a struct by writing an XPath expression of the general form "Struct/ns:Field". This has the subtle drawback that you must use the namespace prefix in the XPath expression. But prefixes are not guaranteed! The new API has functions that take the namespace URI.
The old way to access a field in a struct:
meta.set ( XAP_NS_XAP_T_PG, "MaxPageSize/stDim:unit", "inch" ); found = meta.get ( XAP_NS_XAP_T_PG, "MaxPageSize/stDim:unit", value, features );
The new way to access a field in a struct:
meta.SetStructField ( XAP_NS_XAP_T_PG, "MaxPageSize", kXMP_NS_ST_Dim, "unit" "inch" ); found = meta.GetStructField ( XAP_NS_XAP_T_PG, "MaxPageSize", kXMP_NS_ST_Dim, "unit", &value, &options );
Accessing Composite Data Structures
The old API forced you to write full XPath expressions for composite data structures with arrays or structs nested inside each other. This forced you to know the XPath syntax, and suffer the risks of using namespace prefixes for struct fields. The new API has path composition functions in XMPUtils to avoid both of these problems. You can compose the entire path then call Get/SetProperty, or compose all but the last portion then use Get/SetArrayItem or Get/SetStructField.
The examples here only show ComposeArrayItemPath, ComposeStructFieldPath is an obvious analog.
The old way to access a struct field within an array item:
found = meta.get ( XAP_NS_XAP, "Thumbnails/*[1]/xapGImg:format", value, features );
The new way to access a struct field within an array item:
std::string itemPath; XMPUtils::ComposeArrayItemPath ( kXMP_NS_XMP, "Thumbnails", 1, &itemPath ); found = meta.GetStructField ( kXMP_NS_XMP, itemPath.c_str(), kXMP_NS_XMP_G_IMG, "format", &value, &options );
The new API has the same construct-and-parse capability as the old API. This parses one buffer that must be a complete collection of XMP. Parsing multiple buffers where you do the I/O is similar to before, differing mainly in a reversal of the "I'm done" signal.
The old way to read and parse:
MetaXAP meta; char buffer [...]; size_t length; while ( true ) { -- read into buffer, setting length if ( length != 0 ) break; meta.parse ( buffer, length ); } meta.parse ( buffer, 0, true );
The new way to read and parse:
SXMPMeta meta ( 0, 0 ); char buffer [...]; size_t length; while ( true ) { -- read into buffer, setting length if ( length != 0 ) break; meta.ParseFromBuffer ( buffer, length, kXMP_ParseMoreBuffers ); } meta.ParseFromBuffer ( buffer, 0 );
Serialization in the old API was a multi-step process. You used MetaXAP::serialize to generate a hidden serialization, then used MetaXAP::extractSerialization to extract that, then used UtilityXAP::CreateXMLPacket to get the XMP packet wrapping. The New API combines all of this into TXMPMeta::SerializeToBuffer.
The old way to write a serialized packet:
size_t rdfLength = meta.serialize(); std::string header, trailer; char buffer [...]; UtilityXAP::CreateXMLPacket ( "", true, 4096, "\n", header, trailer ); -- write the header string while ( rdfLength > sizeof(buffer) ) { meta.extractSerialization ( buffer, sizeof(buffer) ); -- write this buffer rdfLength -= sizeof(buffer); } meta.extractSerialization ( buffer, rdfLength ); -- write this buffer (only rdfLength bytes) -- write the trailer string
The new way to write a serialized packet:
std::string packet; meta.SerializeToBuffer ( &packet ); -- write the packet string
Iteration
The functions to iterate over the XMP data tree have not changed a lot at first glance, but the way they operate has changed considerably.
The old API essentially offered two ways to do an iteration:
Simple, default iteration code in the new API:
std::string str1, str2, str3; XMP_OptionBits options; SXMPIterator iter ( meta ); while ( iter.Next ( &str1, &str2, &str3, &options ) ) { fprintf ( log, " %s %s = \"%s\", 0x%X\n", str1.c_str(), str2.c_str(), str3.c_str(), options ); }
For this RDF input:
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> <rdf:Description rdf:about='' xmlns:ns1='ns:test1/' xmlns:ns2='ns:test2/'> <ns1:SimpleProp1>Simple1 value</ns1:SimpleProp1> <ns1:SimpleProp2 xml:lang='x-default'>Simple2 value</ns1:SimpleProp2> <ns1:ArrayProp1> <rdf:Bag> <rdf:li>Item1.1 value</rdf:li> <rdf:li>Item1.2 value</rdf:li> </rdf:Bag> </ns1:ArrayProp1> <ns1:ArrayProp2> <rdf:Alt> <rdf:li xml:lang='x-one'>Item2.1 value</rdf:li> <rdf:li xml:lang='x-two'>Item2.2 value</rdf:li> </rdf:Alt> </ns1:ArrayProp2> <ns1:StructProp rdf:parseType='Resource'> <ns2:Field1>Field1 value</ns2:Field1> <ns2:Field2>Field2 value</ns2:Field2> </ns1:StructProp> <ns1:QualProp1 rdf:parseType='Resource'> <rdf:value>Prop value</rdf:value> <ns2:Qual>Qual value</ns2:Qual> </ns1:QualProp1> <ns1:QualProp2 rdf:parseType='Resource'> <rdf:value xml:lang='x-default'>Prop value</rdf:value> <ns2:Qual>Qual value</ns2:Qual> </ns1:QualProp2> <ns1:NestedStructProp rdf:parseType='Resource'> <ns2:Outer rdf:parseType='Resource'> <ns2:Middle rdf:parseType='Resource'> <ns2:Inner rdf:parseType='Resource'> <ns2:Field1>Field1 value</ns2:Field1> <ns2:Field2>Field2 value</ns2:Field2> </ns2:Inner> </ns2:Middle> </ns2:Outer> </ns1:NestedStructProp> </rdf:Description> </rdf:RDF>
The iteration shown above would print (spacing added by hand for legibility):
ns:test1/ = "", 0x80000000 ns:test1/ SimpleProp1 = "Simple1 value", 0x0 ns:test1/ SimpleProp2 = "Simple2 value", 0x50 ns:test1/ SimpleProp2/?xml:lang = "x-default", 0x20 ns:test1/ ArrayProp1 = "", 0x200 ns:test1/ ArrayProp1[1] = "Item1.1 value", 0x0 ns:test1/ ArrayProp1[2] = "Item1.2 value", 0x0 ns:test1/ ArrayProp2 = "", 0xE00 ns:test1/ ArrayProp2[1] = "Item2.1 value", 0x50 ns:test1/ ArrayProp2[1]/?xml:lang = "x-one", 0x20 ns:test1/ ArrayProp2[2] = "Item2.2 value", 0x50 ns:test1/ ArrayProp2[2]/?xml:lang = "x-two", 0x20 ns:test1/ StructProp = "", 0x100 ns:test1/ StructProp/ns2:Field1 = "Field1 value", 0x0 ns:test1/ StructProp/ns2:Field2 = "Field2 value", 0x0 ns:test1/ QualProp1 = "Prop value", 0x10 ns:test1/ QualProp1/?ns2:Qual = "Qual value", 0x20 ns:test1/ QualProp2 = "Prop value", 0x50 ns:test1/ QualProp2/?xml:lang = "x-default", 0x20 ns:test1/ QualProp2/?ns2:Qual = "Qual value", 0x20 ns:test1/ NestedStructProp = "", 0x100 ns:test1/ NestedStructProp/ns2:Outer = "", 0x100 ns:test1/ NestedStructProp/ns2:Outer/ns2:Middle = "", 0x100 ns:test1/ NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner = "", 0x100 ns:test1/ NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner/ns2:Field1 = "Field1 value", 0x0 ns:test1/ NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner/ns2:Field2 = "Field2 value", 0x0