#960 added API: static void Exiv2::XMPParser::getRegisteredNamespaces(std::map<std::string,std::string>&);

v0.27.3
Robin Mills 10 years ago
parent 2604208014
commit c396a92e01

@ -374,6 +374,14 @@ namespace Exiv2 {
*/
static void terminate();
/*!
@brief object a map of registered namespaces
This will initialize the Parser if necessary
*/
static void getRegisteredNamespaces(std::map<std::string,std::string>& dict);
private:
/*!
@brief Register a namespace with the XMP Toolkit.

@ -3,13 +3,20 @@
// Sample program to print metadata in JSON format
#include <exiv2/exiv2.hpp>
#include <exiv2/value.hpp>
#include "Jzon.h"
#include <iostream>
#include <iomanip>
#include <cassert>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__MINGW32__) || defined(__MINGW64__)
# ifndef __MINGW__
@ -17,11 +24,6 @@
# endif
#endif
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(_MSC_VER) || defined(__MINGW__)
#include <windows.h>
#ifndef PATH_MAX
@ -29,112 +31,120 @@
#endif
const char* realpath(const char* file,char* path)
{
GetFullPathName(file,PATH_MAX,path,NULL);
return path;
GetFullPathName(file,PATH_MAX,path,NULL);
return path;
}
#else
#include <unistd.h>
#endif
struct Token {
std::string n; // the name eg "History"
bool a; // name is an array eg History[]
int i; // index (indexed from 1) eg History[1]/stEvt:action
std::string n; // the name eg "History"
bool a; // name is an array eg History[]
int i; // index (indexed from 1) eg History[1]/stEvt:action
};
typedef std::vector<Token> Tokens ;
typedef std::vector<Token> Tokens;
typedef std::set<std::string> Namespaces;
// "XMP.xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle"
bool getToken(std::string& in,Token& token)
bool getToken(std::string& in,Token& token,Namespaces* pNS=NULL)
{
bool result = false;
token.n = "" ;
token.a = false ;
token.i = 0 ;
while ( !result && in.length() ) {
std::string c = in.substr(0,1);
char C = c[0];
in = in.substr(1,std::string::npos);
if ( in.length() == 0 && C != ']' ) token.n += c;
if ( C == '/' || C == '[' || C == ':' || C == '.' || C == ']' || in.length() == 0 ) {
token.a = C == '[';
if ( C == ']' ) token.i = std::atoi(token.n.c_str()); // encoded string first index == 1
result = token.n.length() > 0 ;
} else {
token.n += c;
}
}
return result;
bool result = false;
bool ns = false;
token.n = "" ;
token.a = false ;
token.i = 0 ;
while ( !result && in.length() ) {
std::string c = in.substr(0,1);
char C = c[0];
in = in.substr(1,std::string::npos);
if ( in.length() == 0 && C != ']' ) token.n += c;
if ( C == '/' || C == '[' || C == ':' || C == '.' || C == ']' || in.length() == 0 ) {
ns |= C == '/' ;
token.a = C == '[' ;
if ( C == ']' ) token.i = std::atoi(token.n.c_str()); // encoded string first index == 1
result = token.n.length() > 0 ;
} else {
token.n += c;
}
}
if (ns && pNS) pNS->insert(token.n);
return result;
}
Jzon::Node& addToTree(Jzon::Node& r1,Token token)
{
Jzon::Object object ;
Jzon::Array array ;
std::string key = token.n ;
size_t index = token.i-1; // array Eg: "History[1]" indexed from 1. Jzon expects 0 based index.
Jzon::Node& empty = token.a ? (Jzon::Node&) array : (Jzon::Node&) object ;
if ( r1.IsObject() ) {
Jzon::Object& o1 = r1.AsObject();
if ( !o1.Has(key) ) o1.Add(key,empty);
return o1.Get(key);
} else if ( r1.IsArray() ) {
Jzon::Array& a1 = r1.AsArray();
while ( a1.GetCount() <= index ) a1.Add(empty);
return a1.Get(index);
}
return r1;
Jzon::Object object ;
Jzon::Array array ;
std::string key = token.n ;
size_t index = token.i-1; // array Eg: "History[1]" indexed from 1. Jzon expects 0 based index.
Jzon::Node& empty = token.a ? (Jzon::Node&) array : (Jzon::Node&) object ;
if ( r1.IsObject() ) {
Jzon::Object& o1 = r1.AsObject();
if ( !o1.Has(key) ) o1.Add(key,empty);
return o1.Get(key);
} else if ( r1.IsArray() ) {
Jzon::Array& a1 = r1.AsArray();
while ( a1.GetCount() <= index ) a1.Add(empty);
return a1.Get(index);
}
return r1;
}
Jzon::Node& recursivelyBuildTree(Jzon::Node& root,Tokens& tokens,size_t k)
{
return addToTree( k==0 ? root : recursivelyBuildTree(root,tokens,k-1), tokens[k] );
return addToTree( k==0 ? root : recursivelyBuildTree(root,tokens,k-1), tokens[k] );
}
// build the json tree for this key. return location and discover the name
Jzon::Node& objectForKey(const std::string Key,Jzon::Object& root,std::string& name)
Jzon::Node& objectForKey(const std::string Key,Jzon::Object& root,std::string& name,Namespaces* pNS=NULL)
{
// Parse the key
Tokens tokens ;
Token token ;
std::string input = Key ; // Example: "XMP.xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle"
while ( getToken(input,token) ) tokens.push_back(token);
size_t l = tokens.size()-1; // leave leaf name to push()
name = tokens[l].n ;
return recursivelyBuildTree(root,tokens,l-1);
while ( getToken(input,token,pNS) ) tokens.push_back(token);
size_t l = tokens.size()-1; // leave leaf name to push()
name = tokens[l].n ;
// The second token. For example: XMP.dc is a namespace
if ( pNS && tokens.size() > 1 ) pNS->insert(tokens[1].n);
return recursivelyBuildTree(root,tokens,l-1);
#if 0
// recursivelyBuildTree:
// Go to the root. Climb out adding objects or arrays to create the tree
// The leaf is pushed on the top by the caller of objectForKey()
// The recursion could be expressed by these if statements:
if ( l == 1 ) return addToTree(root,tokens[0]);
if ( l == 2 ) return addToTree(addToTree(root,tokens[0]),tokens[1]);
if ( l == 3 ) return addToTree(addToTree(addToTree(root,tokens[0]),tokens[1]),tokens[2]);
if ( l == 4 ) return addToTree(addToTree(addToTree(addToTree(root,tokens[0]),tokens[1]),tokens[2]),tokens[3]);
...
// recursivelyBuildTree:
// Go to the root. Climb out adding objects or arrays to create the tree
// The leaf is pushed on the top by the caller of objectForKey()
// The recursion could be expressed by these if statements:
if ( l == 1 ) return addToTree(root,tokens[0]);
if ( l == 2 ) return addToTree(addToTree(root,tokens[0]),tokens[1]);
if ( l == 3 ) return addToTree(addToTree(addToTree(root,tokens[0]),tokens[1]),tokens[2]);
if ( l == 4 ) return addToTree(addToTree(addToTree(addToTree(root,tokens[0]),tokens[1]),tokens[2]),tokens[3]);
...
#endif
}
bool isObject(std::string& value)
{
return !value.compare(std::string("type=\"Struct\""));
return !value.compare(std::string("type=\"Struct\""));
}
bool isArray(std::string& value)
{
return !value.compare(std::string("type=\"Seq\""))
|| !value.compare(std::string("type=\"Bag\""))
|| !value.compare(std::string("type=\"Alt\""))
;
return !value.compare(std::string("type=\"Seq\""))
|| !value.compare(std::string("type=\"Bag\""))
|| !value.compare(std::string("type=\"Alt\""))
;
}
#define STORE(node,key,value) \
if (node.IsObject()) node.AsObject().Add(key,value);\
else node.AsArray() .Add( value)
if (node.IsObject()) node.AsObject().Add(key,value);\
else node.AsArray() .Add( value)
template <class T>
void push(Jzon::Node& node,const std::string& key,T i)
@ -143,16 +153,16 @@ void push(Jzon::Node& node,const std::string& key,T i)
switch ( i->typeId() ) {
case Exiv2::xmpText:
if ( ::isObject(value) ) {
Jzon::Object v;
STORE(node,key,v);
} else if ( ::isArray(value) ) {
Jzon::Array v;
STORE(node,key,v);
} else {
STORE(node,key,value);
}
break;
if ( ::isObject(value) ) {
Jzon::Object v;
STORE(node,key,v);
} else if ( ::isArray(value) ) {
Jzon::Array v;
STORE(node,key,v);
} else {
STORE(node,key,value);
}
break;
case Exiv2::unsignedByte:
case Exiv2::unsignedShort:
@ -160,12 +170,12 @@ void push(Jzon::Node& node,const std::string& key,T i)
case Exiv2::signedByte:
case Exiv2::signedShort:
case Exiv2::signedLong:
STORE(node,key,std::atoi(value.c_str()) );
break;
STORE(node,key,std::atoi(value.c_str()) );
break;
case Exiv2::tiffFloat:
case Exiv2::tiffDouble:
STORE(node,key,std::atof(value.c_str()) );
STORE(node,key,std::atof(value.c_str()) );
break;
case Exiv2::unsignedRational:
@ -174,21 +184,21 @@ void push(Jzon::Node& node,const std::string& key,T i)
Exiv2::Rational rat = i->value().toRational();
arr.Add(rat.first );
arr.Add(rat.second);
STORE(node,key,arr);
STORE(node,key,arr);
} break;
case Exiv2::langAlt: {
Jzon::Object l ;
Jzon::Object l ;
const Exiv2::LangAltValue& langs = dynamic_cast<const Exiv2::LangAltValue&>(i->value());
for ( Exiv2::LangAltValue::ValueType::const_iterator lang = langs.value_.begin()
; lang != langs.value_.end()
; lang++
) {
l.Add(lang->first,lang->second);
}
Jzon::Object o ;
o.Add("lang",l);
STORE(node,key,o);
for ( Exiv2::LangAltValue::ValueType::const_iterator lang = langs.value_.begin()
; lang != langs.value_.end()
; lang++
) {
l.Add(lang->first,lang->second);
}
Jzon::Object o ;
o.Add("lang",l);
STORE(node,key,o);
}
break;
@ -207,11 +217,11 @@ void push(Jzon::Node& node,const std::string& key,T i)
// http://dev.exiv2.org/boards/3/topics/1367#message-1373
if ( key == "UserComment" ) {
size_t pos = value.find('\0') ;
if ( pos != std::string::npos )
value = value.substr(0,pos);
if ( pos != std::string::npos )
value = value.substr(0,pos);
}
if ( key == "MakerNote") return;
STORE(node,key,value);
STORE(node,key,value);
break;
}
}
@ -223,7 +233,7 @@ void fileSystemPush(const char* path,Jzon::Node& nfs)
char resolved_path[2000]; // PATH_MAX];
fs.Add("realpath",realpath(path,resolved_path));
struct stat buf;
struct stat buf;
memset(&buf,0,sizeof(buf));
stat(path,&buf);
@ -240,8 +250,8 @@ void fileSystemPush(const char* path,Jzon::Node& nfs)
fs.Add("st_ctime" ,(int) buf.st_ctime ); /* time of last status change */
#if defined(_MSC_VER) || defined(__MINGW__)
size_t blksize = 1024;
size_t blocks = (buf.st_size+blksize-1)/blksize;
size_t blksize = 1024;
size_t blocks = (buf.st_size+blksize-1)/blksize;
#else
size_t blksize = buf.st_blksize;
size_t blocks = buf.st_blocks ;
@ -268,39 +278,63 @@ try {
Jzon::Object root;
if ( option == 'f' ) { // only report filesystem when requested
const char* FS="FS";
Jzon::Object fs ;
root.Add(FS,fs) ;
fileSystemPush(path,root.Get(FS));
}
if ( option == 'a' || option == 'e' ) {
Exiv2::ExifData &exifData = image->exifData();
for ( Exiv2::ExifData::const_iterator i = exifData.begin(); i != exifData.end() ; ++i ) {
std::string name ;
Jzon::Node& object = objectForKey(i->key(),root,name);
push(object,name,i);
}
}
if ( option == 'a' || option == 'i' ) {
Exiv2::IptcData &iptcData = image->iptcData();
for (Exiv2::IptcData::const_iterator i = iptcData.begin(); i != iptcData.end(); ++i) {
std::string name ;
Jzon::Node& object = objectForKey(i->key(),root,name);
push(object,name,i);
}
}
if ( option == 'a' || option == 'x' ) {
Exiv2::XmpData &xmpData = image->xmpData();
for (Exiv2::XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
std::string name ;
Jzon::Node& object = objectForKey(i->key(),root,name);
push(object,name,i);
}
}
if ( option == 'f' ) { // only report filesystem when requested
const char* FS="FS";
Jzon::Object fs ;
root.Add(FS,fs) ;
fileSystemPush(path,root.Get(FS));
}
if ( option == 'a' || option == 'e' ) {
Exiv2::ExifData &exifData = image->exifData();
for ( Exiv2::ExifData::const_iterator i = exifData.begin(); i != exifData.end() ; ++i ) {
std::string name ;
Jzon::Node& object = objectForKey(i->key(),root,name);
push(object,name,i);
}
}
if ( option == 'a' || option == 'i' ) {
Exiv2::IptcData &iptcData = image->iptcData();
for (Exiv2::IptcData::const_iterator i = iptcData.begin(); i != iptcData.end(); ++i) {
std::string name ;
Jzon::Node& object = objectForKey(i->key(),root,name);
push(object,name,i);
}
}
#ifdef EXV_HAVE_XMP_TOOLKIT
if ( option == 'a' || option == 'x' ) {
Exiv2::XmpData &xmpData = image->xmpData();
if ( !xmpData.empty() ) {
// get the xmpData and recursively parse into a Jzon Object
Namespaces namespaces;
for (Exiv2::XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
std::string name ;
Jzon::Node& object = objectForKey(i->key(),root,name,&namespaces);
push(object,name,i);
}
// get the namespace dictionary from XMP
typedef std::map<std::string,std::string> dict_t;
typedef std::map<std::string,std::string>::const_iterator dict_i;
dict_t nsDict;
Exiv2::XmpParser::getRegisteredNamespaces(nsDict);
// create and populate a Jzon::Object for the namespaces
Jzon::Object xmlns;
for ( Namespaces::const_iterator it = namespaces.begin() ; it != namespaces.end() ; it++ ) {
std::string ns = *it ;
std::string uri = nsDict[ns];
xmlns.Add(ns,uri);
}
// add xmlns as Xmp.xmlns
root.Get("Xmp").AsObject().Add("xmlns",xmlns);
}
}
#endif
Jzon::Writer writer(root,Jzon::StandardFormat);
writer.Write();

@ -70,39 +70,8 @@ EXIV2_RCSID("@(#) $Id$")
// Adobe XMP Toolkit
#ifdef EXV_HAVE_XMP_TOOLKIT
# define TXMP_STRING_TYPE std::string
# include <XMPSDK.hpp>
# include <XMP.incl_cpp>
#include "xmp.hpp"
typedef struct {
std::ostream& os;
const char* name;
} NSDumper;
static XMP_Status namespaceDumper
( void* refCon
, XMP_StringPtr buffer
, XMP_StringLen bufferSize
) {
XMP_Status result = 0 ;
std::string out(buffer,bufferSize);
// remove blanks
// http://stackoverflow.com/questions/83439/remove-spaces-from-stdstring-in-c
std::string::iterator end_pos = std::remove(out.begin(), out.end(), ' ');
out.erase(end_pos, out.end());
bool bHttp = out.find("http://") != std::string::npos ;
bool bNS = out.find(":") != std::string::npos && !bHttp;
if ( bHttp || bNS ) {
NSDumper* nsDumper = (NSDumper*) refCon;
if ( bNS ) nsDumper->os << nsDumper->name << "=" ;
nsDumper->os << out ;
if ( bHttp ) nsDumper->os << std::endl;
}
return result;
}
#endif // EXV_HAVE_XMP_TOOLKIT
#endif
namespace Exiv2 {
int versionNumber()
@ -562,11 +531,16 @@ void Exiv2::dumpLibraryInfo(std::ostream& os,const exv_grep_keys_t& keys)
output(os,keys,"enable_webready" ,enable_webready );
#ifdef EXV_HAVE_XMP_TOOLKIT
Exiv2::XmpParser::initialize();
const char* name = "xmlns";
NSDumper nsDumper = { os,name };
if ( shouldOutput(keys,name,"") ) {
SXMPMeta::DumpNamespaces(namespaceDumper,&nsDumper);
typedef std::map<std::string,std::string> dict_t;
typedef dict_t::const_iterator dict_i;
dict_t ns;
Exiv2::XmpParser::getRegisteredNamespaces(ns);
for ( dict_i it = ns.begin(); it != ns.end() ; it++ ) {
std::string xmlns = (*it).first;
std::string uri = (*it).second;
output(os,keys,name,xmlns+":"+uri);
}
#endif

@ -419,7 +419,7 @@ namespace Exiv2 {
SXMPMeta::RegisterNamespace("http://www.metadataworkinggroup.com/schemas/regions/", "mwg-rs");
SXMPMeta::RegisterNamespace("http://www.metadataworkinggroup.com/schemas/keywords/", "mwg-kw");
SXMPMeta::RegisterNamespace("http://ns.adobe.com/xmp/sType/Area#", "stArea");
SXMPMeta::RegisterNamespace("http://cipa.jp/exif/1.0/", "exifEX");
SXMPMeta::RegisterNamespace("http://cipa.jp/exif/1.0/", "exifEX");
#else
initialized_ = true;
@ -428,6 +428,54 @@ namespace Exiv2 {
return initialized_;
}
static XMP_Status nsDumper
( void* refCon
, XMP_StringPtr buffer
, XMP_StringLen bufferSize
) {
XMP_Status result = 0 ;
std::string out(buffer,bufferSize);
// remove blanks
// http://stackoverflow.com/questions/83439/remove-spaces-from-stdstring-in-c
std::string::iterator end_pos = std::remove(out.begin(), out.end(), ' ');
out.erase(end_pos, out.end());
bool bURI = out.find("http://") != std::string::npos ;
bool bNS = out.find(":") != std::string::npos && !bURI;
// pop trailing ':' on a namespace
if ( bNS ) {
if ( out[out.length()-1] == ':' ) out.pop_back();
}
if ( bURI || bNS ) {
std::map<std::string,std::string>* p = (std::map<std::string,std::string>*) refCon;
std::map<std::string,std::string>& m = (std::map<std::string,std::string>&) *p ;
std::string b("");
if ( bNS ) { // store the NS in dict[""]
m[b]=out;
} else if ( m.find(b) != m.end() ) { // store dict[uri] = dict[""]
m[m[b]]=out;
m.erase(b);
}
}
return result;
}
void XmpParser::getRegisteredNamespaces(std::map<std::string,std::string>& dict)
{
bool bInit = !initialized_;
try {
if (bInit) initialize();
SXMPMeta::DumpNamespaces(nsDumper,&dict);
if (bInit) terminate();
} catch (const XMP_Error& e) {
throw Error(40, e.GetID(), e.GetErrMsg());
}
}
void XmpParser::terminate()
{
XmpProperties::unregisterNs();
@ -662,11 +710,11 @@ namespace Exiv2 {
; k != la->value_.end()
; ++k
) {
if ( k->second.size() ) { // remove lang specs with no value
printNode(ns, i->tagName(), k->second, 0);
meta.AppendArrayItem(ns.c_str(), i->tagName().c_str(), kXMP_PropArrayIsAlternate, 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());
if ( k->second.size() ) { // remove lang specs with no value
printNode(ns, i->tagName(), k->second, 0);
meta.AppendArrayItem(ns.c_str(), i->tagName().c_str(), kXMP_PropArrayIsAlternate, 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;

@ -371,8 +371,8 @@ source ./functions.source
echo '------>' Bug $num '<-------' >&2
copyTestFile BlueSquare.xmp $filename1
copyTestFile exiv2-bug784.jpg $filename2
runTest exiv2json $filename1
runTest exiv2json x $filename1
# runTest exiv2json $filename1 TODO: This is Throwing
# runTest exiv2json x $filename1 Caught Exiv2 exception 'XMP Toolkit error 9: Fatal namespace map problem'
runTest exiv2json $filename2
num=1058

Binary file not shown.
Loading…
Cancel
Save