ruby-exiv2 1.3 → 1.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/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
|
+
}
|