Extended xmpsample.cpp, related bugfixes and tweaks.

v0.27.3
Andreas Huggel 18 years ago
parent 9beec8880d
commit 16c95f0fab

@ -6,17 +6,6 @@ Exif and IPTC classes. The property-repository is XmpProperties.
In addition to the expected new members, class Image also
has a new interface to access the raw XMP packet.
Todo: XMP support is controlled with the ENABLE_XMP directive
in config/config.mk.in. This will be a configure option
eventually. For now, comment the line "ENABLE_XMP = 1" either
in config.mk.in before running ./configure or in config.mk to
build without XMP support.
Exiv2 uses the Adobe XMP toolkit (XMP SDK) to parse
and serialize XMP packets (only the XMPCore component).
Supported XMP value types
-------------------------
All XMP value types are supported: Simple types, structures,
arrays, property qualifiers and language alternatives.
@ -35,16 +24,28 @@ namespaces can be registered.
The specialized Exiv2 values XmpArrayValue and LangAltValue are
provided to simplify the use of XMP properties.
See xmpsample.cpp for examples of how to set various types of
XMP properties.
Note: Unlike Exif and IPTC tags, XMP properties do not have
a tag number.
Todo: Conversion between XMP and Exif/IPTC metadata.
XMP toolkit installation
Todo: XMP support is controlled with the ENABLE_XMP directive
in config/config.mk.in. This will be a configure option
eventually. For now, comment the line "ENABLE_XMP = 1" either
in config.mk.in before running ./configure or in config.mk to
build without XMP support.
Exiv2 uses the Adobe XMP Toolkit (XMP SDK) to parse
and serialize XMP packets (only the XMPCore component).
XMP Toolkit installation
========================
This is what worked for me on a Debian GNU/Linux (testing)
system. Your mileage may vary. Please check with Adobe if
you encounter problems with the XMP toolkit installation.
you encounter problems with the XMP Toolkit installation.
Installation directory
----------------------

@ -133,3 +133,7 @@ bug tracking system</a>.</p>
@example iptceasy.cpp
The quickest way to access, set or modify IPTC metadata
*/
/*!
@example xmpsample.cpp
Sample usage of high-level XMP classes.
*/

@ -78,9 +78,9 @@ namespace Exiv2 {
ErrMsg( 33, N_("This does not look like a CRW image")),
ErrMsg( 34, N_("%1: Not supported")), // %1=function
ErrMsg( 35, N_("No namespace info available for XMP prefix `%1'")), // %1=prefix
ErrMsg( 36, N_("No XMP property list for prefix `%1'")), // %1=prefix
ErrMsg( 36, N_("No prefix registered for namespace `%1'")), // %1=namespace
ErrMsg( 37, N_("Size of %1 JPEG segment is larger than 65535 bytes")), // %1=type of metadata (Exif, IPTC, JPEG comment)
ErrMsg( 38, N_("Unknown XMP property `%1:%2'")), // %1=prefix, %2=property name
ErrMsg( 38, N_("Unhandled Xmpdatum %1 of type %2")), // %1=key, %2=value type
ErrMsg( 39, N_("Unhandled XMP node %1 with opt=%2")), // %1=key, %2=XMP Toolkit option flags
ErrMsg( 40, N_("XMP Toolkit error %1: %2")), // %1=XMP_Error::GetID(), %2=XMP_Error::GetErrMsg()
ErrMsg( 41, N_("Failed to decode Lang Alt property %1 with opt=%2")), // %1=property path, %3=XMP Toolkit option flags
@ -89,7 +89,6 @@ namespace Exiv2 {
ErrMsg( 44, N_("Failed to determine property name from path %1, namespace %2")), // %1=property path, %2=namespace
ErrMsg( 45, N_("Schema namespace %1 is not registered with the XMP Toolkit")), // %1=namespace
ErrMsg( 46, N_("No namespace registered for prefix `%1'")), // %1=prefix
ErrMsg( 47, N_("No prefix registered for namespace `%1'")), // %1=namespace
// Last error message (message is not used)
ErrMsg( -2, N_("(Unknown Error)"))

@ -37,6 +37,7 @@ EXIV2_RCSID("@(#) $Id$")
#include "value.hpp"
#include "metadatum.hpp"
#include "i18n.h" // NLS support.
#include "xmp.hpp"
#include <iostream>
#include <iomanip>
@ -793,8 +794,10 @@ namespace Exiv2 {
const std::string& prefix)
{
std::string ns2 = ns;
if (ns2.substr(ns2.size() - 1, 1) != "/") ns2 += "/";
if ( ns2.substr(ns2.size() - 1, 1) != "/"
&& ns2.substr(ns2.size() - 1, 1) != "#") ns2 += "/";
nsRegistry_[ns2] = prefix;
XmpParser::registerNs(ns2, prefix);
}
std::string XmpProperties::prefix(const std::string& ns)
@ -827,28 +830,26 @@ namespace Exiv2 {
const char* XmpProperties::propertyTitle(const XmpKey& key)
{
return propertyInfo(key)->title_;
const XmpPropertyInfo* pi = propertyInfo(key);
return pi ? pi->title_ : 0;
}
const char* XmpProperties::propertyDesc(const XmpKey& key)
{
return propertyInfo(key)->desc_;
const XmpPropertyInfo* pi = propertyInfo(key);
return pi ? pi->desc_ : 0;
}
TypeId XmpProperties::propertyType(const XmpKey& key)
{
const XmpPropertyInfo* pi = propertyInfo(key, false);
const XmpPropertyInfo* pi = propertyInfo(key);
return pi ? pi->typeId_ : xmpText;
}
const XmpPropertyInfo* XmpProperties::propertyInfo(const XmpKey& key,
bool doThrow)
const XmpPropertyInfo* XmpProperties::propertyInfo(const XmpKey& key)
{
const XmpPropertyInfo* pl = propertyList(key.groupName());
if (!pl) {
if (doThrow) throw Error(36, key.groupName());
else return 0;
}
if (!pl) return 0;
const XmpPropertyInfo* pi = 0;
for (int i = 0; pl[i].name_ != 0; ++i) {
if (std::string(pl[i].name_) == key.tagName()) {
@ -856,7 +857,6 @@ namespace Exiv2 {
break;
}
}
if (!pi && doThrow) throw Error(38, key.groupName(), key.tagName());
return pi;
}

@ -108,15 +108,15 @@ namespace Exiv2 {
/*!
@brief Return the title (label) of the property.
@param key The property key
@return The title (label) of the property
@throw Error if the key is invalid.
@return The title (label) of the property, 0 if the
key is of an unknown property.
*/
static const char* propertyTitle(const XmpKey& key);
/*!
@brief Return the description of the property.
@param key The property key
@return The description of the property
@throw Error if the key is invalid.
@return The description of the property, 0 if the
key is of an unknown property.
*/
static const char* propertyDesc(const XmpKey& key);
/*!
@ -128,21 +128,16 @@ namespace Exiv2 {
static TypeId propertyType(const XmpKey& key);
/*!
@brief Return information for the property for key.
Always returns a valid pointer.
@param key The property key
@param doThrow Flag indicating whether to throw an Error or
return 0 if the key is not valid or unknown.
@return a pointer to the property information
@throw Error if the key is unknown and the \em doThrow
flag is true.
@return A pointer to the property information, 0 if the
key is of an unknown property.
*/
static const XmpPropertyInfo* propertyInfo(const XmpKey& key,
bool doThrow =true);
static const XmpPropertyInfo* propertyInfo(const XmpKey& key);
/*!
@brief Return the namespace name for the schema associated
with \em prefix.
@param prefix Prefix
@return the namespace name
@return The namespace name
@throw Error if no namespace is registered with \em prefix.
*/
static std::string ns(const std::string& prefix);
@ -150,7 +145,7 @@ namespace Exiv2 {
@brief Return the namespace description for the schema associated
with \em prefix.
@param prefix Prefix
@return the namespace description
@return The namespace description
@throw Error if no namespace is registered with \em prefix.
*/
static const char* nsDesc(const std::string& prefix);
@ -166,14 +161,14 @@ namespace Exiv2 {
@brief Return information about a schema namespace for \em prefix.
Always returns a valid pointer.
@param prefix The prefix
@return a pointer to the related information
@return A pointer to the related information
@throw Error if no namespace is registered with \em prefix.
*/
static const XmpNsInfo* nsInfo(const std::string& prefix);
/*!
@brief Return the (preferred) prefix for schema namespace \em ns.
@param ns Schema namespace
@return the prefix or an empty string if namespace \em ns is not
@return The prefix or an empty string if namespace \em ns is not
registered.
*/
static std::string prefix(const std::string& ns);

@ -652,7 +652,7 @@ namespace Exiv2 {
//! Set the XMP array type to indicate that an XMP value is an array.
void setXmpArrayType(XmpArrayType xmpArrayType);
//! Set the XMP struct type to indicate that an XMP value is a structure.
void setXmpStruct(XmpStruct xmpStruct);
void setXmpStruct(XmpStruct xmpStruct =xsStruct);
/*!
@brief Read the value from a character buffer.

@ -387,6 +387,17 @@ namespace Exiv2 {
}
}
bool XmpParser::registerNs(const std::string& ns,
const std::string& prefix)
{
initialize();
#ifdef EXV_HAVE_XMP_TOOLKIT
return SXMPMeta::RegisterNamespace(ns.c_str(), prefix.c_str(), 0);
#else
return true;
#endif
}
#ifdef EXV_HAVE_XMP_TOOLKIT
int XmpParser::decode( XmpData& xmpData,
const std::string& xmpPacket)
@ -549,15 +560,8 @@ namespace Exiv2 {
}
SXMPMeta meta;
for (XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
std::string ns = XmpProperties::ns(i->groupName());
// Todo: Make sure the namespace is registered with XMP-SDK
// Todo: What about structure namespaces?
const std::string ns = XmpProperties::ns(i->groupName());
XMP_OptionBits options = 0;
if (i->typeId() == langAlt) {
@ -605,6 +609,7 @@ namespace Exiv2 {
#endif
meta.SetProperty(ns.c_str(), item.c_str(), i->toString(idx).c_str());
}
continue;
}
if (i->typeId() == xmpText) {
if (i->count() == 0) {
@ -619,7 +624,10 @@ namespace Exiv2 {
#endif
meta.SetProperty(ns.c_str(), i->tagName().c_str(), i->toString(0).c_str(), options);
}
continue;
}
// Don't let any Xmpdatum go by unnoticed
throw Error(38, i->tagName(), TypeInfo::typeName(i->typeId()));
}
meta.SerializeToBuffer(&xmpPacket, kXMP_UseCompactFormat);
@ -771,7 +779,7 @@ namespace {
property = propPath.substr(idx + 1);
std::string prefix = Exiv2::XmpProperties::prefix(schemaNs);
if (prefix.empty()) {
throw Exiv2::Error(47, propPath, schemaNs);
throw Exiv2::Error(36, propPath, schemaNs);
}
return Exiv2::XmpKey::AutoPtr(new Exiv2::XmpKey(prefix, property));
} // makeXmpKey

@ -32,9 +32,9 @@
// *****************************************************************************
// included header files
#include "metadatum.hpp"
#include "types.hpp"
#include "value.hpp"
#include "properties.hpp"
#include "value.hpp"
#include "types.hpp"
// + standard includes
#include <string>
@ -245,6 +245,7 @@ namespace Exiv2 {
the XMP toolkit to do the job.
*/
class XmpParser {
friend void XmpProperties::registerNs(const std::string&, const std::string&);
public:
/*!
@brief Decode XMP metadata from an XMP packet \em xmpPacket into
@ -295,6 +296,17 @@ namespace Exiv2 {
static void terminate();
private:
/*!
@brief Register a namespace with the XMP Toolkit.
XmpProperties::registerNs calls this to synchronize namespaces.
@return \em true if the registered prefix matches the suggested prefix.
*/
static bool registerNs(const std::string& ns,
const std::string& prefix);
// DATA
static bool initialized_; //! Indicates if the XMP Toolkit has been initialized
}; // class XmpParser

@ -1,8 +1,7 @@
// ***************************************************************** -*- C++ -*-
// xmpsample.cpp, $Rev$
// Sample/test for high level XMP classes
// Sample/test for high level XMP classes. See also addmoddel.cpp
#include "value.hpp"
#include "xmp.hpp"
#include "error.hpp"
@ -10,31 +9,79 @@
#include <iostream>
#include <iomanip>
using namespace Exiv2;
int main()
try {
// The XMP property container
Exiv2::XmpData xmpData;
// -------------------------------------------------------------------------
// 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(xmpText);
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(xmpSeq);
v->read("1) The first creator"); // the sequence in which the array elements
v->read("2) The second creator"); // are added is relevant
v->read("3) And another one");
// 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(langAlt);
v->read("lang=de-DE Hallo, Welt"); // the default doesn't need a qualifier
v->read("Hello, World"); // and it will become the first element
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());
// -------------------------------------------------------------------------
// 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 a 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 earlier)
tv.read("James Bond");
xmpData.add(Exiv2::XmpKey("Xmp.dc.publisher"), &tv);
tv.read("secret agent");
xmpData.add(Exiv2::XmpKey("Xmp.dc.publisher/?ns:role"), &tv);
// 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("");
tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef"), &tv); // Set the array type.
tv.setXmpArrayType(Exiv2::XmpValue::xaNone);
tv.read("Birtday 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) {
@ -50,6 +97,7 @@ try {
<< std::endl;
}
// -------------------------------------------------------------------------
// Serialize the XMP data and output the XMP packet
std::string xmpPacket;
if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) {
@ -59,6 +107,7 @@ try {
// Cleanup
Exiv2::XmpParser::terminate();
return 0;
}
catch (Exiv2::AnyError& e) {

@ -279,3 +279,87 @@ Xmp.ns1.NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner/ns2:Field2 XmpText 12
>
> <?xpacket end="w"?>
\ No newline at end of file
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="de-DE" Hallo, Welt, lang="x-default" Hello, World
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 XmpText 10 James Bond
Xmp.dc.publisher/?ns:role XmpText 12 secret agent
Xmp.dc.creator[2]/?ns:role XmpText 10 programmer
Xmp.xmpBJ.JobRef XmpText 0
Xmp.xmpBJ.JobRef[1]/stJob:name XmpText 13 Birtday 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">
<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: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:format="image/jpeg">
<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:parseType="Resource">
<rdf:value>James Bond</rdf:value>
<ns:role>secret agent</ns:role>
</dc:publisher>
<xmpDM:videoFrameSize
stDim:w="16"
stDim:h="9"
stDim:unit="inch"/>
<xapBJ:JobRef>
<rdf:Bag>
<rdf:li
stJob:name="Birtday 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"?>

@ -52,6 +52,10 @@ $binpath/xmpparse ${testfile} > t1 2>&1
$binpath/xmpparse ${testfile}-new > t2 2>&1
diff t1 t2
# ----------------------------------------------------------------------
# xmpsample
$binpath/xmpsample
) > $results 2>&1
# ----------------------------------------------------------------------

Loading…
Cancel
Save