#539: Make registration of namespaces actually work (Vladimir Nadvornik, S M Ryan)
This commit is contained in:
parent
1b1df1e649
commit
2e3672d211
@ -54,6 +54,9 @@ set Xmp.tiff.ImageDescription lang=de-DE TIFF Bildbeschreibung
|
||||
# Register a namespace which Exiv2 doesn't know yet with a prefix.
|
||||
reg ns myNamespace/
|
||||
|
||||
# Add a property in the new custom namespace.
|
||||
set Xmp.ns.myProperty myValue
|
||||
|
||||
# There are no built-in Exiv2 value types for structures, qualifiers and
|
||||
# nested types. However, these can be added by using an XmpText value and a
|
||||
# path as the key.
|
||||
@ -65,7 +68,7 @@ set Xmp.xmpDM.videoFrameSize/stDim:unit inch
|
||||
|
||||
# Add an element with a qualifier (using the namespace registered earlier)
|
||||
set Xmp.dc.publisher James Bond
|
||||
set Xmp.dc.publisher/?ns:role secret agent
|
||||
set Xmp.dc.publisher[1]/?ns:role secret agent
|
||||
|
||||
# Add a qualifer to an array element of Xmp.dc.creator (added above)
|
||||
set Xmp.dc.creator[2]/?ns:role programmer
|
||||
|
||||
@ -75,6 +75,10 @@ try {
|
||||
// image, namespaces are decoded and registered at the same time.
|
||||
Exiv2::XmpProperties::registerNs("myNamespace/", "ns");
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Add a property in the new custom namespace.
|
||||
xmpData["Xmp.ns.myProperty"] = "myValue";
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// There are no specialized values for structures, qualifiers and nested
|
||||
// types. However, these can be added by using an XmpTextValue and a path as
|
||||
|
||||
@ -1413,7 +1413,7 @@ namespace Action {
|
||||
std::cout << _("Reg ") << modifyCmd.key_ << "=\""
|
||||
<< modifyCmd.value_ << "\"" << std::endl;
|
||||
}
|
||||
Exiv2::XmpProperties::registerNs(modifyCmd.value_, modifyCmd.key_);
|
||||
// Registration has been done immediately after parsing the command.
|
||||
}
|
||||
|
||||
Modify::AutoPtr Modify::clone() const
|
||||
|
||||
@ -1015,6 +1015,12 @@ namespace {
|
||||
modifyCmd.explicitType_ = explicitType;
|
||||
modifyCmd.value_ = value;
|
||||
|
||||
if (cmdId == reg) {
|
||||
// Registration needs to be done immediately as the new namespaces are
|
||||
// looked up during parsing of subsequent lines (to validate XMP keys).
|
||||
Exiv2::XmpProperties::registerNs(modifyCmd.value_, modifyCmd.key_);
|
||||
}
|
||||
|
||||
return true;
|
||||
} // parseLine
|
||||
|
||||
|
||||
@ -43,6 +43,8 @@ EXIV2_RCSID("@(#) $Id$")
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
@ -812,16 +814,57 @@ namespace Exiv2 {
|
||||
|
||||
XmpProperties::NsRegistry XmpProperties::nsRegistry_;
|
||||
|
||||
const XmpNsInfo* XmpProperties::lookupNsRegistry(const XmpNsInfo::Prefix& prefix)
|
||||
{
|
||||
for (NsRegistry::const_iterator i = nsRegistry_.begin();
|
||||
i != nsRegistry_.end(); ++i) {
|
||||
if (i->second == prefix) return &(i->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void XmpProperties::registerNs(const std::string& ns,
|
||||
const std::string& prefix)
|
||||
{
|
||||
std::string ns2 = ns;
|
||||
if ( ns2.substr(ns2.size() - 1, 1) != "/"
|
||||
&& ns2.substr(ns2.size() - 1, 1) != "#") ns2 += "/";
|
||||
nsRegistry_[ns2] = prefix;
|
||||
// Allocated memory is freed when the namespace is unregistered.
|
||||
// Using malloc/free for better system compatibility in case
|
||||
// users don't unregister their namespaces explicitely.
|
||||
XmpNsInfo xn;
|
||||
char* c = static_cast<char*>(std::malloc(ns2.size() + 1));
|
||||
std::strcpy(c, ns2.c_str());
|
||||
xn.ns_ = c;
|
||||
c = static_cast<char*>(std::malloc(prefix.size() + 1));
|
||||
std::strcpy(c, prefix.c_str());
|
||||
xn.prefix_ = c;
|
||||
xn.xmpPropertyInfo_ = 0;
|
||||
xn.desc_ = "";
|
||||
nsRegistry_[ns2] = xn;
|
||||
XmpParser::registerNs(ns2, prefix);
|
||||
}
|
||||
|
||||
void XmpProperties::unregisterNs(const std::string& ns)
|
||||
{
|
||||
NsRegistry::iterator i = nsRegistry_.find(ns);
|
||||
if (i != nsRegistry_.end()) {
|
||||
XmpParser::unregisterNs(ns);
|
||||
std::free(const_cast<char*>(i->second.prefix_));
|
||||
std::free(const_cast<char*>(i->second.ns_));
|
||||
nsRegistry_.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void XmpProperties::unregisterNs()
|
||||
{
|
||||
NsRegistry::iterator i = nsRegistry_.begin();
|
||||
while (i != nsRegistry_.end()) {
|
||||
NsRegistry::iterator kill = i++;
|
||||
unregisterNs(kill->first);
|
||||
}
|
||||
}
|
||||
|
||||
std::string XmpProperties::prefix(const std::string& ns)
|
||||
{
|
||||
std::string ns2 = ns;
|
||||
@ -829,7 +872,7 @@ namespace Exiv2 {
|
||||
NsRegistry::const_iterator i = nsRegistry_.find(ns2);
|
||||
std::string p;
|
||||
if (i != nsRegistry_.end()) {
|
||||
p = i->second;
|
||||
p = i->second.prefix_;
|
||||
}
|
||||
else {
|
||||
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Ns(ns2));
|
||||
@ -840,13 +883,8 @@ namespace Exiv2 {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
const XmpNsInfo* xn = lookupNsRegistry(XmpNsInfo::Prefix(prefix));
|
||||
if (xn != 0) return xn->ns_;
|
||||
return nsInfo(prefix)->ns_;
|
||||
}
|
||||
|
||||
@ -894,7 +932,9 @@ namespace Exiv2 {
|
||||
|
||||
const XmpNsInfo* XmpProperties::nsInfo(const std::string& prefix)
|
||||
{
|
||||
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Prefix(prefix));
|
||||
const XmpNsInfo::Prefix pf(prefix);
|
||||
const XmpNsInfo* xn = lookupNsRegistry(pf);
|
||||
if (!xn) xn = find(xmpNsInfo, pf);
|
||||
if (!xn) throw Error(35, prefix);
|
||||
return xn;
|
||||
}
|
||||
|
||||
@ -181,13 +181,33 @@ namespace Exiv2 {
|
||||
@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.
|
||||
prefix is overwritten.
|
||||
|
||||
@note This invalidates XMP keys generated with the previous prefix.
|
||||
*/
|
||||
static void registerNs(const std::string& ns, const std::string& prefix);
|
||||
/*!
|
||||
@brief Unregister a custom namespace \em ns.
|
||||
|
||||
The function only has an effect if there is a namespace \em ns
|
||||
registered earlier, it does not unregister built-in namespaces.
|
||||
|
||||
@note This invalidates XMP keys generated in this namespace.
|
||||
*/
|
||||
static void unregisterNs(const std::string& ns);
|
||||
/*!
|
||||
@brief Unregister all custom namespaces.
|
||||
|
||||
The function only unregisters namespaces registered earlier, it does not
|
||||
unregister built-in namespaces.
|
||||
|
||||
@note This invalidates XMP keys generated in any custom namespace.
|
||||
*/
|
||||
static void unregisterNs();
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, std::string> NsRegistry;
|
||||
typedef std::map<std::string, XmpNsInfo> NsRegistry;
|
||||
static const XmpNsInfo* lookupNsRegistry(const XmpNsInfo::Prefix& prefix);
|
||||
static NsRegistry nsRegistry_;
|
||||
|
||||
}; // class XmpProperties
|
||||
|
||||
16
src/xmp.cpp
16
src/xmp.cpp
@ -371,6 +371,7 @@ namespace Exiv2 {
|
||||
|
||||
void XmpParser::terminate()
|
||||
{
|
||||
XmpProperties::unregisterNs();
|
||||
if (initialized_) {
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
SXMPMeta::Terminate();
|
||||
@ -394,7 +395,20 @@ namespace Exiv2 {
|
||||
initialize();
|
||||
return true;
|
||||
#endif
|
||||
} // XmpParser::registerNs
|
||||
} // XmpParser::registerNs
|
||||
|
||||
void XmpParser::unregisterNs(const std::string& ns)
|
||||
{
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
try {
|
||||
// Throws XMP Toolkit error 8: Unimplemented method XMPMeta::DeleteNamespace
|
||||
// SXMPMeta::DeleteNamespace(ns.c_str());
|
||||
}
|
||||
catch (const XMP_Error& e) {
|
||||
throw Error(40, e.GetID(), e.GetErrMsg());
|
||||
}
|
||||
#endif
|
||||
} // XmpParser::unregisterNs
|
||||
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
int XmpParser::decode( XmpData& xmpData,
|
||||
|
||||
@ -257,6 +257,7 @@ namespace Exiv2 {
|
||||
*/
|
||||
class XmpParser {
|
||||
friend void XmpProperties::registerNs(const std::string&, const std::string&);
|
||||
friend void XmpProperties::unregisterNs(const std::string&);
|
||||
public:
|
||||
/*!
|
||||
@brief Decode XMP metadata from an XMP packet \em xmpPacket into
|
||||
@ -299,7 +300,7 @@ namespace Exiv2 {
|
||||
*/
|
||||
static bool initialize();
|
||||
/*!
|
||||
@brief Terminate the XMP Toolkit.
|
||||
@brief Terminate the XMP Toolkit and unregister custom namespaces.
|
||||
|
||||
Call this method when the XmpParser is no longer needed to
|
||||
allow the XMP Toolkit to cleanly shutdown.
|
||||
@ -316,6 +317,12 @@ namespace Exiv2 {
|
||||
*/
|
||||
static bool registerNs(const std::string& ns,
|
||||
const std::string& prefix);
|
||||
/*!
|
||||
@brief Delete a namespace from the XMP Toolkit.
|
||||
|
||||
XmpProperties::unregisterNs calls this to synchronize namespaces.
|
||||
*/
|
||||
static void unregisterNs(const std::string& ns);
|
||||
|
||||
// DATA
|
||||
static bool initialized_; //! Indicates if the XMP Toolkit has been initialized
|
||||
|
||||
@ -54,6 +54,9 @@ set Xmp.tiff.ImageDescription lang=de-DE TIFF Bildbeschreibung
|
||||
# Register a namespace which Exiv2 doesn't know yet with a prefix.
|
||||
reg ns myNamespace/
|
||||
|
||||
# Add a property in the new custom namespace.
|
||||
set Xmp.ns.myProperty myValue
|
||||
|
||||
# There are no built-in Exiv2 value types for structures, qualifiers and
|
||||
# nested types. However, these can be added by using an XmpText value and a
|
||||
# path as the key.
|
||||
@ -65,7 +68,7 @@ set Xmp.xmpDM.videoFrameSize/stDim:unit inch
|
||||
|
||||
# Add an element with a qualifier (using the namespace registered earlier)
|
||||
set Xmp.dc.publisher James Bond
|
||||
set Xmp.dc.publisher/?ns:role secret agent
|
||||
set Xmp.dc.publisher[1]/?ns:role secret agent
|
||||
|
||||
# Add a qualifer to an array element of Xmp.dc.creator (added above)
|
||||
set Xmp.dc.creator[2]/?ns:role programmer
|
||||
|
||||
@ -293,6 +293,7 @@ Xmp.dc.format XmpText 10 image/jpeg
|
||||
Xmp.dc.creator XmpSeq 3 1) The first creator, 2) The second creator, 3) And another one
|
||||
Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt
|
||||
Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung
|
||||
Xmp.ns.myProperty XmpText 7 myValue
|
||||
Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16
|
||||
Xmp.xmpDM.videoFrameSize/stDim:h XmpText 1 9
|
||||
Xmp.xmpDM.videoFrameSize/stDim:unit XmpText 4 inch
|
||||
@ -323,7 +324,8 @@ Xmp.xmpBJ.JobRef[2]/stJob:role XmpText 8 Best man
|
||||
dc:five="256"
|
||||
dc:six="false"
|
||||
dc:seven="Seven"
|
||||
dc:format="image/jpeg">
|
||||
dc:format="image/jpeg"
|
||||
ns:myProperty="myValue">
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>Palmtree</rdf:li>
|
||||
@ -427,11 +429,12 @@ Set Xmp.dc.description "Hello, World" (LangAlt)
|
||||
Set Xmp.tiff.ImageDescription "TIFF image description" (LangAlt)
|
||||
Set Xmp.tiff.ImageDescription "lang=de-DE TIFF Bildbeschreibung" (LangAlt)
|
||||
Reg ns="myNamespace/"
|
||||
Set Xmp.ns.myProperty "myValue" (XmpText)
|
||||
Set Xmp.xmpDM.videoFrameSize/stDim:w "16" (XmpText)
|
||||
Set Xmp.xmpDM.videoFrameSize/stDim:h "9" (XmpText)
|
||||
Set Xmp.xmpDM.videoFrameSize/stDim:unit "inch" (XmpText)
|
||||
Set Xmp.dc.publisher "James Bond" (XmpBag)
|
||||
Set Xmp.dc.publisher/?ns:role "secret agent" (XmpText)
|
||||
Set Xmp.dc.publisher[1]/?ns:role "secret agent" (XmpText)
|
||||
Set Xmp.dc.creator[2]/?ns:role "programmer" (XmpText)
|
||||
Set Xmp.xmpBJ.JobRef "type=Bag" (XmpText)
|
||||
Set Xmp.xmpBJ.JobRef[1]/stJob:name "Birthday party" (XmpText)
|
||||
@ -457,8 +460,9 @@ Xmp.dc.creator[2]/?ns:role XmpText 10 programmer
|
||||
Xmp.dc.creator[3] XmpText 18 3) And another one
|
||||
Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt
|
||||
Xmp.dc.publisher XmpText 0 type="Bag"
|
||||
Xmp.dc.publisher/?ns:role XmpText 12 secret agent
|
||||
Xmp.dc.publisher[1] XmpText 10 James Bond
|
||||
Xmp.dc.publisher[1]/?ns:role XmpText 12 secret agent
|
||||
Xmp.ns.myProperty XmpText 7 myValue
|
||||
Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung
|
||||
Xmp.xmpDM.videoFrameSize XmpText 0 type="Struct"
|
||||
Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16
|
||||
|
||||
Loading…
Reference in New Issue
Block a user