1
|
// ***************************************************************** -*- C++ -*-
|
2
|
// exif2json.cpp, $Id: exif2json.cpp 518 2013-05-10 23:53:00Z robinwmills@gmail.com $
|
3
|
// Sample program to print the Exif metadata in JSON format
|
4
|
|
5
|
#include <exiv2/exiv2.hpp>
|
6
|
#include <Jzon.h>
|
7
|
|
8
|
#include <iostream>
|
9
|
#include <iomanip>
|
10
|
#include <cassert>
|
11
|
#include <string>
|
12
|
|
13
|
#if defined(__MINGW32__) || defined(__MINGW64__)
|
14
|
# ifndef __MINGW__
|
15
|
# define __MINGW__
|
16
|
# endif
|
17
|
#endif
|
18
|
|
19
|
#include <stdlib.h>
|
20
|
#include <limits.h>
|
21
|
#include <sys/types.h>
|
22
|
#include <sys/stat.h>
|
23
|
|
24
|
#if defined(_MSC_VER) || defined(__MINGW__)
|
25
|
#include <windows.h>
|
26
|
#ifndef PATH_MAX
|
27
|
# define PATH_MAX 512
|
28
|
#endif
|
29
|
const char* realpath(const char* file,char* path)
|
30
|
{
|
31
|
GetFullPathName(file,PATH_MAX,path,NULL);
|
32
|
return path;
|
33
|
}
|
34
|
#else
|
35
|
#include <unistd.h>
|
36
|
#endif
|
37
|
|
38
|
using namespace std;
|
39
|
using namespace Jzon;
|
40
|
using namespace Exiv2;
|
41
|
|
42
|
// http://stackoverflow.com/questions/236129/splitting-a-string-in-c
|
43
|
static size_t split(const std::string& s, char delim, std::vector<std::string>& elems)
|
44
|
{
|
45
|
std::stringstream ss(s);
|
46
|
std::string item;
|
47
|
while (std::getline(ss, item, delim)) {
|
48
|
elems.push_back(item);
|
49
|
}
|
50
|
return elems.size();
|
51
|
}
|
52
|
|
53
|
Jzon::Object& objectForKey(std::string Key,std::string& name,Jzon::Object& root)
|
54
|
{
|
55
|
static Jzon::Object object;
|
56
|
|
57
|
std::vector<std::string> keys ;
|
58
|
size_t l = split(Key,'.',keys);
|
59
|
if ( l < 3 || l > 7 ) return object; // maybe we should throw
|
60
|
|
61
|
name = keys[l-1];
|
62
|
size_t k = 0;
|
63
|
Jzon::Object& r1 = root;
|
64
|
|
65
|
// this is horrible References are pointers that don't work properly!
|
66
|
if ( !r1.Has(keys[k]) ) r1.Add(keys[k],object);
|
67
|
Jzon::Object& r2 = (Jzon::Object&) r1.Get(keys[k]);
|
68
|
if ( l == 2 ) return r2;
|
69
|
|
70
|
if ( !r2.Has(keys[++k])) r2.Add(keys[k],object);
|
71
|
Jzon::Object& r3 = (Jzon::Object&) r2.Get(keys[k]);
|
72
|
if ( l == 3 ) return r3;
|
73
|
|
74
|
if ( !r3.Has(keys[++k])) r3.Add(keys[k],object);
|
75
|
Jzon::Object& r4 = (Jzon::Object&) r3.Get(keys[k]);
|
76
|
if ( l == 4 ) return r4;
|
77
|
|
78
|
if ( !r4.Has(keys[++k])) r4.Add(keys[k],object);
|
79
|
Jzon::Object& r5 = (Jzon::Object&) r4.Get(keys[k]);
|
80
|
if ( l == 5 ) return r5;
|
81
|
|
82
|
if ( !r5.Has(keys[++k])) r5.Add(keys[k],object);
|
83
|
Jzon::Object& r6 = (Jzon::Object&) r5.Get(keys[k]);
|
84
|
if ( l == 6 ) return r6;
|
85
|
|
86
|
if ( !r6.Has(keys[++k])) r6.Add(keys[k],object);
|
87
|
Jzon::Object& r7 = (Jzon::Object&) r6.Get(keys[k]);
|
88
|
if ( l == 7 ) return r7;
|
89
|
|
90
|
return object;
|
91
|
}
|
92
|
|
93
|
// ExifData::const_iterator i
|
94
|
template <class T>
|
95
|
void push(Jzon::Object& json,const std::string& key,T i)
|
96
|
{
|
97
|
std::string value = i->value().toString();
|
98
|
|
99
|
switch ( i->typeId() ) {
|
100
|
case Exiv2::xmpText:
|
101
|
case Exiv2::asciiString :
|
102
|
case Exiv2::string:
|
103
|
case Exiv2::comment:
|
104
|
json.Add(key,value);
|
105
|
break;
|
106
|
|
107
|
case Exiv2::unsignedByte:
|
108
|
case Exiv2::unsignedShort:
|
109
|
case Exiv2::unsignedLong:
|
110
|
case Exiv2::signedByte:
|
111
|
case Exiv2::signedShort:
|
112
|
case Exiv2::signedLong:
|
113
|
json.Add(key,(int)i->value().toLong());
|
114
|
break;
|
115
|
|
116
|
case Exiv2::tiffFloat:
|
117
|
case Exiv2::tiffDouble:
|
118
|
json.Add(key,i->value().toFloat());
|
119
|
break;
|
120
|
|
121
|
case Exiv2::unsignedRational:
|
122
|
case Exiv2::signedRational: {
|
123
|
Jzon::Array arr;
|
124
|
Rational rat = i->value().toRational();
|
125
|
arr.Add (rat.first );
|
126
|
arr.Add (rat.second);
|
127
|
json.Add(key,arr);
|
128
|
} break;
|
129
|
|
130
|
case Exiv2::date:
|
131
|
case Exiv2::time:
|
132
|
json.Add(key,i->value().toString());
|
133
|
break;
|
134
|
|
135
|
default:
|
136
|
case Exiv2::undefined:
|
137
|
case Exiv2::tiffIfd:
|
138
|
case Exiv2::directory:
|
139
|
case Exiv2::xmpAlt:
|
140
|
case Exiv2::xmpBag:
|
141
|
case Exiv2::xmpSeq:
|
142
|
case Exiv2::langAlt:
|
143
|
// http://dev.exiv2.org/boards/3/topics/1367#message-1373
|
144
|
if ( key == "UserComment" ) {
|
145
|
size_t pos = value.find('\0') ;
|
146
|
if ( pos != string::npos )
|
147
|
value = value.substr(0,pos);
|
148
|
}
|
149
|
|
150
|
if ( key != "MakerNote") json.Add(key,value);
|
151
|
break;
|
152
|
}
|
153
|
}
|
154
|
|
155
|
void fileSystemPush(const char* path,Jzon::Node& nfs)
|
156
|
{
|
157
|
Jzon::Object& fs = (Jzon::Object&) nfs;
|
158
|
fs.Add("path",path);
|
159
|
char resolved_path[PATH_MAX];
|
160
|
fs.Add("realpath",realpath(path,resolved_path));
|
161
|
|
162
|
struct stat buf;
|
163
|
memset(&buf,0,sizeof(buf));
|
164
|
stat(path,&buf);
|
165
|
|
166
|
fs.Add("st_dev" ,(int) buf.st_dev ); /* ID of device containing file */
|
167
|
fs.Add("st_ino" ,(int) buf.st_ino ); /* inode number */
|
168
|
fs.Add("st_mode" ,(int) buf.st_mode ); /* protection */
|
169
|
fs.Add("st_nlink" ,(int) buf.st_nlink ); /* number of hard links */
|
170
|
fs.Add("st_uid" ,(int) buf.st_uid ); /* user ID of owner */
|
171
|
fs.Add("st_gid" ,(int) buf.st_gid ); /* group ID of owner */
|
172
|
fs.Add("st_rdev" ,(int) buf.st_rdev ); /* device ID (if special file) */
|
173
|
fs.Add("st_size" ,(int) buf.st_size ); /* total size, in bytes */
|
174
|
fs.Add("st_atime" ,(int) buf.st_atime ); /* time of last access */
|
175
|
fs.Add("st_mtime" ,(int) buf.st_mtime ); /* time of last modification */
|
176
|
fs.Add("st_ctime" ,(int) buf.st_ctime ); /* time of last status change */
|
177
|
|
178
|
#if defined(_MSC_VER) || defined(__MINGW__)
|
179
|
size_t blksize = 1024;
|
180
|
size_t blocks = (buf.st_size+blksize-1)/blksize;
|
181
|
#else
|
182
|
size_t blksize = buf.st_blksize;
|
183
|
size_t blocks = buf.st_blocks ;
|
184
|
#endif
|
185
|
fs.Add("st_blksize",(int) blksize ); /* blocksize for file system I/O */
|
186
|
fs.Add("st_blocks" ,(int) blocks ); /* number of 512B blocks allocated */
|
187
|
}
|
188
|
|
189
|
int main(int argc, char* const argv[])
|
190
|
try {
|
191
|
|
192
|
if (argc != 2) {
|
193
|
std::cout << "Usage: " << argv[0] << " file\n";
|
194
|
return 1;
|
195
|
}
|
196
|
|
197
|
const char* path=argv[1];
|
198
|
|
199
|
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
|
200
|
assert(image.get() != 0);
|
201
|
image->readMetadata();
|
202
|
|
203
|
Jzon::Object root;
|
204
|
|
205
|
const char* FS="FS";
|
206
|
Jzon::Object fs ;
|
207
|
root.Add (FS,fs) ;
|
208
|
fileSystemPush(path,root.Get(FS));
|
209
|
|
210
|
Exiv2::ExifData &exifData = image->exifData();
|
211
|
for ( ExifData::const_iterator i = exifData.begin(); i != exifData.end() ; ++i ) {
|
212
|
std::string key ;
|
213
|
push(objectForKey(i->key(),key,root),key,i);
|
214
|
}
|
215
|
|
216
|
Exiv2::IptcData &iptcData = image->iptcData();
|
217
|
for (Exiv2::IptcData::const_iterator i = iptcData.begin(); i != iptcData.end(); ++i) {
|
218
|
std::string key ;
|
219
|
push(objectForKey(i->key(),key,root),key,i);
|
220
|
}
|
221
|
|
222
|
Exiv2::XmpData &xmpData = image->xmpData();
|
223
|
for (Exiv2::XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
|
224
|
std::string key ;
|
225
|
push(objectForKey(i->key(),key,root),key,i);
|
226
|
}
|
227
|
/*
|
228
|
This is only for testing long paths
|
229
|
{
|
230
|
ExifData::const_iterator i = exifData.begin();
|
231
|
std::string key;
|
232
|
push(objectForKey("This.Is.A.Rather.Long.Path.Key",key,root),key,i);
|
233
|
}
|
234
|
*/
|
235
|
Jzon::Writer writer(root,Jzon::StandardFormat);
|
236
|
writer.Write();
|
237
|
std::cout << writer.GetResult() << std::endl;
|
238
|
return 0;
|
239
|
}
|
240
|
|
241
|
//catch (std::exception& e) {
|
242
|
//catch (Exiv2::AnyError& e) {
|
243
|
catch (Exiv2::Error& e) {
|
244
|
std::cout << "Caught Exiv2 exception '" << e.what() << "'\n";
|
245
|
return -1;
|
246
|
}
|