ruby-exiv2 0.4 → 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/Makefile +9 -9
- data/lib/exif.cpp +91 -153
- data/lib/exiv2.cpp +1 -0
- data/lib/exiv2.hpp +4 -0
- data/lib/extconf.rb +30 -5
- data/lib/image.cpp +35 -1
- data/lib/iptc.cpp +175 -0
- data/lib/marshall.cpp +170 -0
- data/test/data/gps-test.jpg +0 -0
- data/test/data/smiley1.jpg +0 -0
- data/test/image.rb +45 -0
- metadata +7 -3
data/lib/Makefile
CHANGED
@@ -4,7 +4,7 @@ SHELL = /bin/sh
|
|
4
4
|
#### Start of system configuration section. ####
|
5
5
|
|
6
6
|
srcdir = .
|
7
|
-
topdir = /opt/local/lib/ruby/1.8/
|
7
|
+
topdir = /opt/local/lib/ruby/1.8/i686-darwin8.8.3
|
8
8
|
hdrdir = $(topdir)
|
9
9
|
VPATH = $(srcdir):$(topdir):$(hdrdir)
|
10
10
|
prefix = $(DESTDIR)/opt/local
|
@@ -30,7 +30,7 @@ vendorlibdir = $(vendordir)/$(ruby_version)
|
|
30
30
|
sitelibdir = $(sitedir)/$(ruby_version)
|
31
31
|
libexecdir = $(exec_prefix)/libexec
|
32
32
|
|
33
|
-
CC =
|
33
|
+
CC = g++ -c
|
34
34
|
LIBRUBY = $(LIBRUBY_SO)
|
35
35
|
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
36
36
|
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
@@ -38,19 +38,19 @@ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
|
38
38
|
|
39
39
|
RUBY_EXTCONF_H =
|
40
40
|
CFLAGS = -fno-common -O -pipe -I/opt/local/include -fno-common -pipe -fno-common
|
41
|
-
INCFLAGS = -I. -I. -I/opt/local/lib/ruby/1.8/
|
42
|
-
CPPFLAGS =
|
41
|
+
INCFLAGS = -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin8.8.3 -I.
|
42
|
+
CPPFLAGS = -DHAVE_EXIF_HPP -O -pipe -I/opt/local/include -Wall -I/usr/include/exiv2 -I/Users/max/Sites/exif/include/exiv2
|
43
43
|
CXXFLAGS = $(CFLAGS)
|
44
|
-
DLDFLAGS = -L/opt/local/lib -lstdc++
|
44
|
+
DLDFLAGS = -L/opt/local/lib -lstdc++ -L/Users/max/Sites/exif/lib
|
45
45
|
LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace
|
46
46
|
AR = ar
|
47
47
|
EXEEXT =
|
48
48
|
|
49
49
|
RUBY_INSTALL_NAME = ruby
|
50
50
|
RUBY_SO_NAME = ruby
|
51
|
-
arch =
|
52
|
-
sitearch =
|
53
|
-
vendorarch =
|
51
|
+
arch = i686-darwin8.8.3
|
52
|
+
sitearch = i686-darwin8.8.3
|
53
|
+
vendorarch = i686-darwin8.8.3
|
54
54
|
ruby_version = 1.8
|
55
55
|
ruby = /opt/local/bin/ruby
|
56
56
|
RUBY = $(ruby)
|
@@ -76,7 +76,7 @@ extout =
|
|
76
76
|
extout_prefix =
|
77
77
|
target_prefix =
|
78
78
|
LOCAL_LIBS =
|
79
|
-
LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lobjc
|
79
|
+
LIBS = $(LIBRUBYARG_SHARED) -lexiv2 -lpthread -ldl -lobjc
|
80
80
|
SRCS = exif.cpp exiv2.cpp image.cpp
|
81
81
|
OBJS = exif.o exiv2.o image.o
|
82
82
|
TARGET = exiv2
|
data/lib/exif.cpp
CHANGED
@@ -1,71 +1,7 @@
|
|
1
1
|
#include "exiv2.hpp"
|
2
2
|
|
3
3
|
|
4
|
-
|
5
|
-
if(value.count() <= 0) {
|
6
|
-
rb_warn("Empty value (no entries)");
|
7
|
-
return Qnil;
|
8
|
-
}
|
9
|
-
|
10
|
-
Exiv2::TypeId type_id = value.typeId();
|
11
|
-
switch(type_id) {
|
12
|
-
case Exiv2::invalidTypeId:
|
13
|
-
{
|
14
|
-
rb_warn("Trying to demarshall invalid type id");
|
15
|
-
return Qnil;
|
16
|
-
}
|
17
|
-
|
18
|
-
case Exiv2::unsignedByte:
|
19
|
-
case Exiv2::unsignedShort:
|
20
|
-
case Exiv2::unsignedLong:
|
21
|
-
case Exiv2::signedShort:
|
22
|
-
case Exiv2::signedLong:
|
23
|
-
{
|
24
|
-
return INT2NUM(value.toLong());
|
25
|
-
}
|
26
|
-
|
27
|
-
case Exiv2::asciiString:
|
28
|
-
case Exiv2::string:
|
29
|
-
case Exiv2::undefined:
|
30
|
-
{
|
31
|
-
VALUE str = rb_str_buf_new(value.size() - 1);
|
32
|
-
value.copy((Exiv2::byte *)STR(str), Exiv2::littleEndian);
|
33
|
-
LEN(str) = value.size() - 1;
|
34
|
-
STR(str)[LEN(str)] = '\0';
|
35
|
-
LEN(str) = strlen(STR(str));
|
36
|
-
return str;
|
37
|
-
}
|
38
|
-
|
39
|
-
case Exiv2::unsignedRational:
|
40
|
-
case Exiv2::signedRational:
|
41
|
-
{
|
42
|
-
Exiv2::Rational r = value.toRational();
|
43
|
-
ID rational_id = rb_intern("Rational");
|
44
|
-
if(rb_const_defined(rb_cObject, rational_id)) {
|
45
|
-
VALUE rational = rb_const_get(rb_cObject, rational_id);
|
46
|
-
return rb_funcall(rational, rb_intern("new!"), 2, INT2NUM(r.first), INT2NUM(r.second));
|
47
|
-
}
|
48
|
-
return INT2NUM(r.first/r.second);
|
49
|
-
}
|
50
|
-
|
51
|
-
case Exiv2::invalid6:
|
52
|
-
case Exiv2::date:
|
53
|
-
case Exiv2::time:
|
54
|
-
case Exiv2::comment:
|
55
|
-
case Exiv2::directory:
|
56
|
-
case Exiv2::lastTypeId:
|
57
|
-
{
|
58
|
-
VALUE str = rb_str_buf_new(value.size() - 1);
|
59
|
-
value.copy((Exiv2::byte *)STR(str), Exiv2::littleEndian);
|
60
|
-
LEN(str) = value.size() - 1;
|
61
|
-
char msg[200];
|
62
|
-
snprintf(msg, sizeof(msg), " !!! %d", type_id);
|
63
|
-
rb_funcall(str, rb_intern("<<"), 1, rb_str_new2(msg));
|
64
|
-
return str;
|
65
|
-
}
|
66
|
-
}
|
67
|
-
return Qfalse;
|
68
|
-
}
|
4
|
+
|
69
5
|
|
70
6
|
/*
|
71
7
|
* Access exif tag by name
|
@@ -91,76 +27,9 @@ static VALUE exiv2_exif_get(VALUE self, VALUE key) {
|
|
91
27
|
}
|
92
28
|
|
93
29
|
return unmarshall_value(pos->value());
|
94
|
-
//std::string v = pos->toString();
|
95
|
-
//return rb_str_new(v.c_str(), v.length());
|
96
30
|
__NIL_END
|
97
31
|
}
|
98
32
|
|
99
|
-
/*
|
100
|
-
* First, I have to get out type by key. If such key is forbidden, I will refuse to marshall it.
|
101
|
-
* Then, I will cast ruby VALUE to C++ value, according to type_id
|
102
|
-
* then I will just set apropreciated hash entry to this casted value
|
103
|
-
*/
|
104
|
-
static bool marshall_value(Exiv2::ExifData &exifData, const char* key, VALUE value) {
|
105
|
-
Exiv2::TypeId type_id;
|
106
|
-
try {
|
107
|
-
Exiv2::ExifKey exif_key(key);
|
108
|
-
type_id = Exiv2::ExifTags::tagType(exif_key.tag(), exif_key.ifdId());
|
109
|
-
}
|
110
|
-
catch(Exiv2::Error& e) {
|
111
|
-
rb_raise(eError, "Cannot set tag %s because it doesn't exists. Look at http://www.exiv2.org/tags.html for list of supported tags", key);
|
112
|
-
}
|
113
|
-
switch(type_id) {
|
114
|
-
case Exiv2::invalidTypeId:
|
115
|
-
{
|
116
|
-
rb_warn("Trying to marshall invalid type id");
|
117
|
-
return Qnil;
|
118
|
-
}
|
119
|
-
|
120
|
-
case Exiv2::unsignedByte:
|
121
|
-
case Exiv2::unsignedShort:
|
122
|
-
case Exiv2::unsignedLong:
|
123
|
-
case Exiv2::signedShort:
|
124
|
-
case Exiv2::signedLong:
|
125
|
-
{
|
126
|
-
exifData[key] = NUM2INT(value);
|
127
|
-
return true;
|
128
|
-
}
|
129
|
-
|
130
|
-
case Exiv2::asciiString:
|
131
|
-
case Exiv2::string:
|
132
|
-
case Exiv2::undefined:
|
133
|
-
{
|
134
|
-
exifData[key] = std::string(STR(value));
|
135
|
-
return true;
|
136
|
-
}
|
137
|
-
|
138
|
-
case Exiv2::unsignedRational:
|
139
|
-
case Exiv2::signedRational:
|
140
|
-
{
|
141
|
-
if(rb_respond_to(value, rb_intern("numerator"))) {
|
142
|
-
int numerator = NUM2INT(rb_funcall(value, rb_intern("numerator"), 0));
|
143
|
-
int denominator = NUM2INT(rb_funcall(value, rb_intern("denominator"), 0));
|
144
|
-
exifData[key] = Exiv2::Rational(numerator, denominator);
|
145
|
-
return true;
|
146
|
-
}
|
147
|
-
exifData[key] = Exiv2::Rational(NUM2INT(value), 1);
|
148
|
-
return true;
|
149
|
-
}
|
150
|
-
|
151
|
-
case Exiv2::invalid6:
|
152
|
-
case Exiv2::date:
|
153
|
-
case Exiv2::time:
|
154
|
-
case Exiv2::comment:
|
155
|
-
case Exiv2::directory:
|
156
|
-
case Exiv2::lastTypeId:
|
157
|
-
{
|
158
|
-
exifData[key] = std::string(STR(value));
|
159
|
-
return true;
|
160
|
-
}
|
161
|
-
}
|
162
|
-
return false;
|
163
|
-
}
|
164
33
|
|
165
34
|
|
166
35
|
/*
|
@@ -197,7 +66,7 @@ static VALUE exiv2_exif_each(int argc, VALUE *argv, VALUE self) {
|
|
197
66
|
|
198
67
|
Exiv2::ExifData &exifData = image->image->exifData();
|
199
68
|
if(exifData.empty()) {
|
200
|
-
return
|
69
|
+
return Qnil;
|
201
70
|
}
|
202
71
|
|
203
72
|
Exiv2::ExifData::const_iterator end = exifData.end();
|
@@ -288,36 +157,96 @@ static VALUE exiv2_exif_empty(VALUE self) {
|
|
288
157
|
}
|
289
158
|
|
290
159
|
|
291
|
-
|
160
|
+
|
292
161
|
static void tag_leave(Exiv2::TagInfo* info) {
|
293
162
|
|
294
163
|
}
|
295
164
|
|
296
|
-
static VALUE create_exiv2_tag(
|
297
|
-
|
298
|
-
rb_iv_set(tag_info, "@exif", exif);
|
299
|
-
return tag_info;
|
165
|
+
static VALUE create_exiv2_tag(Exiv2::TagInfo* info) {
|
166
|
+
return Data_Wrap_Struct(cTag, 0, tag_leave, info);
|
300
167
|
}
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
for (
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
rb_yield(create_exiv2_tag(self, Exiv2::iopTagInfo + i));
|
310
|
-
}
|
311
|
-
for (int i=0; Exiv2::gpsTagInfo[i].tag_ != 0xffff; ++i) {
|
312
|
-
rb_yield(create_exiv2_tag(self, Exiv2::gpsTagInfo + i));
|
168
|
+
|
169
|
+
static int iterate_tag_collection(const Exiv2::TagInfo* collection, bool to_yield = true) {
|
170
|
+
Exiv2::TagInfo* _collection = const_cast<Exiv2::TagInfo *>(collection);
|
171
|
+
int i;
|
172
|
+
for (i=0; _collection[i].tag_ != 0xffff; ++i) {
|
173
|
+
if(to_yield) {
|
174
|
+
rb_yield(create_exiv2_tag(_collection + i));
|
175
|
+
}
|
313
176
|
}
|
177
|
+
return i;
|
178
|
+
}
|
179
|
+
|
180
|
+
#ifdef HAVE_IFDTAGLIST
|
181
|
+
static VALUE exiv2_tags_each(VALUE self) {
|
182
|
+
__BEGIN
|
183
|
+
iterate_tag_collection(Exiv2::ExifTags::ifdTagList());
|
184
|
+
iterate_tag_collection(Exiv2::ExifTags::exifTagList());
|
185
|
+
iterate_tag_collection(Exiv2::ExifTags::iopTagList());
|
186
|
+
iterate_tag_collection(Exiv2::ExifTags::gpsTagList());
|
314
187
|
return self;
|
188
|
+
__END
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
static VALUE exiv2_tags_count(VALUE self) {
|
194
|
+
__BEGIN
|
195
|
+
return INT2NUM(
|
196
|
+
iterate_tag_collection(Exiv2::ExifTags::ifdTagList(), false) +
|
197
|
+
iterate_tag_collection(Exiv2::ExifTags::exifTagList(), false) +
|
198
|
+
iterate_tag_collection(Exiv2::ExifTags::iopTagList(), false) +
|
199
|
+
iterate_tag_collection(Exiv2::ExifTags::gpsTagList(), false)
|
200
|
+
);
|
201
|
+
__END
|
202
|
+
}
|
203
|
+
#endif /* HAVE_IFDTAGLIST */
|
204
|
+
|
205
|
+
static VALUE exiv2_tag_name(VALUE self) {
|
206
|
+
__BEGIN
|
207
|
+
Exiv2::TagInfo* tag;
|
208
|
+
Data_Get_Struct(self, Exiv2::TagInfo, tag);
|
209
|
+
|
210
|
+
return tag->name_ ? rb_str_new2(tag->name_) : Qnil;
|
211
|
+
__END
|
212
|
+
}
|
213
|
+
|
214
|
+
static VALUE exiv2_tag_title(VALUE self) {
|
215
|
+
__BEGIN
|
216
|
+
Exiv2::TagInfo* tag;
|
217
|
+
Data_Get_Struct(self, Exiv2::TagInfo, tag);
|
218
|
+
|
219
|
+
return tag->title_ ? rb_str_new2(tag->title_) : Qnil;
|
220
|
+
__END
|
221
|
+
}
|
222
|
+
|
223
|
+
static VALUE exiv2_tag_desc(VALUE self) {
|
224
|
+
__BEGIN
|
225
|
+
Exiv2::TagInfo* tag;
|
226
|
+
Data_Get_Struct(self, Exiv2::TagInfo, tag);
|
227
|
+
|
228
|
+
return tag->desc_ ? rb_str_new2(tag->desc_) : Qnil;
|
229
|
+
__END
|
230
|
+
}
|
231
|
+
|
232
|
+
static VALUE exiv2_tag_section(VALUE self) {
|
233
|
+
__BEGIN
|
234
|
+
Exiv2::TagInfo* tag;
|
235
|
+
Data_Get_Struct(self, Exiv2::TagInfo, tag);
|
236
|
+
|
237
|
+
return rb_str_new2(Exiv2::ExifTags::sectionName(tag->sectionId_));
|
238
|
+
__END
|
315
239
|
}
|
316
240
|
|
317
|
-
static VALUE
|
241
|
+
static VALUE exiv2_tag_ifd(VALUE self) {
|
242
|
+
__BEGIN
|
243
|
+
Exiv2::TagInfo* tag;
|
244
|
+
Data_Get_Struct(self, Exiv2::TagInfo, tag);
|
318
245
|
|
246
|
+
return rb_str_new2(Exiv2::ExifTags::ifdName(tag->ifdId_));
|
247
|
+
__END
|
319
248
|
}
|
320
|
-
|
249
|
+
|
321
250
|
|
322
251
|
void Init_exif() {
|
323
252
|
cExif = rb_define_class_under(mExiv2, "Exif", rb_cObject);
|
@@ -328,8 +257,17 @@ void Init_exif() {
|
|
328
257
|
rb_define_method(cExif, "clear", VALUEFUNC(exiv2_exif_clear), 0);
|
329
258
|
rb_define_method(cExif, "count", VALUEFUNC(exiv2_exif_count), 0);
|
330
259
|
rb_define_method(cExif, "empty?", VALUEFUNC(exiv2_exif_empty), 0);
|
331
|
-
// rb_define_singleton_method(cExif, "exif_tags_each", VALUEFUNC(exiv2_exif_tags_each), 0);
|
332
260
|
// rb_define_singleton_method(cExif, "iptc_tags_each", VALUEFUNC(exiv2_iptc_tags_each), 0);
|
333
261
|
|
334
262
|
cTag = rb_define_class_under(mExiv2, "Tag", rb_cObject);
|
263
|
+
#ifdef HAVE_IFDTAGLIST
|
264
|
+
rb_define_singleton_method(cTag, "each", VALUEFUNC(exiv2_tags_each), 0);
|
265
|
+
rb_define_singleton_method(cTag, "count", VALUEFUNC(exiv2_tags_count), 0);
|
266
|
+
#endif /* HAVE_IFDTAGLIST */
|
267
|
+
|
268
|
+
rb_define_method(cTag, "ifd", VALUEFUNC(exiv2_tag_ifd), 0);
|
269
|
+
rb_define_method(cTag, "section", VALUEFUNC(exiv2_tag_section), 0);
|
270
|
+
rb_define_method(cTag, "name", VALUEFUNC(exiv2_tag_name), 0);
|
271
|
+
rb_define_method(cTag, "title", VALUEFUNC(exiv2_tag_title), 0);
|
272
|
+
rb_define_method(cTag, "desc", VALUEFUNC(exiv2_tag_desc), 0);
|
335
273
|
}
|
data/lib/exiv2.cpp
CHANGED
@@ -58,6 +58,7 @@ void Init_exiv2() {
|
|
58
58
|
|
59
59
|
Init_image();
|
60
60
|
Init_exif();
|
61
|
+
Init_iptc();
|
61
62
|
//cThumbnail = rb_define_class_under(mExiv2, "Thumbnail", rb_cObject);
|
62
63
|
//rb_define_method(cThumbnail, "extension", VALUEFUNC(exiv2_thumb_ext), 0);
|
63
64
|
//rb_define_method(cThumbnail, "format", VALUEFUNC(exiv2_thumb_format), 0);
|
data/lib/exiv2.hpp
CHANGED
@@ -33,6 +33,7 @@
|
|
33
33
|
// included header files
|
34
34
|
#include <image.hpp>
|
35
35
|
#include <exif.hpp>
|
36
|
+
#include <iptc.hpp>
|
36
37
|
|
37
38
|
|
38
39
|
#include <string>
|
@@ -101,9 +102,12 @@ struct rbImage {
|
|
101
102
|
Exiv2::Image::AutoPtr image;
|
102
103
|
};
|
103
104
|
|
105
|
+
bool marshall_value(Exiv2::ExifData &exifData, const char* key, VALUE value);
|
106
|
+
VALUE unmarshall_value(const Exiv2::Value& value);
|
104
107
|
|
105
108
|
void Init_image();
|
106
109
|
void Init_exif();
|
110
|
+
void Init_iptc();
|
107
111
|
|
108
112
|
|
109
113
|
#endif /* EXIV2_HPP_ */
|
data/lib/extconf.rb
CHANGED
@@ -1,15 +1,40 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "mkmf"
|
4
|
-
#CONFIG["CXX"] = "g++ -g " + CONFIG['LDSHARED']
|
5
4
|
CONFIG["CPP"] = "g++ -E "
|
6
|
-
|
5
|
+
CONFIG["CC"] = "g++ -c "
|
7
6
|
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
$
|
8
|
+
#
|
9
|
+
# -I/usr/local/include/exiv2
|
10
|
+
$CPPFLAGS << " -Wall -I/usr/include/exiv2 -I/Users/max/Sites/exif/include/exiv2 "
|
11
|
+
$LDFLAGS << " -lstdc++ -L/Users/max/Sites/exif/lib "
|
12
12
|
have_header "exif.hpp"
|
13
13
|
have_library "exiv2", "_ZN5Exiv29MetadatumD0Ev"
|
14
|
+
|
15
|
+
|
16
|
+
ifd_tag_list = "Exiv2::ExifTags::ifdTagList"
|
17
|
+
def ifd_tag_list.upcase
|
18
|
+
"IFDTAGLIST"
|
19
|
+
end
|
20
|
+
have_func ifd_tag_list do <<-SRC
|
21
|
+
#include <image.hpp>
|
22
|
+
#include <exif.hpp>
|
23
|
+
|
24
|
+
|
25
|
+
#include <string>
|
26
|
+
#include <vector>
|
27
|
+
#include <iostream>
|
28
|
+
#include <fstream>
|
29
|
+
#include <iomanip>
|
30
|
+
#include <cstring>
|
31
|
+
#include <cassert>
|
32
|
+
|
33
|
+
#include <stdarg.h>
|
34
|
+
int main(void) {
|
35
|
+
Exiv2::ExifTags::ifdTagList();
|
36
|
+
}
|
37
|
+
SRC
|
38
|
+
end
|
14
39
|
create_makefile 'exiv2'
|
15
40
|
|
data/lib/image.cpp
CHANGED
@@ -48,6 +48,24 @@ static VALUE exiv2_image_initialize(VALUE self, VALUE file) {
|
|
48
48
|
__END
|
49
49
|
}
|
50
50
|
|
51
|
+
/*
|
52
|
+
*
|
53
|
+
* Load Exiv2::Image from memory string
|
54
|
+
* content = File.open("a.jpg").read
|
55
|
+
* img = Exiv2::Image.load_string(content)
|
56
|
+
*/
|
57
|
+
static VALUE exiv2_image_load_string(VALUE self, VALUE string) {
|
58
|
+
__BEGIN
|
59
|
+
Check_Type(string, T_STRING);
|
60
|
+
rbImage* image = new rbImage();
|
61
|
+
image->dirty = false;
|
62
|
+
VALUE img = Data_Wrap_Struct(self, 0, image_delete, image);
|
63
|
+
|
64
|
+
image->image = Exiv2::ImageFactory::open(CBSTR(string), LEN(string));
|
65
|
+
return img;
|
66
|
+
__END
|
67
|
+
}
|
68
|
+
|
51
69
|
|
52
70
|
static void image_real_save(rbImage* image) {
|
53
71
|
if(image->dirty) {
|
@@ -127,11 +145,24 @@ static VALUE exiv2_image_exif(VALUE self) {
|
|
127
145
|
rbImage* image;
|
128
146
|
Data_Get_Struct(self, rbImage, image);
|
129
147
|
VALUE exif = Data_Wrap_Struct(cExif, 0, image_leave, image);
|
130
|
-
rb_iv_set(
|
148
|
+
rb_iv_set(exif, "@image", self);
|
131
149
|
return exif;
|
132
150
|
__END
|
133
151
|
}
|
134
152
|
|
153
|
+
/*
|
154
|
+
* Access to iptc data of image
|
155
|
+
*/
|
156
|
+
static VALUE exiv2_image_iptc(VALUE self) {
|
157
|
+
__BEGIN
|
158
|
+
rbImage* image;
|
159
|
+
Data_Get_Struct(self, rbImage, image);
|
160
|
+
VALUE iptc = Data_Wrap_Struct(cIptc, 0, image_leave, image);
|
161
|
+
rb_iv_set(iptc, "@image", self);
|
162
|
+
return iptc;
|
163
|
+
__END
|
164
|
+
}
|
165
|
+
|
135
166
|
|
136
167
|
/*
|
137
168
|
* Dump thumbnail to file.
|
@@ -175,7 +206,10 @@ void Init_image() {
|
|
175
206
|
cImage = rb_define_class_under(mExiv2, "Image", rb_cObject);
|
176
207
|
rb_define_alloc_func(cImage, exiv2_image_s_allocate);
|
177
208
|
rb_define_method(cImage, "initialize", VALUEFUNC(exiv2_image_initialize), 1);
|
209
|
+
rb_define_singleton_method(cImage, "load_string", VALUEFUNC(exiv2_image_load_string), 1);
|
210
|
+
|
178
211
|
rb_define_method(cImage, "exif", VALUEFUNC(exiv2_image_exif), 0);
|
212
|
+
rb_define_method(cImage, "iptc", VALUEFUNC(exiv2_image_iptc), 0);
|
179
213
|
|
180
214
|
rb_define_method(cImage, "thumbnail", VALUEFUNC(exiv2_image_thumbnail), 1);
|
181
215
|
rb_define_method(cImage, "thumbnail=", VALUEFUNC(exiv2_image_thumbnail_set), 1);
|
data/lib/iptc.cpp
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
#include "exiv2.hpp"
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
/*
|
6
|
+
* Access iptc tag by name
|
7
|
+
*
|
8
|
+
* <code>Exiv2::Image.new("test/data/smiley.jpg").iptc["Iptc.Application2.ObjectName"] => "GreeenDude"</code>
|
9
|
+
*/
|
10
|
+
static VALUE exiv2_iptc_get(VALUE self, VALUE key) {
|
11
|
+
__BEGIN
|
12
|
+
rbImage* image;
|
13
|
+
Data_Get_Struct(self, rbImage, image);
|
14
|
+
|
15
|
+
VALUE strkey = rb_funcall(key, rb_intern("to_s"), 0);
|
16
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
17
|
+
|
18
|
+
if(iptcData.empty()) {
|
19
|
+
return Qnil;
|
20
|
+
}
|
21
|
+
|
22
|
+
Exiv2::IptcKey iptcKey(STR(strkey));
|
23
|
+
Exiv2::IptcData::const_iterator pos = iptcData.findKey(iptcKey);
|
24
|
+
if (pos == iptcData.end()) {
|
25
|
+
return Qnil;
|
26
|
+
}
|
27
|
+
|
28
|
+
return unmarshall_value(pos->value());
|
29
|
+
__NIL_END
|
30
|
+
}
|
31
|
+
|
32
|
+
#if 0
|
33
|
+
/*
|
34
|
+
* @iptc["Iptc.Application2.ObjectName"] = "GreeenDude"
|
35
|
+
* [] — is a universal accessor
|
36
|
+
*/
|
37
|
+
static VALUE exiv2_iptc_set(VALUE self, VALUE key, VALUE value) {
|
38
|
+
__BEGIN
|
39
|
+
rbImage* image;
|
40
|
+
Data_Get_Struct(self, rbImage, image);
|
41
|
+
|
42
|
+
VALUE strkey = rb_funcall(key, rb_intern("to_s"), 0);
|
43
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
44
|
+
|
45
|
+
if(!marshall_value(iptcData, STR(strkey), value)) {
|
46
|
+
THROW("Couldn't write %s", STR(strkey));
|
47
|
+
}
|
48
|
+
|
49
|
+
image->dirty = true;
|
50
|
+
return value;
|
51
|
+
__NIL_END
|
52
|
+
}
|
53
|
+
#endif
|
54
|
+
|
55
|
+
/*
|
56
|
+
* Iterates through all iptc tags in image
|
57
|
+
*/
|
58
|
+
static VALUE exiv2_iptc_each(int argc, VALUE *argv, VALUE self) {
|
59
|
+
__BEGIN
|
60
|
+
rbImage* image;
|
61
|
+
Data_Get_Struct(self, rbImage, image);
|
62
|
+
|
63
|
+
VALUE prefix;
|
64
|
+
rb_scan_args(argc, argv, "01", &prefix);
|
65
|
+
|
66
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
67
|
+
if(iptcData.empty()) {
|
68
|
+
return Qnil;
|
69
|
+
}
|
70
|
+
|
71
|
+
Exiv2::IptcData::const_iterator end = iptcData.end();
|
72
|
+
for(Exiv2::IptcData::const_iterator i = iptcData.begin(); i != end; ++i) {
|
73
|
+
VALUE key = rb_str_new(i->key().c_str(), i->key().length());
|
74
|
+
VALUE val = unmarshall_value(i->value());
|
75
|
+
//VALUE val = rb_str_new(i->toString().c_str(), i->toString().length());
|
76
|
+
if(prefix != Qnil && INT2FIX(0) != rb_funcall(key, rb_intern("index"), 1, prefix)) {
|
77
|
+
continue;
|
78
|
+
}
|
79
|
+
rb_yield(rb_ary_new3(2, key, val));
|
80
|
+
}
|
81
|
+
return self;
|
82
|
+
__END
|
83
|
+
}
|
84
|
+
|
85
|
+
#if 0
|
86
|
+
/*
|
87
|
+
* Delete iptc value by it's name
|
88
|
+
*/
|
89
|
+
static VALUE exiv2_iptc_delete(VALUE self, VALUE key) {
|
90
|
+
__BEGIN
|
91
|
+
rbImage* image;
|
92
|
+
Data_Get_Struct(self, rbImage, image);
|
93
|
+
|
94
|
+
VALUE strkey = rb_funcall(key, rb_intern("to_s"), 0);
|
95
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
96
|
+
|
97
|
+
if(iptcData.empty()) {
|
98
|
+
return Qnil;
|
99
|
+
}
|
100
|
+
|
101
|
+
Exiv2::IptcKey iptcKey(STR(strkey));
|
102
|
+
Exiv2::IptcData::iterator pos = iptcData.findKey(iptcKey);
|
103
|
+
if (pos == iptcData.end()) {
|
104
|
+
return Qnil;
|
105
|
+
}
|
106
|
+
|
107
|
+
std::string v = pos->toString();
|
108
|
+
iptcData.erase(pos);
|
109
|
+
return rb_str_new(v.c_str(), v.length());
|
110
|
+
__NIL_END
|
111
|
+
}
|
112
|
+
#endif
|
113
|
+
|
114
|
+
/*
|
115
|
+
* Clear all iptc data in image
|
116
|
+
*/
|
117
|
+
static VALUE exiv2_iptc_clear(VALUE self) {
|
118
|
+
__BEGIN
|
119
|
+
rbImage* image;
|
120
|
+
Data_Get_Struct(self, rbImage, image);
|
121
|
+
|
122
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
123
|
+
|
124
|
+
if(iptcData.empty()) {
|
125
|
+
return Qnil;
|
126
|
+
}
|
127
|
+
iptcData.clear();
|
128
|
+
return self;
|
129
|
+
__END
|
130
|
+
}
|
131
|
+
|
132
|
+
/*
|
133
|
+
* Count of iptc tags in image
|
134
|
+
*/
|
135
|
+
static VALUE exiv2_iptc_count(VALUE self) {
|
136
|
+
__BEGIN
|
137
|
+
rbImage* image;
|
138
|
+
Data_Get_Struct(self, rbImage, image);
|
139
|
+
|
140
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
141
|
+
|
142
|
+
return INT2FIX(iptcData.count());
|
143
|
+
__END
|
144
|
+
}
|
145
|
+
|
146
|
+
/*
|
147
|
+
* Predicate method. Is iptc empty?
|
148
|
+
*/
|
149
|
+
static VALUE exiv2_iptc_empty(VALUE self) {
|
150
|
+
__BEGIN
|
151
|
+
rbImage* image;
|
152
|
+
Data_Get_Struct(self, rbImage, image);
|
153
|
+
|
154
|
+
Exiv2::IptcData &iptcData = image->image->iptcData();
|
155
|
+
|
156
|
+
return iptcData.empty() ? Qtrue : Qfalse;
|
157
|
+
__NIL_END
|
158
|
+
}
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
void Init_iptc() {
|
165
|
+
cIptc = rb_define_class_under(mExiv2, "Iptc", rb_cObject);
|
166
|
+
rb_define_method(cIptc, "each", VALUEFUNC(exiv2_iptc_each), -1);
|
167
|
+
rb_define_method(cIptc, "[]", VALUEFUNC(exiv2_iptc_get), 1);
|
168
|
+
#if 0
|
169
|
+
rb_define_method(cIptc, "[]=", VALUEFUNC(exiv2_iptc_set), 2);
|
170
|
+
rb_define_method(cIptc, "delete", VALUEFUNC(exiv2_iptc_delete), 1);
|
171
|
+
#endif
|
172
|
+
rb_define_method(cIptc, "clear", VALUEFUNC(exiv2_iptc_clear), 0);
|
173
|
+
rb_define_method(cIptc, "count", VALUEFUNC(exiv2_iptc_count), 0);
|
174
|
+
rb_define_method(cIptc, "empty?", VALUEFUNC(exiv2_iptc_empty), 0);
|
175
|
+
}
|
data/lib/marshall.cpp
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
#include "exiv2.hpp"
|
2
|
+
|
3
|
+
|
4
|
+
typedef VALUE (unmarshaller)(const Exiv2::Value& value, long pos = 0);
|
5
|
+
|
6
|
+
static VALUE unmarshall_long_value(const Exiv2::Value& value, long pos = 0) {
|
7
|
+
return INT2NUM(value.toLong(pos));
|
8
|
+
}
|
9
|
+
|
10
|
+
|
11
|
+
static VALUE unmarshall_rational_value(const Exiv2::Value& value, long pos = 0) {
|
12
|
+
Exiv2::Rational r = value.toRational(pos);
|
13
|
+
ID rational_id = rb_intern("Rational");
|
14
|
+
if(rb_const_defined(rb_cObject, rational_id)) {
|
15
|
+
VALUE rational = rb_const_get(rb_cObject, rational_id);
|
16
|
+
return rb_funcall(rational, rb_intern("new!"), 2, INT2NUM(r.first), INT2NUM(r.second));
|
17
|
+
}
|
18
|
+
return INT2NUM(r.first/r.second);
|
19
|
+
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
static VALUE unmarshall_multiple_values(const Exiv2::Value& value, unmarshaller f) {
|
24
|
+
if(value.count() <= 0) {
|
25
|
+
rb_warn("Empty value (no entries)");
|
26
|
+
return Qnil;
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
if(value.count() == 1) {
|
31
|
+
return f(value);
|
32
|
+
}
|
33
|
+
|
34
|
+
VALUE retval = rb_ary_new2(value.count());
|
35
|
+
for(int i = 0; i < value.count(); i++) {
|
36
|
+
rb_ary_store(retval, i, f(value, i));
|
37
|
+
}
|
38
|
+
return retval;
|
39
|
+
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
VALUE unmarshall_value(const Exiv2::Value& value) {
|
46
|
+
Exiv2::TypeId type_id = value.typeId();
|
47
|
+
|
48
|
+
|
49
|
+
switch(type_id) {
|
50
|
+
case Exiv2::invalidTypeId:
|
51
|
+
{
|
52
|
+
rb_warn("Trying to demarshall invalid type id");
|
53
|
+
return Qnil;
|
54
|
+
}
|
55
|
+
|
56
|
+
case Exiv2::unsignedByte:
|
57
|
+
case Exiv2::unsignedShort:
|
58
|
+
case Exiv2::unsignedLong:
|
59
|
+
case Exiv2::signedShort:
|
60
|
+
case Exiv2::signedLong:
|
61
|
+
{
|
62
|
+
return unmarshall_multiple_values(value, unmarshall_long_value);
|
63
|
+
}
|
64
|
+
|
65
|
+
case Exiv2::asciiString:
|
66
|
+
case Exiv2::string:
|
67
|
+
case Exiv2::undefined:
|
68
|
+
{
|
69
|
+
std::string str_value = value.toString();
|
70
|
+
return rb_str_new(str_value.c_str(), str_value.length());
|
71
|
+
}
|
72
|
+
|
73
|
+
case Exiv2::unsignedRational:
|
74
|
+
case Exiv2::signedRational:
|
75
|
+
{
|
76
|
+
return unmarshall_multiple_values(value, unmarshall_rational_value);
|
77
|
+
|
78
|
+
Exiv2::Rational r = value.toRational();
|
79
|
+
ID rational_id = rb_intern("Rational");
|
80
|
+
if(rb_const_defined(rb_cObject, rational_id)) {
|
81
|
+
VALUE rational = rb_const_get(rb_cObject, rational_id);
|
82
|
+
return rb_funcall(rational, rb_intern("new!"), 2, INT2NUM(r.first), INT2NUM(r.second));
|
83
|
+
}
|
84
|
+
return INT2NUM(r.first/r.second);
|
85
|
+
}
|
86
|
+
|
87
|
+
case Exiv2::invalid6:
|
88
|
+
case Exiv2::date:
|
89
|
+
case Exiv2::time:
|
90
|
+
case Exiv2::comment:
|
91
|
+
case Exiv2::directory:
|
92
|
+
case Exiv2::lastTypeId:
|
93
|
+
{
|
94
|
+
VALUE str = rb_str_buf_new(value.size() - 1);
|
95
|
+
value.copy((Exiv2::byte *)STR(str), Exiv2::littleEndian);
|
96
|
+
LEN(str) = value.size() - 1;
|
97
|
+
char msg[200];
|
98
|
+
snprintf(msg, sizeof(msg), " !!! %d", type_id);
|
99
|
+
rb_funcall(str, rb_intern("<<"), 1, rb_str_new2(msg));
|
100
|
+
return str;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
return Qfalse;
|
104
|
+
}
|
105
|
+
|
106
|
+
/*
|
107
|
+
* First, I have to get out type by key. If such key is forbidden, I will refuse to marshall it.
|
108
|
+
* Then, I will cast ruby VALUE to C++ value, according to type_id
|
109
|
+
* then I will just set apropreciated hash entry to this casted value
|
110
|
+
*/
|
111
|
+
bool marshall_value(Exiv2::ExifData &exifData, const char* key, VALUE value) {
|
112
|
+
Exiv2::TypeId type_id;
|
113
|
+
try {
|
114
|
+
Exiv2::ExifKey exif_key(key);
|
115
|
+
type_id = Exiv2::ExifTags::tagType(exif_key.tag(), exif_key.ifdId());
|
116
|
+
}
|
117
|
+
catch(Exiv2::Error& e) {
|
118
|
+
rb_raise(eError, "Cannot set tag %s because it doesn't exists. Look at http://www.exiv2.org/tags.html for list of supported tags", key);
|
119
|
+
}
|
120
|
+
switch(type_id) {
|
121
|
+
case Exiv2::invalidTypeId:
|
122
|
+
{
|
123
|
+
rb_warn("Trying to marshall invalid type id");
|
124
|
+
return Qnil;
|
125
|
+
}
|
126
|
+
|
127
|
+
case Exiv2::unsignedByte:
|
128
|
+
case Exiv2::unsignedShort:
|
129
|
+
case Exiv2::unsignedLong:
|
130
|
+
case Exiv2::signedShort:
|
131
|
+
case Exiv2::signedLong:
|
132
|
+
{
|
133
|
+
exifData[key] = NUM2INT(value);
|
134
|
+
return true;
|
135
|
+
}
|
136
|
+
|
137
|
+
case Exiv2::asciiString:
|
138
|
+
case Exiv2::string:
|
139
|
+
case Exiv2::undefined:
|
140
|
+
{
|
141
|
+
exifData[key] = std::string(STR(value));
|
142
|
+
return true;
|
143
|
+
}
|
144
|
+
|
145
|
+
case Exiv2::unsignedRational:
|
146
|
+
case Exiv2::signedRational:
|
147
|
+
{
|
148
|
+
if(rb_respond_to(value, rb_intern("numerator"))) {
|
149
|
+
int numerator = NUM2INT(rb_funcall(value, rb_intern("numerator"), 0));
|
150
|
+
int denominator = NUM2INT(rb_funcall(value, rb_intern("denominator"), 0));
|
151
|
+
exifData[key] = Exiv2::Rational(numerator, denominator);
|
152
|
+
return true;
|
153
|
+
}
|
154
|
+
exifData[key] = Exiv2::Rational(NUM2INT(value), 1);
|
155
|
+
return true;
|
156
|
+
}
|
157
|
+
|
158
|
+
case Exiv2::invalid6:
|
159
|
+
case Exiv2::date:
|
160
|
+
case Exiv2::time:
|
161
|
+
case Exiv2::comment:
|
162
|
+
case Exiv2::directory:
|
163
|
+
case Exiv2::lastTypeId:
|
164
|
+
{
|
165
|
+
exifData[key] = std::string(STR(value));
|
166
|
+
return true;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
return false;
|
170
|
+
}
|
Binary file
|
Binary file
|
data/test/image.rb
CHANGED
@@ -129,10 +129,55 @@ class ImageTest < Test::Unit::TestCase
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
+
def test_read_undefined
|
133
|
+
open_test_file "exiv2-fujifilm-finepix-s2pro.jpg" do |filename|
|
134
|
+
assert @img = Exiv2::Image.new(filename)
|
135
|
+
assert_equal "48 49 48 48 ", @img.exif["Exif.Iop.InteroperabilityVersion"]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_read_ascii
|
140
|
+
open_test_file "exiv2-fujifilm-finepix-s2pro.jpg" do |filename|
|
141
|
+
assert @img = Exiv2::Image.new(filename)
|
142
|
+
assert_equal "R98", @img.exif["Exif.Iop.InteroperabilityIndex"]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
132
146
|
def __test_thumbnail
|
133
147
|
open_test_file "exiv2-fujifilm-finepix-s2pro.jpg" do |filename|
|
134
148
|
assert @img = Exiv2::Image.new(filename)
|
135
149
|
|
136
150
|
end
|
137
151
|
end
|
152
|
+
|
153
|
+
def test_read_multiple_rationals
|
154
|
+
open_test_file "gps-test.jpg" do |filename|
|
155
|
+
assert @img = Exiv2::Image.new(filename)
|
156
|
+
require 'rational'
|
157
|
+
assert_equal [Rational.new!(41, 1), Rational.new!(53, 1), Rational.new!(4091, 100)], @img.exif["Exif.GPSInfo.GPSLatitude"]
|
158
|
+
assert_equal [Rational.new!(12, 1), Rational.new!(28, 1), Rational.new!(5996, 100)], @img.exif["Exif.GPSInfo.GPSLongitude"]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_tag_access
|
163
|
+
return unless Exiv2::Tag.respond_to?(:count)
|
164
|
+
assert_equal 139, Exiv2::Tag.count
|
165
|
+
exif_tag = nil
|
166
|
+
Exiv2::Tag.each do |tag|
|
167
|
+
exif_tag = tag
|
168
|
+
break
|
169
|
+
end
|
170
|
+
assert_equal "IFD0", exif_tag.ifd
|
171
|
+
assert_equal "ImageStructure", exif_tag.section
|
172
|
+
assert_equal "NewSubfileType", exif_tag.name
|
173
|
+
assert_equal "New Subfile Type", exif_tag.title
|
174
|
+
assert_equal "A general indication of the kind of data contained in this subfile.", exif_tag.desc
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_iptc_get
|
178
|
+
open_test_file "smiley1.jpg" do |filename|
|
179
|
+
assert @img = Exiv2::Image.new(filename)
|
180
|
+
assert_equal "Seattle", @img.iptc["Iptc.Application2.City"]
|
181
|
+
end
|
182
|
+
end
|
138
183
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ruby-exiv2
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2006-11-
|
6
|
+
version: "0.5"
|
7
|
+
date: 2006-11-20 00:00:00 +03:00
|
8
8
|
summary: Exiv2 (exif image tags handling) library driver
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -12,7 +12,7 @@ email: max@maxidoors.ru
|
|
12
12
|
homepage: http://maxidoors.ru/
|
13
13
|
rubyforge_project: ruby-exiv2
|
14
14
|
description:
|
15
|
-
autorequire:
|
15
|
+
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
18
|
has_rdoc: true
|
@@ -32,12 +32,16 @@ files:
|
|
32
32
|
- test/data
|
33
33
|
- test/image.rb
|
34
34
|
- test/data/exiv2-fujifilm-finepix-s2pro.jpg
|
35
|
+
- test/data/gps-test.jpg
|
36
|
+
- test/data/smiley1.jpg
|
35
37
|
- lib/exif.cpp
|
36
38
|
- lib/exiv2.cpp
|
37
39
|
- lib/exiv2.hpp
|
38
40
|
- lib/extconf.rb
|
39
41
|
- lib/image.cpp
|
42
|
+
- lib/iptc.cpp
|
40
43
|
- lib/Makefile
|
44
|
+
- lib/marshall.cpp
|
41
45
|
- README
|
42
46
|
test_files: []
|
43
47
|
|