ruby-exiv2 1.3 → 1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -0
- data/Rakefile +5 -29
- data/{ext → lib}/exiv2.hpp +4 -4
- data/lib/exiv2.rb +22 -28
- data/lib/exiv2/exif.rb +19 -0
- data/lib/exiv2/image.rb +205 -0
- data/lib/exiv2/iptc.rb +19 -0
- data/lib/exiv2/metadata/clear.cpp +14 -0
- data/lib/exiv2/metadata/count.cpp +10 -0
- data/lib/exiv2/metadata/delete.cpp +23 -0
- data/lib/exiv2/metadata/each.cpp +26 -0
- data/lib/exiv2/metadata/get.cpp +21 -0
- data/lib/exiv2/metadata/is_empty.cpp +10 -0
- data/lib/exiv2/metadata/marshall.cpp +85 -0
- data/lib/exiv2/metadata/set.cpp +16 -0
- data/{ext/marshall.cpp → lib/exiv2/metadata/unmarshall.cpp} +0 -1
- data/lib/exiv2/tag.rb +110 -0
- data/lib/exiv2/throw.cpp +12 -0
- data/test/{image.rb → test_image.rb} +14 -2
- metadata +58 -48
- data/ext/exif.cpp +0 -369
- data/ext/exiv2.cpp +0 -78
- data/ext/extconf.rb +0 -75
- data/ext/image.cpp +0 -230
- data/ext/iptc.cpp +0 -253
- data/setup.rb +0 -1585
data/README
CHANGED
data/Rakefile
CHANGED
@@ -6,17 +6,11 @@ require 'rake/packagetask'
|
|
6
6
|
require 'rake/contrib/rubyforgepublisher'
|
7
7
|
|
8
8
|
PKG_NAME = "ruby-exiv2"
|
9
|
-
PKG_VERSION = "1.
|
9
|
+
PKG_VERSION = "1.5"
|
10
10
|
PKG_AUTHOR = "Max Lapshin"
|
11
11
|
PKG_EMAIL = "max@maxidoors.ru"
|
12
|
-
PKG_HOMEPAGE = "http://
|
12
|
+
PKG_HOMEPAGE = "http://github.com/maxlapshin/exiv2"
|
13
13
|
PKG_SUMMARY = "Exiv2 (exif image tags handling) library driver"
|
14
|
-
PKG_SVN = "http://svn.maxidoors.ru/ruby-exiv2"
|
15
|
-
PKG_RDOC_OPTS = ['--main=README',
|
16
|
-
'--line-numbers',
|
17
|
-
'--webcvs='+PKG_SVN,
|
18
|
-
'--charset=utf-8',
|
19
|
-
'--promiscuous']
|
20
14
|
|
21
15
|
|
22
16
|
spec = Gem::Specification.new do |s|
|
@@ -29,19 +23,16 @@ spec = Gem::Specification.new do |s|
|
|
29
23
|
s.summary = PKG_SUMMARY
|
30
24
|
s.require_path = "lib"
|
31
25
|
s.rubyforge_project = PKG_NAME
|
32
|
-
s.files = %w(README Rakefile
|
26
|
+
s.files = %w(README Rakefile init.rb) +
|
33
27
|
Dir.glob("{test}/**/*") +
|
34
|
-
Dir.glob("
|
35
|
-
Dir.glob("lib/**/*.rb")
|
28
|
+
Dir.glob("lib/**/*.{rb,cpp,hpp}")
|
36
29
|
s.test_files = FileList["{test}/**/*.rb"].to_a
|
37
30
|
s.has_rdoc = true
|
38
31
|
s.extra_rdoc_files = ["README"]
|
39
|
-
s.rdoc_options = PKG_RDOC_OPTS
|
40
|
-
s.extensions << 'ext/extconf.rb'
|
41
32
|
end
|
42
33
|
|
43
34
|
Rake::GemPackageTask.new(spec) do |pkg|
|
44
|
-
pkg.need_tar =
|
35
|
+
pkg.need_tar = false
|
45
36
|
end
|
46
37
|
|
47
38
|
task :default => [ :test ]
|
@@ -77,7 +68,6 @@ Rake::RDocTask.new("doc") do |rdoc|
|
|
77
68
|
rdoc.rdoc_files.include('README')
|
78
69
|
# rdoc.rdoc_files.include('CHANGELOG')
|
79
70
|
# rdoc.rdoc_files.include('TODO')
|
80
|
-
rdoc.options = PKG_RDOC_OPTS
|
81
71
|
rdoc.rdoc_files.include "ruby-exiv2.cpp"
|
82
72
|
end
|
83
73
|
|
@@ -109,18 +99,4 @@ task :push_docs do
|
|
109
99
|
].each { |p| p.upload }
|
110
100
|
end
|
111
101
|
|
112
|
-
desc "Build binary driver"
|
113
|
-
task :build do
|
114
|
-
puts `cd lib; [ -e Makefile ] || ruby extconf.rb; make`
|
115
|
-
end
|
116
|
-
|
117
|
-
desc "Rebuild binary driver"
|
118
|
-
task :rebuild do
|
119
|
-
puts `cd lib; ruby extconf.rb; make clean all`
|
120
|
-
end
|
121
102
|
|
122
|
-
desc "Mark files in SVN"
|
123
|
-
task :release => [:clobber, :package] do
|
124
|
-
svn_aware_revision = 'r_' + PKG_VERSION.gsub(/-|\./, '_')
|
125
|
-
puts `svn copy #{PKG_SVN}/trunk #{PKG_SVN}/tags/#{svn_aware_revision} -m "release #{svn_aware_revision}"`
|
126
|
-
end
|
data/{ext → lib}/exiv2.hpp
RENAMED
@@ -89,7 +89,7 @@ void rb_exiv2_throw(const char* file, long unsigned int line, const char *fmt, .
|
|
89
89
|
#if defined(__cplusplus)
|
90
90
|
extern "C" {
|
91
91
|
#endif
|
92
|
-
void Init_exiv2(void);
|
92
|
+
//void Init_exiv2(void);
|
93
93
|
extern VALUE mExiv2, cImage, cExif, cTag, cIptc, eError;
|
94
94
|
|
95
95
|
#if defined(__cplusplus)
|
@@ -105,9 +105,9 @@ struct rbImage {
|
|
105
105
|
VALUE unmarshall_value(const Exiv2::Value& value);
|
106
106
|
|
107
107
|
|
108
|
-
void Init_image();
|
109
|
-
void Init_exif();
|
110
|
-
void Init_iptc();
|
108
|
+
//void Init_image();
|
109
|
+
//void Init_exif();
|
110
|
+
//void Init_iptc();
|
111
111
|
|
112
112
|
|
113
113
|
#endif /* EXIV2_HPP_ */
|
data/lib/exiv2.rb
CHANGED
@@ -1,38 +1,32 @@
|
|
1
|
-
begin
|
2
|
-
require File.dirname(__FILE__)+'/exiv2_bin'
|
3
|
-
rescue LoadError
|
4
|
-
require File.dirname(__FILE__)+'/../ext/exiv2_bin'
|
5
|
-
end
|
6
1
|
require 'enumerator'
|
7
2
|
require 'rubygems'
|
3
|
+
require 'inline'
|
8
4
|
require 'active_support'
|
5
|
+
require 'erb'
|
9
6
|
|
10
7
|
module Exiv2
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
# method []= is used to set exif or iptc data
|
21
|
-
def []=(key, value)
|
22
|
-
return exif[key] = value if key[0...4] == "Exif"
|
23
|
-
return iptc[key] = value if key[0...4] == "Iptc"
|
24
|
-
raise Exiv2::Error, "Unknown key for writing: #{key.inspect}"
|
25
|
-
end
|
26
|
-
|
27
|
-
# clues together Iptc.Application2.DateCreated and Iptc.Application2.TimeCreated if possible
|
28
|
-
def created_at
|
29
|
-
date = iptc["Iptc.Application2.DateCreated"]
|
30
|
-
time = iptc["Iptc.Application2.TimeCreated"]
|
31
|
-
return date unless time
|
32
|
-
Time.utc(date.year, date.month, date.day, time.hour, time.min, time.sec)
|
33
|
-
end
|
8
|
+
def self.prepare_builder(builder)
|
9
|
+
Config::CONFIG["CPP"] = "g++ -E "
|
10
|
+
Config::CONFIG["CC"] = "g++ "
|
11
|
+
Config::CONFIG["LDSHARED"].gsub!(/^cc /,"g++ ")
|
12
|
+
builder.include "<exiv2.hpp>"
|
13
|
+
builder.add_compile_flags "-x c++ -I#{File.dirname(__FILE__)} -I/usr/local/include/exiv2", '-lstdc++ -lexiv2'
|
14
|
+
builder.include_ruby_last
|
34
15
|
end
|
35
16
|
|
17
|
+
def self.load(&block)
|
18
|
+
ERB.new(File.read(File.dirname(__FILE__)+"/exiv2/" + block.call)).result(block.binding)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'exiv2/image'
|
23
|
+
require 'exiv2/exif'
|
24
|
+
require 'exiv2/iptc'
|
25
|
+
require 'exiv2/tag'
|
26
|
+
|
27
|
+
module Exiv2
|
28
|
+
class Error < StandardError; end
|
29
|
+
|
36
30
|
class SubTagAccess
|
37
31
|
def initialize(class_name, sub_tag, parent)
|
38
32
|
@class_name = class_name.split("::").last
|
data/lib/exiv2/exif.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Exiv2
|
2
|
+
class Exif
|
3
|
+
inline do |builder|
|
4
|
+
kind = "Exif"
|
5
|
+
Exiv2::prepare_builder(builder)
|
6
|
+
builder.prefix(Exiv2::load {"metadata/marshall.cpp"})
|
7
|
+
builder.prefix(Exiv2::load {"metadata/unmarshall.cpp"})
|
8
|
+
builder.prefix(Exiv2::load {"throw.cpp"})
|
9
|
+
|
10
|
+
builder.c(Exiv2::load {"metadata/get.cpp"}, {:method_name => "[]"})
|
11
|
+
builder.c(Exiv2::load {"metadata/set.cpp"}, {:method_name => "[]="})
|
12
|
+
builder.c_raw(Exiv2::load {"metadata/each.cpp"}, {:method_name => "each"})
|
13
|
+
builder.c(Exiv2::load {"metadata/delete.cpp"}, {:method_name => "delete"})
|
14
|
+
builder.c(Exiv2::load {"metadata/clear.cpp"}, {:method_name => "clear"})
|
15
|
+
builder.c(Exiv2::load {"metadata/count.cpp"}, {:method_name => "count"})
|
16
|
+
builder.c(Exiv2::load {"metadata/is_empty.cpp"}, {:method_name => "empty?"})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/exiv2/image.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
module Exiv2
|
2
|
+
class Image
|
3
|
+
# method [] is used to read exif or iptc data
|
4
|
+
def [](key)
|
5
|
+
return exif[key] if key[0...4] == "Exif"
|
6
|
+
return iptc[key] if key[0...4] == "Iptc"
|
7
|
+
end
|
8
|
+
|
9
|
+
# method []= is used to set exif or iptc data
|
10
|
+
def []=(key, value)
|
11
|
+
return exif[key] = value if key[0...4] == "Exif"
|
12
|
+
return iptc[key] = value if key[0...4] == "Iptc"
|
13
|
+
raise Exiv2::Error, "Unknown key for writing: #{key.inspect}"
|
14
|
+
end
|
15
|
+
|
16
|
+
# clues together Iptc.Application2.DateCreated and Iptc.Application2.TimeCreated if possible
|
17
|
+
def created_at
|
18
|
+
date = iptc["Iptc.Application2.DateCreated"]
|
19
|
+
time = iptc["Iptc.Application2.TimeCreated"]
|
20
|
+
return date unless time
|
21
|
+
Time.utc(date.year, date.month, date.day, time.hour, time.min, time.sec)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
inline do |builder|
|
29
|
+
Exiv2.prepare_builder(builder)
|
30
|
+
builder.prefix <<-PREFIX
|
31
|
+
VALUE mExiv2, cImage, cExif, cTag, cIptc, cThumbnail, eError;
|
32
|
+
|
33
|
+
|
34
|
+
static void image_real_save(rbImage* image) {
|
35
|
+
if(image->dirty) {
|
36
|
+
image->image->writeMetadata();
|
37
|
+
image->dirty = false;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
static void image_leave(rbImage* image) {
|
42
|
+
}
|
43
|
+
|
44
|
+
static void image_delete(rbImage* image) {
|
45
|
+
__BEGIN
|
46
|
+
image_real_save(image);
|
47
|
+
delete image;
|
48
|
+
__VOID_END
|
49
|
+
}
|
50
|
+
PREFIX
|
51
|
+
|
52
|
+
builder.c_singleton <<-EOF, :method_name => "new"
|
53
|
+
static VALUE exiv2_image_alloc_and_initialize(VALUE file) {
|
54
|
+
__BEGIN
|
55
|
+
rbImage* image = new rbImage();
|
56
|
+
VALUE rb_image = Data_Wrap_Struct(self, 0, image_delete, image);
|
57
|
+
image->dirty = false;
|
58
|
+
|
59
|
+
|
60
|
+
try {
|
61
|
+
if(!strcmp(rb_class2name(rb_class_of(file)), "StringIO")) {
|
62
|
+
rb_iv_set(rb_image, "@content", file);
|
63
|
+
VALUE string = rb_funcall(file, rb_intern("string"), 0);
|
64
|
+
image->image = Exiv2::ImageFactory::open(CBSTR(string), LEN(string));
|
65
|
+
} else if(rb_respond_to(file, rb_intern("read"))) {
|
66
|
+
VALUE file_content = rb_funcall(file, rb_intern("read"), 0);
|
67
|
+
rb_iv_set(rb_image, "@file_content", file_content);
|
68
|
+
image->image = Exiv2::ImageFactory::open(CBSTR(file_content), LEN(file_content));
|
69
|
+
} else if(TYPE(file) == T_STRING) {
|
70
|
+
image->image = Exiv2::ImageFactory::open(CSTR(file));
|
71
|
+
}
|
72
|
+
image->image->readMetadata();
|
73
|
+
}
|
74
|
+
catch(const Exiv2::AnyError&) {
|
75
|
+
rb_raise(eError, "Cannot open file %s", STR(file));
|
76
|
+
}
|
77
|
+
return rb_image;
|
78
|
+
__END
|
79
|
+
}
|
80
|
+
EOF
|
81
|
+
|
82
|
+
builder.c_singleton <<-EOF, :method_name => "load_string"
|
83
|
+
static VALUE exiv2_image_load_string(VALUE string) {
|
84
|
+
__BEGIN
|
85
|
+
Check_Type(string, T_STRING);
|
86
|
+
rbImage* image = new rbImage();
|
87
|
+
image->dirty = false;
|
88
|
+
VALUE img = Data_Wrap_Struct(self, 0, image_delete, image);
|
89
|
+
|
90
|
+
image->image = Exiv2::ImageFactory::open(CBSTR(string), LEN(string));
|
91
|
+
return img;
|
92
|
+
__END
|
93
|
+
}
|
94
|
+
EOF
|
95
|
+
|
96
|
+
builder.c <<-EOF, :method_name => "save"
|
97
|
+
static VALUE exiv2_image_save() {
|
98
|
+
__BEGIN
|
99
|
+
rbImage* image;
|
100
|
+
Data_Get_Struct(self, rbImage, image);
|
101
|
+
image_real_save(image);
|
102
|
+
return self;
|
103
|
+
__END
|
104
|
+
}
|
105
|
+
EOF
|
106
|
+
|
107
|
+
builder.c <<-EOF, :method_name => "clear"
|
108
|
+
static VALUE exiv2_image_clear() {
|
109
|
+
__BEGIN
|
110
|
+
rbImage* image;
|
111
|
+
Data_Get_Struct(self, rbImage, image);
|
112
|
+
image->image->clearMetadata();
|
113
|
+
image->dirty = true;
|
114
|
+
return self;
|
115
|
+
__END
|
116
|
+
}
|
117
|
+
EOF
|
118
|
+
|
119
|
+
%w(exif iptc).each do |kind|
|
120
|
+
builder.add_to_init "rb_define_method(c, \"#{kind.classify}\", (VALUE(*)(ANYARGS))exiv2_image_#{kind}, 0);"
|
121
|
+
builder.c <<-EOF, :method_name => kind
|
122
|
+
static VALUE exiv2_image_#{kind}() {
|
123
|
+
__BEGIN
|
124
|
+
rbImage* image;
|
125
|
+
VALUE klass = rb_path2class("Exiv2::#{kind.classify}");
|
126
|
+
Data_Get_Struct(self, rbImage, image);
|
127
|
+
VALUE reader = Data_Wrap_Struct(klass, 0, image_leave, image);
|
128
|
+
rb_iv_set(reader, "@image", self);
|
129
|
+
return reader;
|
130
|
+
__END
|
131
|
+
}
|
132
|
+
EOF
|
133
|
+
end
|
134
|
+
|
135
|
+
builder.c <<-EOF, :method_name => "comment"
|
136
|
+
static VALUE exiv2_image_get_comment() {
|
137
|
+
__BEGIN
|
138
|
+
rbImage* image;
|
139
|
+
Data_Get_Struct(self, rbImage, image);
|
140
|
+
std::string comment = image->image->comment();
|
141
|
+
return rb_str_new(comment.c_str(), comment.length());
|
142
|
+
__END
|
143
|
+
}
|
144
|
+
EOF
|
145
|
+
|
146
|
+
builder.c <<-EOF, :method_name => "comment="
|
147
|
+
static VALUE exiv2_image_set_comment(VALUE comment) {
|
148
|
+
__BEGIN
|
149
|
+
rbImage* image;
|
150
|
+
Data_Get_Struct(self, rbImage, image);
|
151
|
+
switch(TYPE(comment)) {
|
152
|
+
case T_STRING: {
|
153
|
+
image->image->setComment(CSTR(comment));
|
154
|
+
image->dirty = true;
|
155
|
+
break;
|
156
|
+
}
|
157
|
+
case T_NIL: {
|
158
|
+
image->image->clearComment();
|
159
|
+
image->dirty = true;
|
160
|
+
break;
|
161
|
+
}
|
162
|
+
default: {
|
163
|
+
rb_raise(rb_eStandardError, "Can only set comment to string, or clear it with nil value");
|
164
|
+
}
|
165
|
+
}
|
166
|
+
return comment;
|
167
|
+
__END
|
168
|
+
}
|
169
|
+
EOF
|
170
|
+
|
171
|
+
builder.c <<-EOF, :method_name => "thumbnail"
|
172
|
+
static VALUE exiv2_image_thumbnail(VALUE file_name) {
|
173
|
+
__BEGIN
|
174
|
+
Check_Type(file_name, T_STRING);
|
175
|
+
|
176
|
+
rbImage* image;
|
177
|
+
Data_Get_Struct(self, rbImage, image);
|
178
|
+
|
179
|
+
Exiv2::ExifData &exifData = image->image->exifData();
|
180
|
+
// exifData.writeThumbnail(STR(file_name));
|
181
|
+
if(rb_block_given_p()) {
|
182
|
+
rb_yield(file_name);
|
183
|
+
}
|
184
|
+
return self;
|
185
|
+
__END
|
186
|
+
}
|
187
|
+
EOF
|
188
|
+
|
189
|
+
builder.c <<-EOF, :method_name => "thumbnail="
|
190
|
+
static VALUE exiv2_image_thumbnail_set(VALUE file_name) {
|
191
|
+
__BEGIN
|
192
|
+
Check_Type(file_name, T_STRING);
|
193
|
+
|
194
|
+
rbImage* image;
|
195
|
+
Data_Get_Struct(self, rbImage, image);
|
196
|
+
|
197
|
+
Exiv2::ExifData &exifData = image->image->exifData();
|
198
|
+
// exifData.setJpegThumbnail(STR(file_name));
|
199
|
+
return self;
|
200
|
+
__END
|
201
|
+
}
|
202
|
+
EOF
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
data/lib/exiv2/iptc.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Exiv2
|
2
|
+
class Iptc
|
3
|
+
inline do |builder|
|
4
|
+
kind = "Iptc"
|
5
|
+
Exiv2::prepare_builder(builder)
|
6
|
+
builder.prefix(Exiv2::load {"metadata/marshall.cpp"})
|
7
|
+
builder.prefix(Exiv2::load {"metadata/unmarshall.cpp"})
|
8
|
+
builder.prefix(Exiv2::load {"throw.cpp"})
|
9
|
+
|
10
|
+
builder.c(Exiv2::load {"metadata/get.cpp"}, {:method_name => "[]"})
|
11
|
+
builder.c(Exiv2::load {"metadata/set.cpp"}, {:method_name => "[]="})
|
12
|
+
builder.c_raw(Exiv2::load {"metadata/each.cpp"}, {:method_name => "each"})
|
13
|
+
builder.c(Exiv2::load {"metadata/delete.cpp"}, {:method_name => "delete"})
|
14
|
+
builder.c(Exiv2::load {"metadata/clear.cpp"}, {:method_name => "clear"})
|
15
|
+
builder.c(Exiv2::load {"metadata/count.cpp"}, {:method_name => "count"})
|
16
|
+
builder.c(Exiv2::load {"metadata/is_empty.cpp"}, {:method_name => "empty?"})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
static VALUE exiv2_metadata_clear() {
|
2
|
+
__BEGIN
|
3
|
+
rbImage* image;
|
4
|
+
Data_Get_Struct(self, rbImage, image);
|
5
|
+
|
6
|
+
Exiv2::<%= kind %>Data &data = image->image-><%= kind.downcase %>Data();
|
7
|
+
|
8
|
+
if(data.empty()) {
|
9
|
+
return Qnil;
|
10
|
+
}
|
11
|
+
data.clear();
|
12
|
+
return self;
|
13
|
+
__END
|
14
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
static VALUE exiv2_metadata_delete(VALUE rb_key) {
|
2
|
+
__BEGIN
|
3
|
+
rbImage* image;
|
4
|
+
Data_Get_Struct(self, rbImage, image);
|
5
|
+
|
6
|
+
VALUE strkey = rb_funcall(rb_key, rb_intern("to_s"), 0);
|
7
|
+
Exiv2::<%= kind %>Data &data = image->image-><%= kind.downcase %>Data();
|
8
|
+
|
9
|
+
if(data.empty()) {
|
10
|
+
return Qnil;
|
11
|
+
}
|
12
|
+
|
13
|
+
Exiv2::<%= kind %>Key key(STR(strkey));
|
14
|
+
Exiv2::<%= kind %>Data::iterator pos = data.findKey(key);
|
15
|
+
if (pos == data.end()) {
|
16
|
+
return Qnil;
|
17
|
+
}
|
18
|
+
|
19
|
+
std::string v = pos->toString();
|
20
|
+
data.erase(pos);
|
21
|
+
return rb_str_new(v.c_str(), v.length());
|
22
|
+
__NIL_END
|
23
|
+
}
|