Blame samples/exiv2json.cpp

Packit 01d647
// ***************************************************************** -*- C++ -*-
Packit 01d647
// exiv2json.cpp
Packit 01d647
// Sample program to print metadata in JSON format
Packit 01d647

Packit 01d647
#include <exiv2/exiv2.hpp>
Packit 01d647
#include "Jzon.h"
Packit 01d647

Packit 01d647
#include <iostream>
Packit 01d647
#include <iomanip>
Packit 01d647
#include <cassert>
Packit 01d647
#include <string>
Packit 01d647
#include <map>
Packit 01d647
#include <vector>
Packit 01d647
#include <set>
Packit 01d647

Packit 01d647
#include <cstdlib>
Packit 01d647
#include <limits.h>
Packit 01d647
#include <sys/types.h>
Packit 01d647
#include <sys/stat.h>
Packit 01d647

Packit 01d647
#if defined(__MINGW32__) || defined(__MINGW64__)
Packit 01d647
# ifndef  __MINGW__
Packit 01d647
#  define __MINGW__
Packit 01d647
# endif
Packit 01d647
#endif
Packit 01d647

Packit 01d647
#if defined(_MSC_VER) || defined(__MINGW__)
Packit 01d647
#include <windows.h>
Packit 01d647
#ifndef  PATH_MAX
Packit 01d647
# define PATH_MAX 512
Packit 01d647
#endif
Packit 01d647
const char* realpath(const char* file,char* path)
Packit 01d647
{
Packit 01d647
    GetFullPathName(file,PATH_MAX,path,NULL);
Packit 01d647
    return path;
Packit 01d647
}
Packit 01d647
#else
Packit 01d647
#include <unistd.h>
Packit 01d647
#endif
Packit 01d647

Packit 01d647
struct Token {
Packit 01d647
    std::string n; // the name eg "History"
Packit 01d647
    bool        a; // name is an array eg History[]
Packit 01d647
    int         i; // index (indexed from 1) eg History[1]/stEvt:action
Packit 01d647
};
Packit 01d647
typedef std::vector<Token>    Tokens;
Packit 01d647

Packit 01d647
// "XMP.xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle"
Packit 01d647
bool getToken(std::string& in,Token& token,Exiv2::StringSet* pNS=NULL)
Packit 01d647
{
Packit 01d647
    bool result = false;
Packit 01d647
    bool ns     = false;
Packit 01d647

Packit 01d647
    token.n = ""    ;
Packit 01d647
    token.a = false ;
Packit 01d647
    token.i = 0     ;
Packit 01d647

Packit 01d647
    while ( !result && in.length() ) {
Packit 01d647
        std::string c = in.substr(0,1);
Packit 01d647
        char        C = c[0];
Packit 01d647
        in            = in.substr(1,std::string::npos);
Packit 01d647
        if ( in.length() == 0 && C != ']' ) token.n += c;
Packit 01d647
        if ( C == '/' || C == '[' || C == ':' || C == '.' || C == ']' || in.length() == 0 ) {
Packit 01d647
            ns        |= C == '/' ;
Packit 01d647
            token.a    = C == '[' ;
Packit 01d647
            if (         C == ']' ) token.i = std::atoi(token.n.c_str()); // encoded string first index == 1
Packit 01d647
            result     = token.n.length() > 0 ;
Packit 01d647
        }  else {
Packit 01d647
            token.n   += c;
Packit 01d647
        }
Packit 01d647
    }
Packit 01d647
    if (ns && pNS) pNS->insert(token.n);
Packit 01d647

Packit 01d647
    return result;
Packit 01d647
}
Packit 01d647

Packit 01d647
Jzon::Node& addToTree(Jzon::Node& r1,Token token)
Packit 01d647
{
Packit 01d647
    Jzon::Object object ;
Packit 01d647
    Jzon::Array  array  ;
Packit 01d647

Packit 01d647
    std::string  key    = token.n  ;
Packit 01d647
    size_t       index  = token.i-1; // array Eg: "History[1]" indexed from 1.  Jzon expects 0 based index.
Packit 01d647
    Jzon::Node&  empty  = token.a ? (Jzon::Node&) array : (Jzon::Node&) object ;
Packit 01d647

Packit 01d647
    if (  r1.IsObject() ) {
Packit 01d647
        Jzon::Object& o1 = r1.AsObject();
Packit 01d647
        if (   !o1.Has(key) ) o1.Add(key,empty);
Packit 01d647
        return  o1.Get(key);
Packit 01d647
    } else if ( r1.IsArray() ) {
Packit 01d647
        Jzon::Array& a1 = r1.AsArray();
Packit 01d647
        while ( a1.GetCount() <= index ) a1.Add(empty);
Packit 01d647
        return  a1.Get(index);
Packit 01d647
    }
Packit 01d647
    return r1;
Packit 01d647
}
Packit 01d647

Packit 01d647
Jzon::Node& recursivelyBuildTree(Jzon::Node& root,Tokens& tokens,size_t k)
Packit 01d647
{
Packit 01d647
    return addToTree( k==0 ? root : recursivelyBuildTree(root,tokens,k-1), tokens[k] );
Packit 01d647
}
Packit 01d647

Packit 01d647
// build the json tree for this key.  return location and discover the name
Packit 01d647
Jzon::Node& objectForKey(const std::string& Key,Jzon::Object& root,std::string& name,Exiv2::StringSet* pNS=NULL)
Packit 01d647
{
Packit 01d647
    // Parse the key
Packit 01d647
    Tokens      tokens ;
Packit 01d647
    Token       token  ;
Packit 01d647
    std::string input  = Key ; // Example: "XMP.xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle"
Packit 01d647
    while ( getToken(input,token,pNS) ) tokens.push_back(token);
Packit 01d647
    size_t      l      = tokens.size()-1; // leave leaf name to push()
Packit 01d647
    name               = tokens[l].n ;
Packit 01d647

Packit 01d647
    // The second token.  For example: XMP.dc is a namespace
Packit 01d647
    if ( pNS && tokens.size() > 1 ) pNS->insert(tokens[1].n);
Packit 01d647
    return recursivelyBuildTree(root,tokens,l-1);
Packit 01d647

Packit 01d647
#if 0
Packit 01d647
    // recursivelyBuildTree:
Packit 01d647
    // Go to the root.  Climb out adding objects or arrays to create the tree
Packit 01d647
    // The leaf is pushed on the top by the caller of objectForKey()
Packit 01d647
    // The recursion could be expressed by these if statements:
Packit 01d647
    if ( l == 1 ) return                               addToTree(root,tokens[0]);
Packit 01d647
    if ( l == 2 ) return                     addToTree(addToTree(root,tokens[0]),tokens[1]);
Packit 01d647
    if ( l == 3 ) return           addToTree(addToTree(addToTree(root,tokens[0]),tokens[1]),tokens[2]);
Packit 01d647
    if ( l == 4 ) return addToTree(addToTree(addToTree(addToTree(root,tokens[0]),tokens[1]),tokens[2]),tokens[3]);
Packit 01d647
    ...
Packit 01d647
#endif
Packit 01d647
}
Packit 01d647

Packit 01d647
bool isObject(std::string& value)
Packit 01d647
{
Packit 01d647
    return !value.compare(std::string("type=\"Struct\""));
Packit 01d647
}
Packit 01d647

Packit 01d647
bool isArray(std::string& value)
Packit 01d647
{
Packit 01d647
    return !value.compare(std::string("type=\"Seq\""))
Packit 01d647
    ||     !value.compare(std::string("type=\"Bag\""))
Packit 01d647
    ||     !value.compare(std::string("type=\"Alt\""))
Packit 01d647
    ;
Packit 01d647
}
Packit 01d647

Packit 01d647
#define STORE(node,key,value) \
Packit 01d647
    if  (node.IsObject()) node.AsObject().Add(key,value);\
Packit 01d647
    else                  node.AsArray() .Add(    value)
Packit 01d647

Packit 01d647
template <class T>
Packit 01d647
void push(Jzon::Node& node,const std::string& key,T i)
Packit 01d647
{
Packit 01d647
#define ABORT_IF_I_EMTPY          \
Packit 01d647
    if (i->value().size() == 0) { \
Packit 01d647
        return;                   \
Packit 01d647
    }
Packit 01d647

Packit 01d647
    std::string value = i->value().toString();
Packit 01d647

Packit 01d647
    switch ( i->typeId() ) {
Packit 01d647
        case Exiv2::xmpText:
Packit 01d647
             if (        ::isObject(value) ) {
Packit 01d647
                 Jzon::Object   v;
Packit 01d647
                 STORE(node,key,v);
Packit 01d647
             } else if ( ::isArray(value) ) {
Packit 01d647
                 Jzon::Array    v;
Packit 01d647
                 STORE(node,key,v);
Packit 01d647
             } else {
Packit 01d647
                 STORE(node,key,value);
Packit 01d647
             }
Packit 01d647
        break;
Packit 01d647

Packit 01d647
        case Exiv2::unsignedByte:
Packit 01d647
        case Exiv2::unsignedShort:
Packit 01d647
        case Exiv2::unsignedLong:
Packit 01d647
        case Exiv2::signedByte:
Packit 01d647
        case Exiv2::signedShort:
Packit 01d647
        case Exiv2::signedLong:
Packit 01d647
             STORE(node,key,std::atoi(value.c_str()) );
Packit 01d647
        break;
Packit 01d647

Packit 01d647
        case Exiv2::tiffFloat:
Packit 01d647
        case Exiv2::tiffDouble:
Packit 01d647
             STORE(node,key,std::atof(value.c_str()) );
Packit 01d647
        break;
Packit 01d647

Packit 01d647
        case Exiv2::unsignedRational:
Packit 01d647
        case Exiv2::signedRational: {
Packit 01d647
             ABORT_IF_I_EMTPY
Packit 01d647
             Jzon::Array     arr;
Packit 01d647
             Exiv2::Rational rat = i->value().toRational();
Packit 01d647
             arr.Add(rat.first );
Packit 01d647
             arr.Add(rat.second);
Packit 01d647
             STORE(node,key,arr);
Packit 01d647
        } break;
Packit 01d647

Packit 01d647
        case Exiv2::langAlt: {
Packit 01d647
             ABORT_IF_I_EMTPY
Packit 01d647
             Jzon::Object l ;
Packit 01d647
             const Exiv2::LangAltValue& langs = dynamic_cast<const Exiv2::LangAltValue&>(i->value());
Packit 01d647
             for ( Exiv2::LangAltValue::ValueType::const_iterator lang = langs.value_.begin()
Packit 01d647
                 ; lang != langs.value_.end()
Packit 01d647
                 ; lang++
Packit 01d647
             ) {
Packit 01d647
                l.Add(lang->first,lang->second);
Packit 01d647
             }
Packit 01d647
             Jzon::Object o ;
Packit 01d647
             o.Add("lang",l);
Packit 01d647
             STORE(node,key,o);
Packit 01d647
        }
Packit 01d647
        break;
Packit 01d647

Packit 01d647
        default:
Packit 01d647
        case Exiv2::date:
Packit 01d647
        case Exiv2::time:
Packit 01d647
        case Exiv2::asciiString :
Packit 01d647
        case Exiv2::string:
Packit 01d647
        case Exiv2::comment:
Packit 01d647
        case Exiv2::undefined:
Packit 01d647
        case Exiv2::tiffIfd:
Packit 01d647
        case Exiv2::directory:
Packit 01d647
        case Exiv2::xmpAlt:
Packit 01d647
        case Exiv2::xmpBag:
Packit 01d647
        case Exiv2::xmpSeq:
Packit 01d647
             // http://dev.exiv2.org/boards/3/topics/1367#message-1373
Packit 01d647
             if ( key == "UserComment" ) {
Packit 01d647
                size_t pos  = value.find('\0') ;
Packit 01d647
                if (   pos != std::string::npos )
Packit 01d647
                    value = value.substr(0,pos);
Packit 01d647
             }
Packit 01d647
             if ( key == "MakerNote") return;
Packit 01d647
             STORE(node,key,value);
Packit 01d647
        break;
Packit 01d647
    }
Packit 01d647
}
Packit 01d647

Packit 01d647
void fileSystemPush(const char* path,Jzon::Node& nfs)
Packit 01d647
{
Packit 01d647
    Jzon::Object& fs = (Jzon::Object&) nfs;
Packit 01d647
    fs.Add("path",path);
Packit 01d647
    char resolved_path[2000]; // PATH_MAX];
Packit 01d647
    fs.Add("realpath",realpath(path,resolved_path));
Packit 01d647

Packit 01d647
    struct stat buf;
Packit 01d647
    memset(&buf,0,sizeof(buf));
Packit 01d647
    stat(path,&buf;;
Packit 01d647

Packit 01d647
    fs.Add("st_dev"    ,(int) buf.st_dev    ); /* ID of device containing file    */
Packit 01d647
    fs.Add("st_ino"    ,(int) buf.st_ino    ); /* inode number                    */
Packit 01d647
    fs.Add("st_mode"   ,(int) buf.st_mode   ); /* protection                      */
Packit 01d647
    fs.Add("st_nlink"  ,(int) buf.st_nlink  ); /* number of hard links            */
Packit 01d647
    fs.Add("st_uid"    ,(int) buf.st_uid    ); /* user ID of owner                */
Packit 01d647
    fs.Add("st_gid"    ,(int) buf.st_gid    ); /* group ID of owner               */
Packit 01d647
    fs.Add("st_rdev"   ,(int) buf.st_rdev   ); /* device ID (if special file)     */
Packit 01d647
    fs.Add("st_size"   ,(int) buf.st_size   ); /* total size, in bytes            */
Packit 01d647
    fs.Add("st_atime"  ,(int) buf.st_atime  ); /* time of last access             */
Packit 01d647
    fs.Add("st_mtime"  ,(int) buf.st_mtime  ); /* time of last modification       */
Packit 01d647
    fs.Add("st_ctime"  ,(int) buf.st_ctime  ); /* time of last status change      */
Packit 01d647

Packit 01d647
#if defined(_MSC_VER) || defined(__MINGW__)
Packit 01d647
    size_t blksize     = 1024;
Packit 01d647
    size_t blocks      = (buf.st_size+blksize-1)/blksize;
Packit 01d647
#else
Packit 01d647
    size_t blksize     = buf.st_blksize;
Packit 01d647
    size_t blocks      = buf.st_blocks ;
Packit 01d647
#endif
Packit 01d647
    fs.Add("st_blksize",(int) blksize       ); /* blocksize for file system I/O   */
Packit 01d647
    fs.Add("st_blocks" ,(int) blocks        ); /* number of 512B blocks allocated */
Packit 01d647
}
Packit 01d647

Packit 01d647
int main(int argc, char* const argv[])
Packit 01d647
{
Packit 01d647
    try {
Packit 01d647
        if (argc < 2 || argc > 3) {
Packit 01d647
            std::cout << "Usage: " << argv[0] << " [-option] file"       << std::endl;
Packit 01d647
            std::cout << "Option: all | exif | iptc | xmp | filesystem"  << std::endl;
Packit 01d647
            return 1;
Packit 01d647
        }
Packit 01d647
        const char* path   = argv[argc-1];
Packit 01d647
        const char* opt    = argc == 3 ? argv[1] : "-all" ;
Packit 01d647
        while      (opt[0] == '-') opt++ ; // skip past leading -'s
Packit 01d647
        char        option = opt[0];
Packit 01d647

Packit 01d647
        Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
Packit 01d647
        assert(image.get() != 0);
Packit 01d647
        image->readMetadata();
Packit 01d647

Packit 01d647
        Jzon::Object   root;
Packit 01d647

Packit 01d647
        if ( option == 'f' ) { // only report filesystem when requested
Packit 01d647
            const char*    FS="FS";
Packit 01d647
            Jzon::Object      fs  ;
Packit 01d647
            root.Add(FS,fs) ;
Packit 01d647
            fileSystemPush(path,root.Get(FS));
Packit 01d647
        }
Packit 01d647

Packit 01d647
        if ( option == 'a' || option == 'e' ) {
Packit 01d647
            Exiv2::ExifData &exifData = image->exifData();
Packit 01d647
            for ( Exiv2::ExifData::const_iterator i = exifData.begin(); i != exifData.end() ; ++i ) {
Packit 01d647
                std::string name   ;
Packit 01d647
                Jzon::Node& object = objectForKey(i->key(),root,name);
Packit 01d647
                push(object,name,i);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647

Packit 01d647
        if ( option == 'a' || option == 'i' ) {
Packit 01d647
            Exiv2::IptcData &iptcData = image->iptcData();
Packit 01d647
            for (Exiv2::IptcData::const_iterator i = iptcData.begin(); i != iptcData.end(); ++i) {
Packit 01d647
                std::string name   ;
Packit 01d647
                Jzon::Node& object = objectForKey(i->key(),root,name);
Packit 01d647
                push(object,name,i);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647

Packit 01d647
    #ifdef EXV_HAVE_XMP_TOOLKIT
Packit 01d647
        if ( option == 'a' || option == 'x' ) {
Packit 01d647

Packit 01d647
            Exiv2::XmpData  &xmpData  = image->xmpData();
Packit 01d647
            if ( !xmpData.empty() ) {
Packit 01d647
                // get the xmpData and recursively parse into a Jzon Object
Packit 01d647
                Exiv2::StringSet     namespaces;
Packit 01d647
                for (Exiv2::XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
Packit 01d647
                    std::string name   ;
Packit 01d647
                    Jzon::Node& object = objectForKey(i->key(),root,name,&namespaces);
Packit 01d647
                    push(object,name,i);
Packit 01d647
                }
Packit 01d647

Packit 01d647
                // get the namespace dictionary from XMP
Packit 01d647
                Exiv2::Dictionary                          nsDict;
Packit 01d647
                Exiv2::XmpProperties::registeredNamespaces(nsDict);
Packit 01d647

Packit 01d647
                // create and populate a Jzon::Object for the namespaces
Packit 01d647
                Jzon::Object    xmlns;
Packit 01d647
                for ( Exiv2::StringSet_i it = namespaces.begin() ; it != namespaces.end() ; it++ ) {
Packit 01d647
                    std::string ns  = *it       ;
Packit 01d647
                    std::string uri = nsDict[ns];
Packit 01d647
                    xmlns.Add(ns,uri);
Packit 01d647
                }
Packit 01d647

Packit 01d647
                // add xmlns as Xmp.xmlns
Packit 01d647
                root.Get("Xmp").AsObject().Add("xmlns",xmlns);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
    #endif
Packit 01d647

Packit 01d647
        Jzon::Writer writer(root, Jzon::StandardFormat);
Packit 01d647
        writer.Write();
Packit 01d647
        std::cout << writer.GetResult() << std::endl;
Packit 01d647
        return 0;
Packit 01d647
    }
Packit 01d647

Packit 01d647
    catch (Exiv2::Error& e) {
Packit 01d647
        std::cout << "Caught Exiv2 exception '" << e.what() << "'\n";
Packit 01d647
        return -1;
Packit 01d647
    }
Packit 01d647
}