Merged rev. 1198-1213 from branches/xmp.

v0.27.3
Andreas Huggel 18 years ago
parent ac314ddfbc
commit bc2fa9a4de

@ -1,39 +1,43 @@
XMP support
===========
Exiv2 uses the Adobe XMP toolkit (XMP SDK) to parse
and serialize XMP packets (only the XMPCore component).
Top-level Exiv2 classes to access XMP metadata are XmpData,
Xmpdatum and XmpKey. They work similar to the corresponding
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.
Top-level classes to access XMP metadata are XmpData,
XmpDatum and XmpKey. They work similar to the corresponding
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.
Supported XMP types
-------------------
Simple types : supported
Structures : not supported
Arrays : unordered and ordered arrays are supported
alternative arrays are not supported
Exiv2 uses the Adobe XMP toolkit (XMP SDK) to parse
and serialize XMP packets (only the XMPCore component).
Property Qualifiers : not supported
Language Alternatives : not supported
Supported XMP value types
-------------------------
All XMP value types are supported: Simple types, structures,
arrays, property qualifiers and language alternatives.
XMP properties are accessed through keys of the form
"Xmp.<Prefix>.<Property>", where <Prefix> is the preferred
(or rather, registered) prefix for a schema namespace and
<Property> is the name of a property in that namespace. Only
known properties in known namespaces are supported at the
moment. Functions to register namespaces and properterties
are not provided yet.
"Xmp.<Prefix>.<PropertyPath>", where <Prefix> is the preferred
(or rather, registered) prefix for a schema namespace and
<PropertyPath> is the path of the XMP node. In its most basic
form, to address simple properties, <PropertyPath> is the name
of the property. In general, <PropertyPath> can be used to
address any XMP node, including array items, structure fields
qualifiers and deeply nested properties.
Any properties in known namespaces are supported and additional
namespaces can be registered.
The specialized Exiv2 values XmpArrayValue and LangAltValue are
provided to simplify the use 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
========================
This is what worked for me on a Debian GNU/Linux (testing)
@ -52,8 +56,8 @@ this via configure options.
External packages (non-Debian)
-----------------
xmp_v411_sdk.zip - from adobe.com: http://www.adobe.com/devnet/xmp/sdk/eula.html
expat-2.0.1.tar.gz - from sourceforge.net: http://sourceforge.net/project/showfiles.php?group_id=10127
xmp_v411_sdk.zip - http://www.adobe.com/devnet/xmp/sdk/eula.html
expat-2.0.1.tar.gz - http://sourceforge.net/project/showfiles.php?group_id=10127
exiv2 - from SVN
Installation steps

@ -77,11 +77,20 @@ namespace Exiv2 {
ErrMsg( 32, N_("Setting %1 in %2 images is not supported")), // %1=metadata type, %2=image format
ErrMsg( 33, N_("This does not look like a CRW image")),
ErrMsg( 34, N_("%1: Not supported")), // %1=function
ErrMsg( 35, N_("Unknown XMP prefix `%1'")), // %1=prefix
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( 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( 39, N_("XMP Toolkit error %1: %2")), // %1=XMP_Error::GetID(), %2=XMP_Error::GetErrMsg()
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
ErrMsg( 42, N_("Failed to decode Lang Alt qualifier %1 with opt=%2")), // %1=qualifier path, %3=XMP Toolkit option flags
ErrMsg( 43, N_("Failed to encode Lang Alt property %1")), // %1=key
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)"))
};

@ -42,6 +42,7 @@ EXIV2_RCSID("@(#) $Id$")
#include "actions.hpp"
#include "utils.hpp"
#include "i18n.h" // NLS support.
#include "xmp.hpp"
#include <string>
#include <iostream>
@ -154,6 +155,7 @@ int main(int argc, char* const argv[])
taskFactory.cleanup();
params.cleanup();
Exiv2::XmpParser::terminate();
return rc;
} // main

@ -291,11 +291,10 @@ namespace Exiv2 {
io_->read(xmpPacket.pData_, xmpPacket.size_);
if (io_->error() || io_->eof()) throw Error(14);
xmpPacket_.assign(reinterpret_cast<char*>(xmpPacket.pData_), xmpPacket.size_);
if (XmpParser::decode(xmpData_, xmpPacket_)) {
if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: Failed to decode XMP metadata.\n";
#endif
xmpData_.clear();
}
--search;
}
@ -394,7 +393,7 @@ namespace Exiv2 {
throw Error(22);
}
const long bufMinSize = 31;
const long bufMinSize = 36;
long bufRead = 0;
DataBuf buf(bufMinSize);
const long seek = io_->tell();
@ -468,8 +467,7 @@ namespace Exiv2 {
}
if (exifData_.count() > 0) ++search;
// Todo: Update here when merging branches/xmp
if (xmpPacket_.size() > 0) ++search;
if (xmpData_.count() > 0) ++search;
if (iptcData_.count() > 0) ++search;
if (!comment_.empty()) ++search;
@ -526,22 +524,28 @@ namespace Exiv2 {
--search;
}
}
// Todo: Update here when merging branches/xmp
if (xmpPacket_.size() > 0) {
// Write APP1 marker, size of APP1 field, XMP id and XMP packet
tmpBuf[0] = 0xff;
tmpBuf[1] = app1_;
if (xmpData_.count() > 0) {
if (XmpParser::encode(xmpPacket_, xmpData_)) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: Failed to encode XMP metadata.\n";
#endif
}
if (xmpPacket_.size() > 0) {
// Write APP1 marker, size of APP1 field, XMP id and XMP packet
tmpBuf[0] = 0xff;
tmpBuf[1] = app1_;
if (xmpPacket_.size() + 31 > 0xffff) throw Error(37, "XMP");
us2Data(tmpBuf + 2, static_cast<uint16_t>(xmpPacket_.size() + 31), bigEndian);
memcpy(tmpBuf + 4, xmpId_, 29);
if (outIo.write(tmpBuf, 33) != 33) throw Error(21);
if (xmpPacket_.size() + 31 > 0xffff) throw Error(37, "XMP");
us2Data(tmpBuf + 2, static_cast<uint16_t>(xmpPacket_.size() + 31), bigEndian);
memcpy(tmpBuf + 4, xmpId_, 29);
if (outIo.write(tmpBuf, 33) != 33) throw Error(21);
// Write new XMP packet
if ( outIo.write(reinterpret_cast<const byte*>(xmpPacket_.data()), xmpPacket_.size())
!= static_cast<long>(xmpPacket_.size())) throw Error(21);
if (outIo.error()) throw Error(21);
--search;
// Write new XMP packet
if ( outIo.write(reinterpret_cast<const byte*>(xmpPacket_.data()), xmpPacket_.size())
!= static_cast<long>(xmpPacket_.size())) throw Error(21);
if (outIo.error()) throw Error(21);
--search;
}
}
if (psData.size_ > 0 || iptcData_.count() > 0) {
// Set the new IPTC IRB, keeps existing IRBs but removes the

@ -36,7 +36,7 @@ EXIV2_RCSID("@(#) $Id: pngchunk.cpp 823 2006-06-23 07:35:00Z cgilles $")
# include "exv_conf.h"
#endif
#define DEBUG 1
//#define DEBUG 1
// some defines to make it easier
#define PNG_CHUNK_TYPE(data, index) &data[index+4]

@ -52,11 +52,14 @@ namespace Exiv2 {
extern const XmpPropertyInfo xmpXmpMMInfo[];
extern const XmpPropertyInfo xmpXmpBJInfo[];
extern const XmpPropertyInfo xmpXmpTPgInfo[];
extern const XmpPropertyInfo xmpPhotoshopInfo[];
extern const XmpPropertyInfo xmpXmpDMInfo[];
extern const XmpPropertyInfo xmpPdfInfo[];
extern const XmpPropertyInfo xmpPhotoshopInfo[];
extern const XmpPropertyInfo xmpCrsInfo[];
extern const XmpPropertyInfo xmpTiffInfo[];
extern const XmpPropertyInfo xmpExifInfo[];
extern const XmpPropertyInfo xmpAuxInfo[];
extern const XmpPropertyInfo xmpIptcInfo[];
extern const XmpNsInfo xmpNsInfo[] = {
// Schemas
@ -69,11 +72,11 @@ namespace Exiv2 {
{ "http://ns.adobe.com/xmp/1.0/DynamicMedia/", "xmpDM", xmpXmpDMInfo, "XMP Dynamic Media schema" },
{ "http://ns.adobe.com/pdf/1.3/", "pdf", xmpPdfInfo, "Adobe PDF schema" },
{ "http://ns.adobe.com/photoshop/1.0/", "photoshop", xmpPhotoshopInfo, "Adobe photoshop schema" },
{ "http://ns.adobe.com/camera-raw-settings/1.0/", "crs", 0, "Camera Raw schema" },
{ "http://ns.adobe.com/camera-raw-settings/1.0/", "crs", xmpCrsInfo, "Camera Raw schema" },
{ "http://ns.adobe.com/tiff/1.0/", "tiff", xmpTiffInfo, "Exif Schema for TIFF Properties" },
{ "http://ns.adobe.com/exif/1.0/", "exif", xmpExifInfo, "Exif schema for Exif-specific Properties" },
{ "http://ns.adobe.com/exif/1.0/aux/", "aux", 0, "Exif schema for Additional Exif Properties" },
{ "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", "iptc" /*Iptc4xmpCore*/, 0, "IPTC Core schema" },
{ "http://ns.adobe.com/exif/1.0/aux/", "aux", xmpAuxInfo, "Exif schema for Additional Exif Properties" },
{ "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", "iptc", xmpIptcInfo, "IPTC Core schema" }, // 'Iptc4xmpCore' is just too long
// Structures
{ "http://ns.adobe.com/xap/1.0/g/", "xapG", 0, "Colorant structure" },
@ -326,6 +329,61 @@ namespace Exiv2 {
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
};
//! crs:CropUnits
extern const TagDetails xmpCrsCropUnits[] = {
{ 0, "pixels" },
{ 1, "inches" },
{ 2, "cm" }
};
extern const XmpPropertyInfo xmpCrsInfo[] = {
{ "AutoBrightness", "AutoBrightness", "Boolean", xmpText, xmpExternal, "When true, \"Brightness\" is automatically adjusted." },
{ "AutoContrast", "AutoContrast", "Boolean", xmpText, xmpExternal, "When true, \"Contrast\" is automatically adjusted." },
{ "AutoExposure", "AutoExposure", "Boolean", xmpText, xmpExternal, "When true, \"Exposure\" is automatically adjusted." },
{ "AutoShadows", "AutoShadows", "Boolean", xmpText, xmpExternal, "When true,\"Shadows\" is automatically adjusted." },
{ "BlueHue", "BlueHue", "Integer", signedShort, xmpExternal, "\"Blue Hue\" setting. Range -100 to 100." },
{ "BlueSaturation", "BlueSaturation", "Integer", signedShort, xmpExternal, "\"Blue Saturation\" setting. Range -100 to +100." },
{ "Brightness", "Brightness", "Integer", unsignedShort, xmpExternal, "\"Brightness\" setting. Range 0 to +150." },
{ "CameraProfile", "CameraProfile", "Text", xmpText, xmpExternal, "\"Camera Profile\" setting." },
{ "ChromaticAberrationB", "ChromaticAberrationB", "Integer", signedShort, xmpExternal, "\"Chomatic Aberration, Fix Blue/Yellow Fringe\" setting. Range -100 to +100." },
{ "ChromaticAberrationR", "ChromaticAberrationR", "Integer", signedShort, xmpExternal, "\"Chomatic Aberration, Fix Red/Cyan Fringe\" setting. Range -100 to +100." },
{ "ColorNoiseReduction", "ColorNoiseReduction", "Integer", unsignedShort, xmpExternal, "\"Color Noise Reducton\" setting. Range 0 to +100." },
{ "Contrast", "Contrast", "Integer", signedShort, xmpExternal, "\"Contrast\" setting. Range -50 to +100." },
{ "CropTop", "CropTop", "Real", xmpText, xmpExternal, "When HasCrop is true, top of crop rectangle" },
{ "CropLeft", "CropLeft", "Real", xmpText, xmpExternal, "When HasCrop is true, left of crop rectangle." },
{ "CropBottom", "CropBottom", "Real", xmpText, xmpExternal, "When HasCrop is true, bottom of crop rectangle." },
{ "CropRight", "CropRight", "Real", xmpText, xmpExternal, "When HasCrop is true, right of crop rectangle." },
{ "CropAngle", "CropAngle", "Real", xmpText, xmpExternal, "When HasCrop is true, angle of crop rectangle." },
{ "CropWidth", "CropWidth", "Real", xmpText, xmpExternal, "Width of resulting cropped image in CropUnits units." },
{ "CropHeight", "CropHeight", "Real", xmpText, xmpExternal, "Height of resulting cropped image in CropUnits units." },
{ "CropUnits", "CropUnits", "Integer", unsignedShort, xmpExternal, "Units for CropWidth and CropHeight. 0=pixels, 1=inches, 2=cm" },
{ "Exposure", "Exposure", "Real", xmpText, xmpExternal, "\"Exposure\" setting. Range -4.0 to +4.0." },
{ "GreenHue", "GreenHue", "Integer", signedShort, xmpExternal, "\"Green Hue\" setting. Range -100 to +100." },
{ "GreenSaturation", "GreenSaturation", "Integer", signedShort, xmpExternal, "\"Green Saturation\" setting. Range -100 to +100." },
{ "HasCrop", "HasCrop", "Boolean", xmpText, xmpExternal, "When true, image has a cropping rectangle." },
{ "HasSettings", "HasSettings", "Boolean", xmpText, xmpExternal, "When true, non-default camera raw settings." },
{ "LuminanceSmoothing", "LuminanceSmoothing", "Integer", unsignedShort, xmpExternal, "\"Luminance Smoothing\" setting. Range 0 to +100." },
{ "RawFileName", "RawFileName", "Text", xmpText, xmpInternal, "File name fo raw file (not a complete path)." },
{ "RedHue", "RedHue", "Integer", signedShort, xmpExternal, "\"Red Hue\" setting. Range -100 to +100." },
{ "RedSaturation", "RedSaturation", "Integer", signedShort, xmpExternal, "\"Red Saturation\" setting. Range -100 to +100." },
{ "Saturation", "Saturation", "Integer", signedShort, xmpExternal, "\"Saturation\" setting. Range -100 to +100." },
{ "Shadows", "Shadows", "Integer", unsignedShort, xmpExternal, "\"Shadows\" setting. Range 0 to +100." },
{ "ShadowTint", "ShadowTint", "Integer", signedShort, xmpExternal, "\"Shadow Tint\" setting. Range -100 to +100." },
{ "Sharpness", "Sharpness", "Integer", unsignedShort, xmpExternal, "\"Sharpness\" setting. Range 0 to +100." },
{ "Temperature", "Temperature", "Integer", unsignedShort, xmpExternal, "\"Temperature\" setting. Range 2000 to 50000." },
{ "Tint", "Tint", "Integer", signedShort, xmpExternal, "\"Tint\" setting. Range -150 to +150." },
{ "ToneCurve", "ToneCurve", "Seq of points (Integer, Integer)", xmpText, xmpExternal, "Array of points (Integer, Integer) defining a \"Tone Curve\"." },
{ "ToneCurveName", "ToneCurveName", "Choice Text", xmpText, xmpInternal, "The name of the Tone Curve described by ToneCurve. One of: Linear, Medium Contrast, "
"Strong Contrast, Custom or a user-defined preset name." },
{ "Version", "Version", "Text", xmpText, xmpInternal, "Version of Camera Raw plugin." },
{ "VignetteAmount", "VignetteAmount", "Integer", signedShort, xmpExternal, "\"Vignetting Amount\" setting. Range -100 to +100." },
{ "VignetteMidpoint", "VignetteMidpoint", "Integer", unsignedShort, xmpExternal, "\"Vignetting Midpoint\" setting. Range 0 to +100." },
{ "WhiteBalance", "WhiteBalance", "Closed Choice Text", xmpText, xmpExternal, "\"White Balance\" setting. One of: As Shot, Auto, Daylight, Cloudy, Shade, Tungsten, "
"Fluorescent, Flash, Custom" },
// End of list marker
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
};
extern const XmpPropertyInfo xmpTiffInfo[] = {
{ "ImageWidth", "ImageWidth", "Integer", unsignedLong, xmpInternal, "TIFF tag 256, 0x100. Image width in pixels." },
{ "ImageLength", "ImageLength", "Integer", unsignedLong, xmpInternal, "TIFF tag 257, 0x101. Image height in pixels." },
@ -663,6 +721,44 @@ namespace Exiv2 {
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
};
extern const XmpPropertyInfo xmpAuxInfo[] = {
{ "Lens", "Lens", "Text", xmpText, xmpInternal, "A description of the lens used to take the photograph. For example, \"70-200 mm f/2.8-4.0\"." },
{ "SerialNumber", "SerialNumber", "Text", xmpText, xmpInternal, "The serial number of the camera or camera body used to take the photograph." },
// End of list marker
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
};
extern const XmpPropertyInfo xmpIptcInfo[] = {
{ "CiAdrCity", "Contact Info-City", "Text", xmpText, xmpExternal, "The contact information city part." },
{ "CiAdrCtry", "Contact Info-Country", "Text", xmpText, xmpExternal, "The contact information country part." },
{ "CiAdrExtadr", "Contact Info-Address", "Text", xmpText, xmpExternal, "The contact information address part. Comprises an optional company name and all required "
"information to locate the building or postbox to which mail should be sent." },
{ "CiAdrPcode", "Contact Info-Postal Code", "Text", xmpText, xmpExternal, "The contact information part denoting the local postal code." },
{ "CiAdrRegion", "Contact Info-State/Province", "Text", xmpText, xmpExternal, "The contact information part denoting regional information like state or province." },
{ "CiEmailWork", "Contact Info-Email", "Text", xmpText, xmpExternal, "The contact information email address part." },
{ "CiTelWork", "Contact Info-Phone", "Text", xmpText, xmpExternal, "The contact information phone number part." },
{ "CiUrlWork", "Contact Info-Web URL", "Text", xmpText, xmpExternal, "The contact information web address part." },
{ "CountryCode", "Country Code", "closed Choice of Text", xmpText, xmpExternal, "Code of the country the content is focussing on -- either the country shown in visual "
"media or referenced in text or audio media. This element is at the top/first level of "
"a top-down geographical hierarchy. The code should be taken from ISO 3166 two or three "
"letter code. The full name of a country should go to the \"Country\" element." },
{ "CreatorContactInfo", "Creator's Contact Info", "ContactInfo", xmpText, xmpExternal, "The creator's contact information provides all necessary information to get in contact "
"with the creator of this news object and comprises a set of sub-properties for proper addressing." },
{ "IntellectualGenre", "Intellectual Genre", "Text", xmpText, xmpExternal, "Describes the nature, intellectual or journalistic characteristic of a news object, not "
"specifically its content." },
{ "Location", "Location", "Text", xmpText, xmpExternal, "Name of a location the content is focussing on -- either the location shown in visual "
"media or referenced by text or audio media. This location name could either be the name "
"of a sublocation to a city or the name of a well known location or (natural) monument "
"outside a city. In the sense of a sublocation to a city this element is at the fourth "
"level of a top-down geographical hierarchy." },
{ "Scene", "IPTC Scene", "bag closed Choice of Text", xmpText, xmpExternal, "Describes the scene of a photo content. Specifies one or more terms from the IPTC "
"\"Scene-NewsCodes\". Each Scene is represented as a string of 6 digits in an unordered list." },
{ "SubjectCode", "IPTC Subject Code", "bag closed Choice of Text", xmpText, xmpExternal, "Specifies one or more Subjects from the IPTC \"Subject-NewsCodes\" taxonomy to "
"categorize the content. Each Subject is represented as a string of 8 digits in an unordered list." },
// End of list marker
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
};
XmpNsInfo::Ns::Ns(const std::string& ns)
: ns_(ns)
{
@ -691,6 +787,44 @@ namespace Exiv2 {
return n == name;
}
XmpProperties::NsRegistry XmpProperties::nsRegistry_;
void XmpProperties::registerNs(const std::string& ns,
const std::string& prefix)
{
std::string ns2 = ns;
if (ns2.substr(ns2.size() - 1, 1) != "/") ns2 += "/";
nsRegistry_[ns2] = prefix;
}
std::string XmpProperties::prefix(const std::string& ns)
{
std::string ns2 = ns;
if (ns2.substr(ns2.size() - 1, 1) != "/") ns2 += "/";
NsRegistry::const_iterator i = nsRegistry_.find(ns2);
std::string p;
if (i != nsRegistry_.end()) {
p = i->second;
}
else {
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Ns(ns2));
if (xn) p = std::string(xn->prefix_);
}
return p;
}
std::string XmpProperties::ns(const std::string& prefix)
{
std::string n;
for (NsRegistry::const_iterator i = nsRegistry_.begin();
i != nsRegistry_.end(); ++i) {
if (i->second == prefix) {
return i->first;
}
}
return nsInfo(prefix)->ns_;
}
const char* XmpProperties::propertyTitle(const XmpKey& key)
{
return propertyInfo(key)->title_;
@ -721,11 +855,6 @@ namespace Exiv2 {
return pi;
}
const char* XmpProperties::ns(const std::string& prefix)
{
return nsInfo(prefix)->ns_;
}
const char* XmpProperties::nsDesc(const std::string& prefix)
{
return nsInfo(prefix)->desc_;
@ -743,12 +872,6 @@ namespace Exiv2 {
return xn;
}
const char* XmpProperties::prefix(const std::string& ns)
{
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Ns(ns));
return xn ? xn->prefix_ : 0;
}
void XmpProperties::printProperties(std::ostream& os, const std::string& prefix)
{
const XmpPropertyInfo* pl = propertyList(prefix);
@ -765,7 +888,7 @@ namespace Exiv2 {
//! Internal Pimpl structure with private members and data of class XmpKey.
struct XmpKey::Impl {
Impl(); //!< Default constructor
Impl() {} //!< Default constructor
Impl(const std::string& prefix, const std::string& property); //!< Constructor
/*!
@ -775,7 +898,7 @@ namespace Exiv2 {
@throw Error if the key cannot be decomposed.
*/
void decomposeKey(const XmpKey* self, const std::string& key);
void decomposeKey(const std::string& key);
// DATA
static const char* familyName_; //!< "Xmp"
@ -785,13 +908,13 @@ namespace Exiv2 {
};
//! @endcond
XmpKey::Impl::Impl()
{
}
XmpKey::Impl::Impl(const std::string& prefix, const std::string& property)
: prefix_(prefix), property_(property)
{
// Validate prefix
if (XmpProperties::ns(prefix).empty()) throw Error(46, prefix);
property_ = property;
prefix_ = prefix;
}
const char* XmpKey::Impl::familyName_ = "Xmp";
@ -799,14 +922,12 @@ namespace Exiv2 {
XmpKey::XmpKey(const std::string& key)
: p_(new Impl)
{
p_->decomposeKey(this, key);
p_->decomposeKey(key);
}
XmpKey::XmpKey(const std::string& prefix, const std::string& property)
: p_(new Impl(prefix, property))
{
// Validate prefix and property, throws
XmpProperties::propertyInfo(*this);
}
XmpKey::~XmpKey()
@ -861,12 +982,12 @@ namespace Exiv2 {
return XmpProperties::propertyTitle(*this);
}
const char* XmpKey::ns() const
std::string XmpKey::ns() const
{
return XmpProperties::ns(p_->prefix_);
}
void XmpKey::Impl::decomposeKey(const XmpKey* self, const std::string& key)
void XmpKey::Impl::decomposeKey(const std::string& key)
{
// Get the family name, prefix and property name parts of the key
std::string::size_type pos1 = key.find('.');
@ -883,12 +1004,11 @@ namespace Exiv2 {
std::string property = key.substr(pos1 + 1);
if (property == "") throw Error(6, key);
// Validate prefix
if (XmpProperties::ns(prefix).empty()) throw Error(46, prefix);
property_ = property;
prefix_ = prefix;
// Validate prefix and property
XmpProperties::propertyInfo(*self);
} // XmpKey::Impl::decomposeKey
// *************************************************************************

@ -141,7 +141,7 @@ namespace Exiv2 {
@return the namespace name
@throw Error if no namespace is registered with \em prefix.
*/
static const char* ns(const std::string& prefix);
static std::string ns(const std::string& prefix);
/*!
@brief Return the namespace description for the schema associated
with \em prefix.
@ -167,14 +167,28 @@ namespace Exiv2 {
*/
static const XmpNsInfo* nsInfo(const std::string& prefix);
/*!
@brief Return the (preferred) prefix for schema namespace \em ns
@brief Return the (preferred) prefix for schema namespace \em ns.
@param ns Schema namespace
@return the prefix or 0 if namespace \em ns is not registered.
@return the prefix or an empty string if namespace \em ns is not
registered.
*/
static const char* prefix(const std::string& ns);
static std::string prefix(const std::string& ns);
//! Print a list of properties of a schema namespace to output stream \em os.
static void printProperties(std::ostream& os, const std::string& prefix);
/*!
@brief Register namespace \em ns with preferred prefix \em prefix.
If the namespace is a known or previously registered namespace, the
prefix is overwritten. This also invalidates XMP keys generated with
the previous prefix.
*/
static void registerNs(const std::string& ns, const std::string& prefix);
private:
typedef std::map<std::string, std::string> NsRegistry;
static NsRegistry nsRegistry_;
}; // class XmpProperties
/*!
@ -192,8 +206,8 @@ namespace Exiv2 {
@param key The key string.
@throw Error if the first part of the key is not '<b>Xmp</b>' or
the remaining parts of the key cannot be parsed and
converted to a known schema prefix and property name.
the second part of the key cannot be parsed and converted
to a known schema prefix.
*/
explicit XmpKey(const std::string& key);
/*!
@ -203,8 +217,7 @@ namespace Exiv2 {
@param prefix Schema prefix name
@param property Property name
@throw Error if the schema prefix or the property name are not
known.
@throw Error if the schema prefix is not known.
*/
XmpKey(const std::string& prefix, const std::string& property);
//! Copy constructor.
@ -237,7 +250,7 @@ namespace Exiv2 {
// Todo: Should this be removed? What about tagLabel then?
//! Return the schema namespace for the prefix of the key
const char* ns() const;
std::string ns() const;
//@}
private:

@ -69,10 +69,12 @@ namespace Exiv2 {
TypeInfoTable(signedRational, "SRational", 8),
TypeInfoTable(string, "String", 1),
TypeInfoTable(date, "Date", 8),
TypeInfoTable(time, "Time", 11),
TypeInfoTable(time, "Time", 11),
TypeInfoTable(comment, "Comment", 1),
TypeInfoTable(directory, "Directory", 1),
TypeInfoTable(xmpText, "XmpText", 1),
TypeInfoTable(xmpArray, "XmpArray", 1),
TypeInfoTable(langAlt, "LangAlt", 1),
// End of list marker
TypeInfoTable(lastTypeId, "(Unknown)", 0)
};

@ -102,7 +102,7 @@ namespace Exiv2 {
string, date, time,
comment,
directory,
xmpText,
xmpText, xmpArray, langAlt,
lastTypeId };
// Todo: decentralize IfdId, so that new ids can be defined elsewhere

@ -50,10 +50,16 @@ EXIV2_RCSID("@(#) $Id$")
// class member definitions
namespace Exiv2 {
Value::Value(TypeId typeId)
: ok_(true), type_(typeId)
{
}
Value& Value::operator=(const Value& rhs)
{
if (this == &rhs) return *this;
type_ = rhs.type_;
ok_ = rhs.ok_;
return *this;
}
@ -109,6 +115,9 @@ namespace Exiv2 {
case xmpText:
value = AutoPtr(new XmpTextValue);
break;
case langAlt:
value = AutoPtr(new LangAltValue);
break;
default:
value = AutoPtr(new DataValue(typeId));
break;
@ -125,6 +134,7 @@ namespace Exiv2 {
{
std::ostringstream os;
write(os);
ok_ = !os.fail();
return os.str();
}
@ -133,14 +143,6 @@ namespace Exiv2 {
return toString();
}
DataValue& DataValue::operator=(const DataValue& rhs)
{
if (this == &rhs) return *this;
Value::operator=(rhs);
value_ = rhs.value_;
return *this;
}
int DataValue::read(const byte* buf, long len, ByteOrder /*byteOrder*/)
{
// byteOrder not needed
@ -192,9 +194,28 @@ namespace Exiv2 {
{
std::ostringstream os;
os << static_cast<int>(value_[n]);
ok_ = !os.fail();
return os.str();
}
long DataValue::toLong(long n) const
{
ok_ = true;
return value_[n];
}
float DataValue::toFloat(long n) const
{
ok_ = true;
return value_[n];
}
Rational DataValue::toRational(long n) const
{
ok_ = true;
return Rational(value_[n], 1);
}
StringValueBase& StringValueBase::operator=(const StringValueBase& rhs)
{
if (this == &rhs) return *this;
@ -235,23 +256,27 @@ namespace Exiv2 {
return os << value_;
}
StringValue& StringValue::operator=(const StringValue& rhs)
long StringValueBase::toLong(long n) const
{
if (this == &rhs) return *this;
StringValueBase::operator=(rhs);
return *this;
ok_ = true;
return value_[n];
}
StringValue* StringValue::clone_() const
float StringValueBase::toFloat(long n) const
{
return new StringValue(*this);
ok_ = true;
return value_[n];
}
AsciiValue& AsciiValue::operator=(const AsciiValue& rhs)
Rational StringValueBase::toRational(long n) const
{
if (this == &rhs) return *this;
StringValueBase::operator=(rhs);
return *this;
ok_ = true;
return Rational(value_[n], 1);
}
StringValue* StringValue::clone_() const
{
return new StringValue(*this);
}
int AsciiValue::read(const std::string& buf)
@ -326,13 +351,6 @@ namespace Exiv2 {
read(comment);
}
CommentValue& CommentValue::operator=(const CommentValue& rhs)
{
if (this == &rhs) return *this;
StringValueBase::operator=(rhs);
return *this;
}
int CommentValue::read(const std::string& comment)
{
std::string c = comment;
@ -387,60 +405,43 @@ namespace Exiv2 {
return new CommentValue(*this);
}
XmpTextValue& XmpTextValue::operator=(const XmpTextValue& rhs)
XmpValue::XmpValue(TypeId typeId)
: Value(typeId),
xmpArrayType_(xaNone),
xmpStruct_(xsNone)
{
}
XmpValue& XmpValue::operator=(const XmpValue& rhs)
{
if (this == &rhs) return *this;
Value::operator=(rhs);
value_ = rhs.value_;
xmpArrayType_ = rhs.xmpArrayType_;
xmpStruct_ = rhs.xmpStruct_;
return *this;
}
int XmpTextValue::read(const std::string& buf)
void XmpValue::setXmpArrayType(XmpArrayType xmpArrayType)
{
std::string::size_type start = 0;
bool escaped = false;
for (std::string::size_type i = 0; i < buf.size(); ++i) {
if (start == 0) {
// skip whitespace leading to the quote
if (isspace(buf[i])) continue;
// first character after that must be a quote
if (buf[i] == quoteChar[0]) {
start = i + 1;
continue;
}
return 1;
}
// look for the first unescaped quote
if (buf[i] == escapeChar[0] && !escaped) {
escaped = true;
continue;
}
if (buf[i] == quoteChar[0] && !escaped) {
value_.push_back(buf.substr(start, i - start));
// remove escape characters
unescapeText(value_.back());
start = 0;
continue;
}
escaped = false;
}
// check for premature end of string
if (escaped || start != 0) return 2;
xmpArrayType_ = xmpArrayType;
}
return 0;
void XmpValue::setXmpStruct(XmpStruct xmpStruct)
{
xmpStruct_ = xmpStruct;
}
int XmpTextValue::read(const byte* buf,
long len,
ByteOrder /*byteOrder*/)
XmpValue::XmpArrayType XmpValue::xmpArrayType() const
{
std::string s(reinterpret_cast<const char*>(buf), len);
return read(s);
return xmpArrayType_;
}
long XmpTextValue::copy(byte* buf,
ByteOrder /*byteOrder*/) const
XmpValue::XmpStruct XmpValue::xmpStruct() const
{
return xmpStruct_;
}
long XmpValue::copy(byte* buf,
ByteOrder /*byteOrder*/) const
{
std::ostringstream os;
write(os);
@ -449,51 +450,211 @@ namespace Exiv2 {
return s.size();
}
long XmpTextValue::size() const
int XmpValue::read(const byte* buf,
long len,
ByteOrder /*byteOrder*/)
{
std::string s(reinterpret_cast<const char*>(buf), len);
return read(s);
}
long XmpValue::size() const
{
std::ostringstream os;
write(os);
return os.str().size();
}
XmpTextValue::XmpTextValue()
: XmpValue(xmpText)
{
}
XmpTextValue::XmpTextValue(const std::string& buf)
: XmpValue(xmpText)
{
read(buf);
}
int XmpTextValue::read(const std::string& buf)
{
value_ = buf;
return 0;
}
XmpTextValue::AutoPtr XmpTextValue::clone() const
{
return AutoPtr(clone_());
}
long XmpTextValue::size() const
{
return static_cast<long>(value_.size());
}
long XmpTextValue::count() const
{
return size();
}
std::ostream& XmpTextValue::write(std::ostream& os) const
{
return os << value_;
}
long XmpTextValue::toLong(long /*n*/) const
{
return stringTo<long>(value_, ok_);
}
float XmpTextValue::toFloat(long /*n*/) const
{
return stringTo<float>(value_, ok_);
}
Rational XmpTextValue::toRational(long /*n*/) const
{
return stringTo<Rational>(value_, ok_);
}
XmpTextValue* XmpTextValue::clone_() const
{
return new XmpTextValue(*this);
}
XmpArrayValue::XmpArrayValue()
: XmpValue(xmpArray)
{
}
int XmpArrayValue::read(const std::string& buf)
{
value_.push_back(buf);
return 0;
}
XmpArrayValue::AutoPtr XmpArrayValue::clone() const
{
return AutoPtr(clone_());
}
long XmpArrayValue::count() const
{
return static_cast<long>(value_.size());
}
std::ostream& XmpArrayValue::write(std::ostream& os) const
{
for (std::vector<std::string>::const_iterator i = value_.begin();
i != value_.end(); ++i) {
if (i != value_.begin()) os << " ";
std::string s(*i);
quoteText(s);
os << s;
if (i != value_.begin()) os << ", ";
os << *i;
}
return os;
}
std::string XmpTextValue::toString(long n) const
std::string XmpArrayValue::toString(long n) const
{
ok_ = true;
return value_[n];
}
long XmpTextValue::toLong(long n) const
long XmpArrayValue::toLong(long n) const
{
bool ok;
return stringTo<long>(value_[n], ok);
return stringTo<long>(value_[n], ok_);
}
float XmpTextValue::toFloat(long n) const
float XmpArrayValue::toFloat(long n) const
{
bool ok;
return stringTo<float>(value_[n], ok);
return stringTo<float>(value_[n], ok_);
}
Rational XmpTextValue::toRational(long n) const
Rational XmpArrayValue::toRational(long n) const
{
bool ok;
return stringTo<Rational>(value_[n], ok);
return stringTo<Rational>(value_[n], ok_);
}
XmpTextValue* XmpTextValue::clone_() const
XmpArrayValue* XmpArrayValue::clone_() const
{
return new XmpTextValue(*this);
return new XmpArrayValue(*this);
}
LangAltValue::LangAltValue()
: XmpValue(langAlt)
{
}
LangAltValue::LangAltValue(const std::string& buf)
: XmpValue(langAlt)
{
read(buf);
}
int LangAltValue::read(const std::string& buf)
{
std::string b = buf;
std::string lang = "x-default";
if (buf.length() > 5 && buf.substr(0, 5) == "lang=") {
std::string::size_type pos = buf.find_first_of(' ');
lang = buf.substr(5, pos-5);
// Strip quotes (so you can also specify the language without quotes)
if (lang[0] == '"') lang = lang.substr(1);
if (lang[lang.length()-1] == '"') lang = lang.substr(0, lang.length()-1);
b.clear();
if (pos != std::string::npos) b = buf.substr(pos+1);
}
value_[lang] = b;
return 0;
}
LangAltValue::AutoPtr LangAltValue::clone() const
{
return AutoPtr(clone_());
}
long LangAltValue::count() const
{
return static_cast<long>(value_.size());
}
std::ostream& LangAltValue::write(std::ostream& os) const
{
for (ValueType::const_iterator i = value_.begin();
i != value_.end(); ++i) {
if (i != value_.begin()) os << ", ";
os << "lang=\"" << i->first << "\" "
<< i->second;
}
return os;
}
std::string LangAltValue::toString(long /*n*/) const
{
ok_ = false;
return "";
}
long LangAltValue::toLong(long /*n*/) const
{
ok_ = false;
return 0;
}
float LangAltValue::toFloat(long /*n*/) const
{
ok_ = false;
return 0.0;
}
Rational LangAltValue::toRational(long /*n*/) const
{
ok_ = false;
return Rational(0, 0);
}
LangAltValue* LangAltValue::clone_() const
{
return new LangAltValue(*this);
}
DateValue::DateValue(int year, int month, int day)
@ -504,16 +665,6 @@ namespace Exiv2 {
date_.day = day;
}
DateValue& DateValue::operator=(const DateValue& rhs)
{
if (this == &rhs) return *this;
Value::operator=(rhs);
date_.year = rhs.date_.year;
date_.month = rhs.date_.month;
date_.day = rhs.date_.day;
return *this;
}
int DateValue::read(const byte* buf, long len, ByteOrder /*byteOrder*/)
{
// Hard coded to read Iptc style dates
@ -602,7 +753,9 @@ namespace Exiv2 {
tms.tm_mday = date_.day;
tms.tm_mon = date_.month - 1;
tms.tm_year = date_.year - 1900;
return static_cast<long>(std::mktime(&tms));
long l = static_cast<long>(std::mktime(&tms));
ok_ = (l != -1);
return l;
}
TimeValue::TimeValue(int hour, int minute,
@ -610,19 +763,11 @@ namespace Exiv2 {
int tzMinute)
: Value(date)
{
time_.hour=hour;
time_.minute=minute;
time_.second=second;
time_.tzHour=tzHour;
time_.tzMinute=tzMinute;
}
TimeValue& TimeValue::operator=(const TimeValue& rhs)
{
if (this == &rhs) return *this;
Value::operator=(rhs);
memcpy(&time_, &rhs.time_, sizeof(time_));
return *this;
time_.hour = hour;
time_.minute = minute;
time_.second = second;
time_.tzHour = tzHour;
time_.tzMinute = tzMinute;
}
int TimeValue::read(const byte* buf, long len, ByteOrder /*byteOrder*/)
@ -760,34 +905,8 @@ namespace Exiv2 {
if (result < 0) {
result += 86400;
}
ok_ = true;
return result;
}
// *****************************************************************************
// free functions
const char quoteChar[] = "\"";
const char escapeChar[] = "\\";
void quoteText(std::string& text)
{
for (std::string::iterator i = text.begin(); i != text.end(); ++i) {
if (*i == escapeChar[0] || *i == quoteChar[0]) {
i = text.insert(i, escapeChar[0]);
if (++i == text.end()) break;
}
}
text = quoteChar + text + quoteChar;
} // quoteText
void unescapeText(std::string& text)
{
for (std::string::iterator i = text.begin(); i != text.end(); ++i) {
if (*i == escapeChar[0]) {
i = text.erase(i); // returns next pos, i.e., skips the escaped char
if (i == text.end()) break;
}
}
} // unescapeText
} // namespace Exiv2

@ -38,6 +38,7 @@
// + standard includes
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <sstream>
#include <memory>
@ -52,11 +53,10 @@ namespace Exiv2 {
/*!
@brief Common interface for all types of values used with metadata.
The interface provides a uniform way to access values independent from
The interface provides a uniform way to access values independent of
their actual C++ type for simple tasks like reading the values from a
string or data buffer. For other tasks, like modifying values you may
need to downcast it to the actual subclass of %Value so that you can
access the subclass specific interface.
need to downcast it to a specific subclass to access its interface.
*/
class Value {
public:
@ -66,15 +66,10 @@ namespace Exiv2 {
//! @name Creators
//@{
//! Constructor, taking a type id to initialize the base class with
explicit Value(TypeId typeId)
: type_(typeId) {}
//! Copy constructor
Value(const Value& rhs)
: type_(rhs.type_) {}
explicit Value(TypeId typeId);
//! Virtual destructor.
virtual ~Value() {}
//@}
//! @name Manipulators
//@{
/*!
@ -196,6 +191,11 @@ namespace Exiv2 {
DataBuf if the value does not have a data area assigned.
*/
virtual DataBuf dataArea() const { return DataBuf(0, 0); };
/*!
@brief Check the \em ok status indicator. After a to<Type> conversion,
this indicator shows whether the conversion was successful.
*/
bool ok() const { return ok_; }
//@}
/*!
@ -220,6 +220,8 @@ namespace Exiv2 {
<TR><TD class="indexkey">time</TD><TD class="indexvalue">%TimeValue</TD></TR>
<TR><TD class="indexkey">comment</TD><TD class="indexvalue">%CommentValue</TD></TR>
<TR><TD class="indexkey">xmpText</TD><TD class="indexvalue">%XmpTextValue</TD></TR>
<TR><TD class="indexkey">xmpArray</TD><TD class="indexvalue">%XmpArrayValue</TD></TR>
<TR><TD class="indexkey">langAlt</TD><TD class="indexvalue">%LangAltValue</TD></TR>
<TR><TD class="indexkey"><EM>default:</EM></TD><TD class="indexvalue">%DataValue(typeId)</TD></TR>
</TABLE>
@ -235,12 +237,14 @@ namespace Exiv2 {
by subclasses but not directly.
*/
Value& operator=(const Value& rhs);
// DATA
mutable bool ok_; //!< Indicates the status of the previous to<Type> conversion
private:
//! Internal virtual copy constructor.
virtual Value* clone_() const =0;
// DATA
TypeId type_; //!< Type of the data
TypeId type_; //!< Type of the data
}; // class Value
@ -259,7 +263,7 @@ namespace Exiv2 {
//! @name Creators
//@{
//! Default constructor.
DataValue(TypeId typeId =undefined) : Value(typeId) {}
explicit DataValue(TypeId typeId =undefined) : Value(typeId) {}
//! Constructor
DataValue(const byte* buf,
long len, ByteOrder byteOrder =invalidByteOrder,
@ -271,8 +275,6 @@ namespace Exiv2 {
//! @name Manipulators
//@{
//! Assignment operator.
DataValue& operator=(const DataValue& rhs);
/*!
@brief Read the value from a character buffer.
@ -318,17 +320,20 @@ namespace Exiv2 {
<EM>n</EM>-th component.
*/
virtual std::string toString(long n) const;
virtual long toLong(long n =0) const { return value_[n]; }
virtual float toFloat(long n =0) const { return value_[n]; }
virtual Rational toRational(long n =0) const
{ return Rational(value_[n], 1); }
virtual long toLong(long n =0) const;
virtual float toFloat(long n =0) const;
virtual Rational toRational(long n =0) const;
//@}
private:
//! Internal virtual copy constructor.
virtual DataValue* clone_() const;
public:
//! Type used to store the data.
typedef std::vector<byte> ValueType;
// DATA
std::vector<byte> value_;
ValueType value_;
}; // class DataValue
@ -346,7 +351,7 @@ namespace Exiv2 {
//! @name Creators
//@{
//! Constructor for subclasses
StringValueBase(TypeId typeId)
explicit StringValueBase(TypeId typeId)
: Value(typeId) {}
//! Constructor for subclasses
StringValueBase(TypeId typeId, const std::string& buf)
@ -361,8 +366,6 @@ namespace Exiv2 {
//! @name Manipulators
//@{
//! Assignment operator.
StringValueBase& operator=(const StringValueBase& rhs);
//! Read the value from buf. This default implementation uses buf as it is.
virtual int read(const std::string& buf);
/*!
@ -378,8 +381,8 @@ namespace Exiv2 {
@return 0 if successful.
*/
virtual int read(const byte* buf,
long len,
ByteOrder byteOrder =invalidByteOrder);
long len,
ByteOrder byteOrder =invalidByteOrder);
//@}
//! @name Accessors
@ -401,16 +404,19 @@ namespace Exiv2 {
virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) const;
virtual long count() const { return size(); }
virtual long size() const;
virtual long toLong(long n =0) const { return value_[n]; }
virtual float toFloat(long n =0) const { return value_[n]; }
virtual Rational toRational(long n =0) const
{ return Rational(value_[n], 1); }
virtual long toLong(long n =0) const;
virtual float toFloat(long n =0) const;
virtual Rational toRational(long n =0) const;
virtual std::ostream& write(std::ostream& os) const;
//@}
protected:
//! Assignment operator.
StringValueBase& operator=(const StringValueBase& rhs);
//! Internal virtual copy constructor.
virtual StringValueBase* clone_() const =0;
public:
// DATA
std::string value_; //!< Stores the string value.
@ -434,20 +440,12 @@ namespace Exiv2 {
StringValue()
: StringValueBase(string) {}
//! Constructor
StringValue(const std::string& buf)
explicit StringValue(const std::string& buf)
: StringValueBase(string, buf) {}
//! Copy constructor
StringValue(const StringValue& rhs)
: StringValueBase(rhs) {}
//! Virtual destructor.
virtual ~StringValue() {}
//@}
//! @name Manipulators
//@{
StringValue& operator=(const StringValue& rhs);
//@}
//! @name Accessors
//@{
AutoPtr clone() const { return AutoPtr(clone_()); }
@ -476,19 +474,14 @@ namespace Exiv2 {
AsciiValue()
: StringValueBase(asciiString) {}
//! Constructor
AsciiValue(const std::string &buf)
explicit AsciiValue(const std::string& buf)
: StringValueBase(asciiString, buf) {}
//! Copy constructor
AsciiValue(const AsciiValue& rhs)
: StringValueBase(rhs) {}
//! Virtual destructor.
virtual ~AsciiValue() {}
//@}
//! @name Manipulators
//@{
//! Assignment operator
AsciiValue& operator=(const AsciiValue& rhs);
/*!
@brief Set the value to that of the string buf. Overrides base class
to append a terminating '\\0' character if buf doesn't end
@ -569,18 +562,13 @@ namespace Exiv2 {
CommentValue()
: StringValueBase(Exiv2::undefined) {}
//! Constructor, uses read(const std::string& comment)
CommentValue(const std::string& comment);
//! Copy constructor
CommentValue(const CommentValue& rhs)
: StringValueBase(rhs) {}
explicit CommentValue(const std::string& comment);
//! Virtual destructor.
virtual ~CommentValue() {}
//@}
//! @name Manipulators
//@{
//! Assignment operator.
CommentValue& operator=(const CommentValue& rhs);
/*!
@brief Read the value from a comment
@ -617,44 +605,52 @@ namespace Exiv2 {
}; // class CommentValue
/*!
@brief %Value type suitable for XMP Text properties and arrays thereof.
Uses a vector of std::string to store the text(s).
@brief Base class for all Exiv2 values used to store XMP property values.
*/
class XmpTextValue : public Value {
class XmpValue : public Value {
public:
//! Shortcut for a %XmpTextValue auto pointer.
typedef std::auto_ptr<XmpTextValue> AutoPtr;
//! Shortcut for a %XmpValue auto pointer.
typedef std::auto_ptr<XmpValue> AutoPtr;
//! XMP array types.
enum XmpArrayType { xaNone, xaAlt, xaBag, xaSeq };
//! XMP structure indicator.
enum XmpStruct { xsNone, xsStruct };
//! @name Creators
//@{
//! Constructor for subclasses
XmpTextValue()
: Value(xmpText) {}
//! Constructor for subclasses
XmpTextValue(const std::string& buf)
: Value(xmpText) { read(buf); }
//! Copy constructor
XmpTextValue(const XmpTextValue& rhs)
: Value(rhs), value_(rhs.value_) {}
//! Virtual destructor.
virtual ~XmpTextValue() {}
explicit XmpValue(TypeId typeId);
//@}
//! @name Manipulators
//! @name Accessors
//@{
//! Assignment operator.
XmpTextValue& operator=(const XmpTextValue& rhs);
//! Return XMP array type, indicates if an XMP value is an array.
XmpArrayType xmpArrayType() const;
//! Return XMP struct, indicates if an XMP value is a structure.
XmpStruct xmpStruct() const;
virtual long size() const;
/*!
@brief Read a text property value or array items from \em buf.
@brief Write value to a character data buffer.
Expects a list of quoted strings, one for each array item. The
quoted strings may be separated by whitespace. Double quotes in
the strings must be escaped with a backslash character: \\".
Examples: ""\\"foo\\", he said"" or ""foo" "bar"".
*/
virtual int read(const std::string& buf);
The user must ensure that the buffer has enough memory. Otherwise
the call results in undefined behaviour.
@note The byte order is required by the interface but not used by this
method, so just use the default.
@param buf Data buffer to write to.
@param byteOrder Byte order. Not used.
@return Number of characters written.
*/
virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) const;
//@}
//! @name Manipulators
//@{
//! 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);
/*!
@brief Read the value from a character buffer.
@ -672,27 +668,126 @@ namespace Exiv2 {
virtual int read(const byte* buf,
long len,
ByteOrder byteOrder =invalidByteOrder);
virtual int read(const std::string& buf) =0;
//@}
protected:
/*!
@brief Assignment operator. Protected so that it can only be used
by subclasses but not directly.
*/
XmpValue& operator=(const XmpValue& rhs);
private:
// DATA
XmpArrayType xmpArrayType_; //!< Type of XMP array
XmpStruct xmpStruct_; //!< XMP structure indicator
}; // class XmpValue
/*!
@brief %Value type suitable for simple XMP properties and
XMP nodes of complex types which are not parsed into
specific values.
Uses a std::string to store the value.
*/
class XmpTextValue : public XmpValue {
public:
//! Shortcut for a %XmpTextValue auto pointer.
typedef std::auto_ptr<XmpTextValue> AutoPtr;
//! @name Creators
//@{
//! Constructor.
XmpTextValue();
//! Constructor, reads the value from a string.
explicit XmpTextValue(const std::string& buf);
//@}
//! @name Manipulators
//@{
virtual int read(const std::string& buf);
//@}
//! @name Accessors
//@{
AutoPtr clone() const { return AutoPtr(clone_()); }
AutoPtr clone() const;
long size() const;
virtual long count() const;
/*!
@brief Write value to a character data buffer.
@brief Convert the value to a long.
The optional parameter \em n is not used and is ignored.
The user must ensure that the buffer has enough memory. Otherwise
the call results in undefined behaviour.
@return The converted value.
*/
virtual long toLong(long n =0) const;
/*!
@brief Convert the value to a float.
The optional parameter \em n is not used and is ignored.
@note The byte order is required by the interface but not used by this
method, so just use the default.
@return The converted value.
*/
virtual float toFloat(long n =0) const;
/*!
@brief Convert the value to a Rational.
The optional parameter \em n is not used and is ignored.
@param buf Data buffer to write to.
@param byteOrder Byte order. Not used.
@return Number of characters written.
*/
virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) const;
virtual long count() const { return static_cast<long>(value_.size()); }
virtual long size() const;
@return The converted value.
*/
virtual Rational toRational(long n =0) const;
virtual std::ostream& write(std::ostream& os) const;
//@}
private:
//! Internal virtual copy constructor.
virtual XmpTextValue* clone_() const;
public:
// DATA
std::string value_; //!< Stores the string values.
}; // class XmpTextValue
/*!
@brief %Value type for simple arrays. Each item in the array is a simple
value, without qualifiers. The array may be an ordered (\em seq),
unordered (\em bag) or alternative array (\em alt). The array
items must not contain qualifiers. For language alternatives use
LangAltValue.
Uses a vector of std::string to store the value(s).
*/
class XmpArrayValue : public XmpValue {
public:
//! Shortcut for a %XmpArrayValue auto pointer.
typedef std::auto_ptr<XmpArrayValue> AutoPtr;
//! @name Creators
//@{
//! Constructor.
XmpArrayValue();
//@}
//! @name Manipulators
//@{
/*!
@brief Read a simple property value from \em buf and append it
to the value.
Appends \em buf to the value after the last existing array element.
Subsequent calls will therefore populate multiple array elements in
the order they are read.
@return 0 if successful.
*/
virtual int read(const std::string& buf);
//@}
//! @name Accessors
//@{
AutoPtr clone() const;
virtual long count() const;
/*!
@brief Return the <EM>n</EM>-th component of the value as a string.
The behaviour of this method may be undefined if there is no
@ -702,16 +797,105 @@ namespace Exiv2 {
virtual long toLong(long n =0) const;
virtual float toFloat(long n =0) const;
virtual Rational toRational(long n =0) const;
/*!
@brief Write all elements of the value to \em os, separated by commas.
@note The output of this method cannot directly be used as the parameter
for read().
*/
virtual std::ostream& write(std::ostream& os) const;
//@}
protected:
private:
//! Internal virtual copy constructor.
virtual XmpTextValue* clone_() const;
virtual XmpArrayValue* clone_() const;
public:
//! Type used to store XMP array elements.
typedef std::vector<std::string> ValueType;
// DATA
std::vector<std::string> value_; //!< Stores the string values.
}; // class XmpTextValue
}; // class XmpArrayValue
/*!
@brief %Value type for XMP language alternative properties.
A language alternative is an array consisting of simple text values,
each of which has a language qualifier.
*/
class LangAltValue : public XmpValue {
public:
//! Shortcut for a %LangAltValue auto pointer.
typedef std::auto_ptr<LangAltValue> AutoPtr;
//! @name Creators
//@{
//! Constructor.
LangAltValue();
//! Constructor, reads the value from a string.
explicit LangAltValue(const std::string& buf);
//@}
//! @name Manipulators
//@{
/*!
@brief Read a simple property value from \em buf and append it
to the value.
Appends \em buf to the value after the last existing array element.
Subsequent calls will therefore populate multiple array elements in
the order they are read.
The format of \em buf is:
<BR>
<CODE>[lang=["]language code["] ]text</CODE>
<BR>
The XMP default language code <CODE>x-default</CODE> is used if
\em buf doesn't start with the keyword <CODE>lang</CODE>.
@return 0 if successful.
*/
virtual int read(const std::string& buf);
//@}
//! @name Accessors
//@{
AutoPtr clone() const;
virtual long count() const;
/*!
@brief Return the <EM>n</EM>-th component of the value as a string.
The behaviour of this method may be undefined if there is no
<EM>n</EM>-th component.
*/
virtual std::string toString(long n) const;
virtual long toLong(long n =0) const;
virtual float toFloat(long n =0) const;
virtual Rational toRational(long n =0) const;
/*!
@brief Write all elements of the value to \em os, separated by commas.
@note The output of this method cannot directly be used as the parameter
for read().
*/
virtual std::ostream& write(std::ostream& os) const;
//@}
private:
//! Internal virtual copy constructor.
virtual LangAltValue* clone_() const;
public:
//! Type used to store language alternative arrays.
typedef std::map<std::string, std::string> ValueType;
// DATA
/*!
@brief Map to store the language alternative values. The language
qualifier is used as the key for the map entries.
*/
ValueType value_;
}; // class LangAltValue
/*!
@brief %Value for simple ISO 8601 dates
@ -744,8 +928,6 @@ namespace Exiv2 {
//! @name Manipulators
//@{
//! Assignment operator.
DateValue& operator=(const DateValue& rhs);
/*!
@brief Read the value from a character buffer.
@ -810,6 +992,7 @@ namespace Exiv2 {
private:
//! Internal virtual copy constructor.
virtual DateValue* clone_() const;
// DATA
Date date_;
@ -854,8 +1037,6 @@ namespace Exiv2 {
//! @name Manipulators
//@{
//! Assignment operator.
TimeValue& operator=(const TimeValue& rhs);
/*!
@brief Read the value from a character buffer.
@ -991,7 +1172,7 @@ namespace Exiv2 {
//! Constructor
ValueType(const byte* buf, long len, ByteOrder byteOrder);
//! Constructor
ValueType(const T& val, ByteOrder byteOrder =littleEndian);
explicit ValueType(const T& val, ByteOrder byteOrder =littleEndian);
//! Copy constructor
ValueType(const ValueType<T>& rhs);
//! Virtual destructor.
@ -1086,19 +1267,6 @@ namespace Exiv2 {
// *****************************************************************************
// free functions, template and inline definitions
//! Double-quote character
extern const char quoteChar[];
//! Escape character
extern const char escapeChar[];
/*!
@brief Quote a string with double-quotes, escape quotes and escape
characters in the string.
*/
void quoteText(std::string& text);
/*!
@brief Remove escape characters from an unquoted string.
*/
void unescapeText(std::string& text);
/*!
@brief Read a value of type T from the data buffer.
@ -1330,6 +1498,7 @@ namespace Exiv2 {
template<typename T>
inline std::string ValueType<T>::toString(long n) const
{
ok_ = true;
return Exiv2::toString(value_[n]);
}
@ -1337,54 +1506,63 @@ namespace Exiv2 {
template<typename T>
inline long ValueType<T>::toLong(long n) const
{
ok_ = true;
return value_[n];
}
// Specialization for rational
template<>
inline long ValueType<Rational>::toLong(long n) const
{
ok_ = (value_[n].second != 0);
return value_[n].first / value_[n].second;
}
// Specialization for unsigned rational
template<>
inline long ValueType<URational>::toLong(long n) const
{
ok_ = (value_[n].second != 0);
return value_[n].first / value_[n].second;
}
// Default implementation
template<typename T>
inline float ValueType<T>::toFloat(long n) const
{
ok_ = true;
return static_cast<float>(value_[n]);
}
// Specialization for rational
template<>
inline float ValueType<Rational>::toFloat(long n) const
{
ok_ = (value_[n].second != 0);
return static_cast<float>(value_[n].first) / value_[n].second;
}
// Specialization for unsigned rational
template<>
inline float ValueType<URational>::toFloat(long n) const
{
ok_ = (value_[n].second != 0);
return static_cast<float>(value_[n].first) / value_[n].second;
}
// Default implementation
template<typename T>
inline Rational ValueType<T>::toRational(long n) const
{
ok_ = true;
return Rational(value_[n], 1);
}
// Specialization for rational
template<>
inline Rational ValueType<Rational>::toRational(long n) const
{
ok_ = true;
return Rational(value_[n].first, value_[n].second);
}
// Specialization for unsigned rational
template<>
inline Rational ValueType<URational>::toRational(long n) const
{
ok_ = true;
return Rational(value_[n].first, value_[n].second);
}

@ -69,6 +69,29 @@ namespace {
}; // class FindXmpdatum
#ifdef EXV_HAVE_XMP_TOOLKIT
//! Convert XMP Toolkit struct option bit to Value::XmpStruct
Exiv2::XmpValue::XmpStruct xmpStruct(const XMP_OptionBits& opt);
//! Convert Value::XmpStruct to XMP Toolkit array option bits
XMP_OptionBits xmpOptionBits(Exiv2::XmpValue::XmpStruct xs);
//! Convert XMP Toolkit array option bits to Value::XmpArrayType
Exiv2::XmpValue::XmpArrayType xmpArrayType(const XMP_OptionBits& opt);
//! Convert Value::XmpArrayType to XMP Toolkit array option bits
XMP_OptionBits xmpOptionBits(Exiv2::XmpValue::XmpArrayType xat);
#define DEBUG
# ifdef DEBUG
//! Print information about a parsed XMP node
void printNode(const std::string& schemaNs,
const std::string& propPath,
const std::string& propValue,
const XMP_OptionBits& opt);
# endif // DEBUG
#endif // EXV_HAVE_XMP_TOOLKIT
//! Make an XMP key from a schema namespace and property path
Exiv2::XmpKey::AutoPtr makeXmpKey(const std::string& schemaNs,
const std::string& propPath);
@ -342,77 +365,154 @@ namespace Exiv2 {
bool XmpParser::initialized_ = false;
bool XmpParser::initialize()
{
if (!initialized_) {
initialized_ = SXMPMeta::Initialize();
}
return initialized_;
}
void XmpParser::terminate()
{
if (initialized_) {
SXMPMeta::Terminate();
initialized_ = false;
}
}
#ifdef EXV_HAVE_XMP_TOOLKIT
int XmpParser::decode( XmpData& xmpData,
const std::string& xmpPacket)
{ try {
xmpData.clear();
if (!initialized_) {
initialized_ = true;
if (!SXMPMeta::Initialize()) {
if (!initialize()) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "XMP Toolkit initialization failed.\n";
std::cerr << "XMP Toolkit initialization failed.\n";
#endif
return 2;
}
return 2;
}
SXMPMeta meta(xmpPacket.data(), xmpPacket.size());
SXMPIterator iter(meta);
std::string schemaNs, propPath, propValue;
XMP_OptionBits opt;
while (iter.Next(&schemaNs, &propPath, &propValue, &opt)) {
if (XMP_NodeIsSchema(opt)) continue;
XmpKey::AutoPtr key = makeXmpKey(schemaNs, propPath);
if (key.get() == 0) continue;
// Create an Exiv2 value and read the property value
Value::AutoPtr val = Value::create(XmpProperties::propertyType(*key.get()));
if (XMP_PropIsSimple(opt)) {
if (val->typeId() != xmpText) {
int ret = val->read(propValue);
if (ret != 0) val = Value::create(xmpText);
}
if (val->typeId() == xmpText) {
std::string pv = propValue;
// Todo: Do not use read() for XmpTextValues
quoteText(pv);
val->read(pv);
#ifdef DEBUG
printNode(schemaNs, propPath, propValue, opt);
#endif
if (XMP_PropIsAlias(opt)) {
// Todo: What should we do with these? Skip for now
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 Exiv2::Error(45, schemaNs);
prefix = prefix.substr(0, prefix.size() - 1);
XmpProperties::registerNs(schemaNs, prefix);
}
continue;
}
else if (XMP_PropIsArray(opt)) {
XMP_Index itemIdx = 1;
std::string itemValue, arrayValue;
XMP_OptionBits itemOpt;
while (meta.GetArrayItem(schemaNs.c_str(), propPath.c_str(),
itemIdx, &itemValue, &itemOpt)) {
if (val->typeId() == xmpText) quoteText(itemValue);
if (itemIdx > 1) arrayValue += " ";
arrayValue += itemValue;
++itemIdx;
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;
}
iter.Skip(kXMP_IterSkipSubtree);
// Todo: Do not use read() for XmpTextValues
val->read(arrayValue);
xmpData.add(*key.get(), val.get());
continue;
}
else {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: XMP property " << key->key()
<< " has unsupported property type; skipping property.\n";
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;
std::cerr << "NOT SO SIMPLE ==> " << propPath << ", " << aPropPath << "\n";
break;
}
}
if (simpleArray) {
// Read the array into an XmpArrayValue
XmpArrayValue::AutoPtr val(new XmpArrayValue);
XMP_Index count = meta.CountArrayItems(schemaNs.c_str(), propPath.c_str());
val->setXmpArrayType(xmpArrayType(opt));
while (count-- > 0) {
iter.Next(&schemaNs, &propPath, &propValue, &opt);
#ifdef DEBUG
printNode(schemaNs, propPath, propValue, opt);
#endif
iter.Skip(kXMP_IterSkipSubtree);
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;
}
xmpData.add(*key.get(), val.get());
}
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(39, e.GetID(), e.GetErrMsg()) << "\n";
std::cerr << Error(40, e.GetID(), e.GetErrMsg()) << "\n";
#endif
xmpData.clear();
return 3;
@ -436,55 +536,70 @@ namespace Exiv2 {
{ try {
xmpPacket.clear();
if (!initialized_) {
initialized_ = true;
if (!SXMPMeta::Initialize()) {
if (!initialize()) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "XMP Toolkit initialization failed.\n";
std::cerr << "XMP Toolkit initialization failed.\n";
#endif
return 2;
}
return 2;
}
SXMPMeta meta;
for (XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
const char* ns = XmpProperties::ns(i->groupName());
std::string ns = XmpProperties::ns(i->groupName());
// Todo: Make sure the namespace is registered with XMP-SDK
// Todo: Requires a separate indicator for array
// (or value type in general => reuse typeId?)
// Todo: What about structure namespaces?
if (i->count() == 1) {
// simple property
//-ahu
std::cerr << i->key() << "\n";
meta.SetProperty(ns, i->tagName().c_str(), i->toString(0).c_str());
XMP_OptionBits options = 0;
if (i->typeId() == langAlt) {
// Encode Lang Alt property
const LangAltValue* la = dynamic_cast<const LangAltValue*>(&i->value());
if (la == 0) throw Error(43, i->key());
int idx = 1;
for (LangAltValue::ValueType::const_iterator k = la->value_.begin();
k != la->value_.end(); ++k) {
meta.AppendArrayItem(ns.c_str(), i->tagName().c_str(), kXMP_PropArrayIsAltText, k->second.c_str());
const std::string item = i->tagName() + "[" + toString(idx++) + "]";
meta.SetQualifier(ns.c_str(), item.c_str(), kXMP_NS_XML, "lang", k->first.c_str());
}
continue;
}
if (i->count() > 1) {
// array or sequence
for (int k = 0; k < i->count(); ++k) {
// Todo: Need indicator if array is ordered
//-ahu
std::cerr << i->key() << " count = " << i->count() << "\n";
meta.AppendArrayItem(ns, i->tagName().c_str(),
kXMP_PropArrayIsOrdered,
i->toString(k).c_str());
// Todo: Xmpdatum should have an XmpValue, not a Value
const XmpValue* val = dynamic_cast<const XmpValue*>(&i->value());
assert(val);
options = xmpOptionBits(val->xmpArrayType())
| xmpOptionBits(val->xmpStruct());
if (i->typeId() == xmpArray) {
meta.SetProperty(ns.c_str(), i->tagName().c_str(), 0, options);
for (int idx = 0; idx < i->count(); ++idx) {
const std::string item = i->tagName() + "[" + toString(idx + 1) + "]";
//-ahu
std::cerr << "Element " << item << " = " << i->toString(idx) << "\n";
meta.SetProperty(ns.c_str(), item.c_str(), i->toString(idx).c_str());
}
}
if (i->typeId() == xmpText) {
if (i->count() == 0) {
meta.SetProperty(ns.c_str(), i->tagName().c_str(), 0, options);
}
else {
meta.SetProperty(ns.c_str(), i->tagName().c_str(), i->toString(0).c_str(), options);
}
}
}
meta.SerializeToBuffer(&xmpPacket, kXMP_UseCompactFormat);
return 0;
}
catch (const XMP_Error& e) {
#ifndef SUPPRESS_WARNINGS
std::cerr << Error(39, e.GetID(), e.GetErrMsg()) << "\n";
std::cerr << Error(40, e.GetID(), e.GetErrMsg()) << "\n";
#endif
xmpPacket.clear();
return 3;
@ -514,45 +629,112 @@ std::cerr << i->key() << " count = " << i->count() << "\n";
// *****************************************************************************
// local definitions
namespace {
#ifdef EXV_HAVE_XMP_TOOLKIT
Exiv2::XmpValue::XmpStruct xmpStruct(const XMP_OptionBits& opt)
{
Exiv2::XmpValue::XmpStruct var(Exiv2::XmpValue::xsNone);
if (XMP_PropIsStruct(opt)) {
var = Exiv2::XmpValue::xsStruct;
}
return var;
}
XMP_OptionBits xmpOptionBits(Exiv2::XmpValue::XmpStruct xs)
{
XMP_OptionBits var(0);
switch (xs) {
case Exiv2::XmpValue::xsNone:
break;
case Exiv2::XmpValue::xsStruct:
XMP_SetOption(var, kXMP_PropValueIsStruct);
break;
}
return var;
}
Exiv2::XmpValue::XmpArrayType xmpArrayType(const XMP_OptionBits& opt)
{
Exiv2::XmpValue::XmpArrayType var(Exiv2::XmpValue::xaNone);
if (XMP_PropIsArray(opt)) {
if (XMP_ArrayIsAlternate(opt)) var = Exiv2::XmpValue::xaAlt;
else if (XMP_ArrayIsOrdered(opt)) var = Exiv2::XmpValue::xaSeq;
else if (XMP_ArrayIsUnordered(opt)) var = Exiv2::XmpValue::xaBag;
}
return var;
}
XMP_OptionBits xmpOptionBits(Exiv2::XmpValue::XmpArrayType xat)
{
XMP_OptionBits var(0);
switch (xat) {
case Exiv2::XmpValue::xaNone:
break;
case Exiv2::XmpValue::xaAlt:
XMP_SetOption(var, kXMP_PropValueIsArray);
XMP_SetOption(var, kXMP_PropArrayIsAlternate);
break;
case Exiv2::XmpValue::xaSeq:
XMP_SetOption(var, kXMP_PropValueIsArray);
XMP_SetOption(var, kXMP_PropArrayIsOrdered);
break;
case Exiv2::XmpValue::xaBag:
XMP_SetOption(var, kXMP_PropValueIsArray);
break;
}
return var;
}
# ifdef DEBUG
void printNode(const std::string& schemaNs,
const std::string& propPath,
const std::string& propValue,
const XMP_OptionBits& opt)
{
static bool first = true;
if (first) {
first = false;
std::cout << "ashisaas\n"
<< "lcqqtrti\n";
}
enum { alia=0, sche, hasq, isqu, stru, arra, lang, simp, len };
std::string opts(len, '.');
if (XMP_PropIsAlias(opt)) opts[alia] = 'X';
if (XMP_NodeIsSchema(opt)) opts[sche] = 'X';
if (XMP_PropHasQualifiers(opt)) opts[hasq] = 'X';
if (XMP_PropIsQualifier(opt)) opts[isqu] = 'X';
if (XMP_PropIsStruct(opt)) opts[stru] = 'X';
if (XMP_PropIsArray(opt)) opts[arra] = 'X';
if (XMP_ArrayIsAltText(opt)) opts[lang] = 'X';
if (XMP_PropIsSimple(opt)) opts[simp] = 'X';
std::cout << opts << " ";
if (opts[sche] == 'X') {
std::cout << "ns=" << schemaNs;
}
else {
std::cout << propPath << " = " << propValue;
}
std::cout << std::endl;
}
# endif // DEBUG
#endif // EXV_HAVE_XMP_TOOLKIT
Exiv2::XmpKey::AutoPtr makeXmpKey(const std::string& schemaNs,
const std::string& propPath)
{
std::string property;
std::string::size_type idx = propPath.find(':');
if (idx != std::string::npos) {
// Don't worry about out_of_range, XMP parser takes care of this
property = propPath.substr(idx + 1);
if (idx == std::string::npos) {
throw Exiv2::Error(44, propPath, schemaNs);
}
else {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: Failed to determine property name from path "
<< propPath << ", namespace " << schemaNs
<< "; skipping property.\n";
#endif
return Exiv2::XmpKey::AutoPtr();
}
const char* prefix = Exiv2::XmpProperties::prefix(schemaNs);
if (prefix == 0) {
#ifndef SUPPRESS_WARNINGS
// Todo: Print warning only for the first property in each ns
std::cerr << "Warning: Unknown schema namespace "
<< schemaNs << "; skipping property "
<< property << ".\n";
#endif
return Exiv2::XmpKey::AutoPtr();
}
Exiv2::XmpKey::AutoPtr key;
// Todo: Avoid the try/catch block
try {
key = Exiv2::XmpKey::AutoPtr(new Exiv2::XmpKey(prefix, property));
}
catch (const Exiv2::AnyError& e) {
// This should only happen for unknown property names
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: " << e << "; skipping property.\n";
#endif
// Don't worry about out_of_range, XMP parser takes care of this
property = propPath.substr(idx + 1);
std::string prefix = Exiv2::XmpProperties::prefix(schemaNs);
if (prefix.empty()) {
throw Exiv2::Error(47, propPath, schemaNs);
}
return key;
return Exiv2::XmpKey::AutoPtr(new Exiv2::XmpKey(prefix, property));
} // makeXmpKey
}

@ -276,6 +276,23 @@ namespace Exiv2 {
*/
static int encode( std::string& xmpPacket,
const XmpData& xmpData);
/*!
@brief Initialize the XMP Toolkit.
Calling this method is usually not needed, as encode() and
decode() will initialize the XMP Toolkit if necessary.
@return True if the initialization was successful, else false.
*/
static bool initialize();
/*!
@brief Terminate the XMP Toolkit.
Call this method when the XmpParser is no longer needed to
allow the XMP Toolkit to cleanly shutdown.
*/
static void terminate();
private:
static bool initialized_; //! Indicates if the XMP Toolkit has been initialized

@ -43,6 +43,7 @@ try {
<< std::dec << md->value()
<< std::endl;
}
Exiv2::XmpParser::terminate();
return 0;
}
catch (Exiv2::AnyError& e) {

@ -59,7 +59,7 @@ try {
if (file.write(reinterpret_cast<const Exiv2::byte*>(xmpPacket.data()), xmpPacket.size()) == 0) {
throw Exiv2::Error(2, filename, Exiv2::strError(), "FileIo::write");
}
Exiv2::XmpParser::terminate();
return 0;
}
catch (Exiv2::AnyError& e) {

Loading…
Cancel
Save