Merged rev. 1198-1213 from branches/xmp.
This commit is contained in:
parent
ac314ddfbc
commit
bc2fa9a4de
52
README-XMP
52
README-XMP
@ -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.
|
||||
Exiv2 uses the Adobe XMP toolkit (XMP SDK) to parse
|
||||
and serialize XMP packets (only the XMPCore component).
|
||||
|
||||
Supported XMP types
|
||||
-------------------
|
||||
Simple types : supported
|
||||
Structures : not supported
|
||||
Arrays : unordered and ordered arrays are supported
|
||||
alternative arrays are not supported
|
||||
|
||||
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
|
||||
|
||||
405
src/value.cpp
405
src/value.cpp
@ -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,11 +256,22 @@ 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];
|
||||
}
|
||||
|
||||
float StringValueBase::toFloat(long n) const
|
||||
{
|
||||
ok_ = true;
|
||||
return value_[n];
|
||||
}
|
||||
|
||||
Rational StringValueBase::toRational(long n) const
|
||||
{
|
||||
ok_ = true;
|
||||
return Rational(value_[n], 1);
|
||||
}
|
||||
|
||||
StringValue* StringValue::clone_() const
|
||||
@ -247,13 +279,6 @@ namespace Exiv2 {
|
||||
return new StringValue(*this);
|
||||
}
|
||||
|
||||
AsciiValue& AsciiValue::operator=(const AsciiValue& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
StringValueBase::operator=(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int AsciiValue::read(const std::string& buf)
|
||||
{
|
||||
value_ = 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;
|
||||
|
||||
return 0;
|
||||
xmpArrayType_ = xmpArrayType;
|
||||
}
|
||||
|
||||
int XmpTextValue::read(const byte* buf,
|
||||
long len,
|
||||
ByteOrder /*byteOrder*/)
|
||||
void XmpValue::setXmpStruct(XmpStruct xmpStruct)
|
||||
{
|
||||
std::string s(reinterpret_cast<const char*>(buf), len);
|
||||
return read(s);
|
||||
xmpStruct_ = xmpStruct;
|
||||
}
|
||||
|
||||
long XmpTextValue::copy(byte* buf,
|
||||
ByteOrder /*byteOrder*/) const
|
||||
XmpValue::XmpArrayType XmpValue::xmpArrayType() const
|
||||
{
|
||||
return xmpArrayType_;
|
||||
}
|
||||
|
||||
XmpValue::XmpStruct XmpValue::xmpStruct() const
|
||||
{
|
||||
return xmpStruct_;
|
||||
}
|
||||
|
||||
long XmpValue::copy(byte* buf,
|
||||
ByteOrder /*byteOrder*/) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
write(os);
|
||||
@ -449,46 +450,71 @@ 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
|
||||
{
|
||||
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;
|
||||
}
|
||||
return os;
|
||||
return os << value_;
|
||||
}
|
||||
|
||||
std::string XmpTextValue::toString(long n) const
|
||||
long XmpTextValue::toLong(long /*n*/) const
|
||||
{
|
||||
return value_[n];
|
||||
return stringTo<long>(value_, ok_);
|
||||
}
|
||||
|
||||
long XmpTextValue::toLong(long n) const
|
||||
float XmpTextValue::toFloat(long /*n*/) const
|
||||
{
|
||||
bool ok;
|
||||
return stringTo<long>(value_[n], ok);
|
||||
return stringTo<float>(value_, ok_);
|
||||
}
|
||||
|
||||
float XmpTextValue::toFloat(long n) const
|
||||
Rational XmpTextValue::toRational(long /*n*/) const
|
||||
{
|
||||
bool ok;
|
||||
return stringTo<float>(value_[n], ok);
|
||||
}
|
||||
|
||||
Rational XmpTextValue::toRational(long n) const
|
||||
{
|
||||
bool ok;
|
||||
return stringTo<Rational>(value_[n], ok);
|
||||
return stringTo<Rational>(value_, ok_);
|
||||
}
|
||||
|
||||
XmpTextValue* XmpTextValue::clone_() const
|
||||
@ -496,6 +522,141 @@ namespace Exiv2 {
|
||||
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 << ", ";
|
||||
os << *i;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string XmpArrayValue::toString(long n) const
|
||||
{
|
||||
ok_ = true;
|
||||
return value_[n];
|
||||
}
|
||||
|
||||
long XmpArrayValue::toLong(long n) const
|
||||
{
|
||||
return stringTo<long>(value_[n], ok_);
|
||||
}
|
||||
|
||||
float XmpArrayValue::toFloat(long n) const
|
||||
{
|
||||
return stringTo<float>(value_[n], ok_);
|
||||
}
|
||||
|
||||
Rational XmpArrayValue::toRational(long n) const
|
||||
{
|
||||
return stringTo<Rational>(value_[n], ok_);
|
||||
}
|
||||
|
||||
XmpArrayValue* XmpArrayValue::clone_() const
|
||||
{
|
||||
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)
|
||||
: Value(date)
|
||||
{
|
||||
@ -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
|
||||
|
||||
398
src/value.hpp
398
src/value.hpp
@ -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_) {}
|
||||
explicit XmpValue(TypeId typeId);
|
||||
//@}
|
||||
|
||||
//! Virtual destructor.
|
||||
virtual ~XmpTextValue() {}
|
||||
//! @name Accessors
|
||||
//@{
|
||||
//! 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 Write value to a character data buffer.
|
||||
|
||||
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
|
||||
//@{
|
||||
//! Assignment operator.
|
||||
XmpTextValue& operator=(const XmpTextValue& rhs);
|
||||
/*!
|
||||
@brief Read a text property value or array items from \em buf.
|
||||
|
||||
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);
|
||||
//! 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);
|
||||
}
|
||||
|
||||
|
||||
392
src/xmp.cpp
392
src/xmp.cpp
@ -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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
iter.Skip(kXMP_IterSkipSubtree);
|
||||
// Todo: Do not use read() for XmpTextValues
|
||||
val->read(arrayValue);
|
||||
}
|
||||
else {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: XMP property " << key->key()
|
||||
<< " has unsupported property type; skipping property.\n";
|
||||
#ifdef DEBUG
|
||||
printNode(schemaNs, propPath, propValue, opt);
|
||||
#endif
|
||||
iter.Skip(kXMP_IterSkipSubtree);
|
||||
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;
|
||||
}
|
||||
XmpKey::AutoPtr key = makeXmpKey(schemaNs, propPath);
|
||||
if (XMP_ArrayIsAltText(opt)) {
|
||||
// Read Lang Alt property
|
||||
LangAltValue::AutoPtr val(new LangAltValue);
|
||||
XMP_Index count = meta.CountArrayItems(schemaNs.c_str(), propPath.c_str());
|
||||
while (count-- > 0) {
|
||||
// Get the text
|
||||
bool haveNext = iter.Next(&schemaNs, &propPath, &propValue, &opt);
|
||||
#ifdef DEBUG
|
||||
printNode(schemaNs, propPath, propValue, opt);
|
||||
#endif
|
||||
if ( !haveNext
|
||||
|| !XMP_PropIsSimple(opt)
|
||||
|| !XMP_PropHasLang(opt)) {
|
||||
throw Error(41, propPath, opt);
|
||||
}
|
||||
const std::string text = propValue;
|
||||
// Get the language qualifier
|
||||
haveNext = iter.Next(&schemaNs, &propPath, &propValue, &opt);
|
||||
#ifdef DEBUG
|
||||
printNode(schemaNs, propPath, propValue, opt);
|
||||
#endif
|
||||
if ( !haveNext
|
||||
|| !XMP_PropIsSimple(opt)
|
||||
|| !XMP_PropIsQualifier(opt)
|
||||
|| propPath.substr(propPath.size() - 8, 8) != "xml:lang") {
|
||||
throw Error(42, propPath, opt);
|
||||
}
|
||||
val->value_[propValue] = text;
|
||||
}
|
||||
xmpData.add(*key.get(), val.get());
|
||||
continue;
|
||||
}
|
||||
if ( XMP_PropIsArray(opt)
|
||||
&& !XMP_PropHasQualifiers(opt)
|
||||
&& !XMP_ArrayIsAltText(opt)) {
|
||||
// Check if all elements are simple
|
||||
bool simpleArray = true;
|
||||
SXMPIterator aIter(meta, schemaNs.c_str(), propPath.c_str());
|
||||
std::string aSchemaNs, aPropPath, aPropValue;
|
||||
XMP_OptionBits aOpt;
|
||||
while (aIter.Next(&aSchemaNs, &aPropPath, &aPropValue, &aOpt)) {
|
||||
if (propPath == aPropPath) continue;
|
||||
if ( !XMP_PropIsSimple(aOpt)
|
||||
|| XMP_PropHasQualifiers(aOpt)
|
||||
|| XMP_PropIsQualifier(aOpt)
|
||||
|| XMP_NodeIsSchema(aOpt)
|
||||
|| XMP_PropIsAlias(aOpt)) {
|
||||
simpleArray = false;
|
||||
|
||||
xmpData.add(*key.get(), val.get());
|
||||
}
|
||||
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
|
||||
val->read(propValue);
|
||||
}
|
||||
xmpData.add(*key.get(), val.get());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
XmpTextValue::AutoPtr val(new XmpTextValue);
|
||||
if ( XMP_PropIsStruct(opt)
|
||||
|| XMP_PropIsArray(opt)) {
|
||||
// Create a metadatum with only XMP options
|
||||
val->setXmpArrayType(xmpArrayType(opt));
|
||||
val->setXmpStruct(xmpStruct(opt));
|
||||
xmpData.add(*key.get(), val.get());
|
||||
continue;
|
||||
}
|
||||
if ( XMP_PropIsSimple(opt)
|
||||
|| XMP_PropIsQualifier(opt)) {
|
||||
val->read(propValue);
|
||||
xmpData.add(*key.get(), val.get());
|
||||
continue;
|
||||
}
|
||||
// Don't let any node go by unnoticed
|
||||
throw Error(39, key->key(), opt);
|
||||
} // iterate through all XMP nodes
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (const XMP_Error& e) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << Error(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: 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) + "]";
|
||||
|
||||
// Todo: Need indicator if array is ordered
|
||||
//-ahu
|
||||
std::cerr << "Element " << item << " = " << i->toString(idx) << "\n";
|
||||
|
||||
//-ahu
|
||||
std::cerr << i->key() << " count = " << i->count() << "\n";
|
||||
|
||||
meta.AppendArrayItem(ns, i->tagName().c_str(),
|
||||
kXMP_PropArrayIsOrdered,
|
||||
i->toString(k).c_str());
|
||||
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();
|
||||
// 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);
|
||||
}
|
||||
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
|
||||
}
|
||||
return key;
|
||||
return Exiv2::XmpKey::AutoPtr(new Exiv2::XmpKey(prefix, property));
|
||||
} // makeXmpKey
|
||||
|
||||
}
|
||||
|
||||
17
src/xmp.hpp
17
src/xmp.hpp
@ -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…
Reference in New Issue
Block a user