exifr 1.0.6 → 1.1.1
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/CHANGELOG +4 -0
- data/README.rdoc +3 -0
- data/lib/exifr/jpeg.rb +7 -7
- data/lib/exifr/tiff.rb +35 -7
- data/tests/test_helper.rb +8 -2
- data/tests/tiff_test.rb +25 -23
- metadata +5 -5
data/CHANGELOG
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
EXIF Reader 1.1.1
|
2
|
+
* feature; "added gps convenience method to make accessing location data easier (degrees to float conversion)"
|
3
|
+
* bug fix; "[GH#22] Fix warnings about uninitialized @comment"; thanks to Ryan Greenberg"
|
4
|
+
|
1
5
|
EXIF Reader 1.0.6
|
2
6
|
* bug fix: "[GH#20] `readlong': undefined method `unpack' for nil:NilClass (NoMethodError)"
|
3
7
|
|
data/README.rdoc
CHANGED
@@ -17,6 +17,9 @@ EXIF Reader is a module to read metadata from JPEG and TIFF images.
|
|
17
17
|
EXIFR::TIFF.new('DSC_0218.TIF').exposure_time.to_s # => "1/100"
|
18
18
|
EXIFR::TIFF.new('DSC_0218.TIF').f_number.to_f # => 5.0
|
19
19
|
|
20
|
+
EXIFR::JPEG.new('enkhuizen.jpg').gps.latitude # => 52.7197888888889
|
21
|
+
EXIFR::JPEG.new('enkhuizen.jpg').gps.longitude # => 5.28397777777778
|
22
|
+
|
20
23
|
== XMP data access
|
21
24
|
If you need to access XMP data you can use the xmp gem. More info and
|
22
25
|
examples at https://github.com/amberbit/xmp
|
data/lib/exifr/jpeg.rb
CHANGED
@@ -42,7 +42,7 @@ module EXIFR
|
|
42
42
|
|
43
43
|
# Return thumbnail data when available.
|
44
44
|
def thumbnail
|
45
|
-
@exif && @exif.jpeg_thumbnails && @exif.jpeg_thumbnails.first
|
45
|
+
defined?(@exif) && @exif && @exif.jpeg_thumbnails && @exif.jpeg_thumbnails.first
|
46
46
|
end
|
47
47
|
|
48
48
|
# Get a hash presentation of the image.
|
@@ -56,22 +56,22 @@ module EXIFR
|
|
56
56
|
# +method+ does exist for EXIF data +nil+ will be returned.
|
57
57
|
def method_missing(method, *args)
|
58
58
|
super unless args.empty?
|
59
|
-
super unless
|
60
|
-
@exif.send method if @exif
|
59
|
+
super unless methods.include?(method.to_s)
|
60
|
+
@exif.send method if defined?(@exif) && @exif
|
61
61
|
end
|
62
62
|
|
63
63
|
def respond_to?(method) # :nodoc:
|
64
|
-
super ||
|
64
|
+
super || methods.include?(method.to_s)
|
65
65
|
end
|
66
66
|
|
67
67
|
def methods # :nodoc:
|
68
|
-
super + TIFF::TAGS
|
68
|
+
super + TIFF::TAGS << "gps"
|
69
69
|
end
|
70
70
|
|
71
71
|
class << self
|
72
72
|
alias instance_methods_without_jpeg_extras instance_methods
|
73
73
|
def instance_methods(include_super = true) # :nodoc:
|
74
|
-
instance_methods_without_jpeg_extras(include_super) + TIFF::TAGS
|
74
|
+
instance_methods_without_jpeg_extras(include_super) + TIFF::TAGS << "gps"
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -108,7 +108,7 @@ module EXIFR
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
@comment = @comment.first if @comment && @comment.size == 1
|
111
|
+
@comment = @comment.first if defined?(@comment) && @comment && @comment.size == 1
|
112
112
|
|
113
113
|
if app1 = @app1s.find { |d| d[0..5] == "Exif\0\0" }
|
114
114
|
@exif_data = app1[6..-1]
|
data/lib/exifr/tiff.rb
CHANGED
@@ -239,10 +239,12 @@ module EXIFR
|
|
239
239
|
IFD_TAGS = [:image, :exif, :gps] # :nodoc:
|
240
240
|
|
241
241
|
time_proc = proc do |value|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
242
|
+
value.map do |value|
|
243
|
+
if value =~ /^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/
|
244
|
+
Time.mktime($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i) rescue nil
|
245
|
+
else
|
246
|
+
value
|
247
|
+
end
|
246
248
|
end
|
247
249
|
end
|
248
250
|
|
@@ -298,12 +300,27 @@ module EXIFR
|
|
298
300
|
const_set("#{type}Orientation", ORIENTATIONS[index] = Orientation.new(index, type))
|
299
301
|
end
|
300
302
|
|
303
|
+
class Degrees < Array
|
304
|
+
def initialize(arr)
|
305
|
+
raise MalformedTIFF, "expected [degrees, minutes, seconds]" unless arr.length == 3
|
306
|
+
super
|
307
|
+
end
|
308
|
+
|
309
|
+
def to_f
|
310
|
+
reduce { |m,v| m * 60 + v}.to_f / 3600
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
301
314
|
ADAPTERS = Hash.new { proc { |v| v } } # :nodoc:
|
302
315
|
ADAPTERS.merge!({
|
303
316
|
:date_time_original => time_proc,
|
304
317
|
:date_time_digitized => time_proc,
|
305
318
|
:date_time => time_proc,
|
306
|
-
:orientation => proc { |v| ORIENTATIONS[v] }
|
319
|
+
:orientation => proc { |v| v.map{|v| ORIENTATIONS[v]} },
|
320
|
+
:gps_latitude => proc { |v| Degrees.new(v) },
|
321
|
+
:gps_longitude => proc { |v| Degrees.new(v) },
|
322
|
+
:gps_dest_latitude => proc { |v| Degrees.new(v) },
|
323
|
+
:gps_dest_longitude => proc { |v| Degrees.new(v) }
|
307
324
|
})
|
308
325
|
|
309
326
|
# Names for all recognized TIFF fields.
|
@@ -357,7 +374,7 @@ module EXIFR
|
|
357
374
|
|
358
375
|
def respond_to?(method) # :nodoc:
|
359
376
|
super ||
|
360
|
-
(@ifds && @ifds.first && @ifds.first.respond_to?(method)) ||
|
377
|
+
(defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method)) ||
|
361
378
|
TAGS.include?(method.to_s)
|
362
379
|
end
|
363
380
|
|
@@ -381,6 +398,17 @@ module EXIFR
|
|
381
398
|
# Get a hash presentation of the (first) image.
|
382
399
|
def to_hash; @ifds.first.to_hash; end
|
383
400
|
|
401
|
+
GPS = Struct.new(:latitude, :longitude, :altitude, :image_direction)
|
402
|
+
|
403
|
+
# Get GPS location, altitude and image direction return nil when not available.
|
404
|
+
def gps
|
405
|
+
return nil unless gps_latitude && gps_longitude
|
406
|
+
GPS.new(gps_latitude.to_f * (gps_latitude_ref == 'S' ? -1 : 1),
|
407
|
+
gps_longitude.to_f * (gps_longitude_ref == 'W' ? -1 : 1),
|
408
|
+
gps_altitude && (gps_altitude.to_f * (gps_altitude_ref == "\1" ? -1 : 1)),
|
409
|
+
gps_img_direction && gps_img_direction.to_f)
|
410
|
+
end
|
411
|
+
|
384
412
|
def inspect # :nodoc:
|
385
413
|
@ifds.inspect
|
386
414
|
end
|
@@ -449,7 +477,7 @@ module EXIFR
|
|
449
477
|
if IFD_TAGS.include? tag
|
450
478
|
@fields[tag] = IFD.new(@data, field.offset, tag)
|
451
479
|
else
|
452
|
-
value =
|
480
|
+
value = ADAPTERS[tag][field.value]
|
453
481
|
@fields[tag] = value.kind_of?(Array) && value.size == 1 ? value.first : value
|
454
482
|
end
|
455
483
|
end
|
data/tests/test_helper.rb
CHANGED
@@ -29,11 +29,17 @@ def f(fname)
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def assert_literally_equal(expected, actual, *args)
|
32
|
-
assert_equal expected.
|
32
|
+
assert_equal expected.to_s_literally, actual.to_s_literally, *args
|
33
33
|
end
|
34
34
|
|
35
35
|
class Hash
|
36
|
-
def
|
36
|
+
def to_s_literally
|
37
37
|
keys.map{|k| k.to_s}.sort.map{|k| "#{k.inspect} => #{self[k].inspect}" }.join(', ')
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
class Object
|
42
|
+
def to_s_literally
|
43
|
+
to_s
|
44
|
+
end
|
45
|
+
end
|
data/tests/tiff_test.rb
CHANGED
@@ -30,32 +30,32 @@ class TIFFTest < Test::Unit::TestCase
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_multiple_images
|
33
|
-
assert_equal
|
33
|
+
assert_equal(2, @t.size)
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_size
|
37
|
-
assert_equal
|
38
|
-
assert_equal
|
39
|
-
assert_equal
|
40
|
-
assert_equal
|
41
|
-
assert_equal
|
42
|
-
assert_equal
|
43
|
-
assert_equal
|
44
|
-
assert_equal
|
37
|
+
assert_equal(269, @t.image_width)
|
38
|
+
assert_equal(269, @t.image_length)
|
39
|
+
assert_equal(269, @t.width)
|
40
|
+
assert_equal(269, @t.height)
|
41
|
+
assert_equal(120, @t[1].image_width)
|
42
|
+
assert_equal(160, @t[1].image_length)
|
43
|
+
assert_equal(120, @t[1].width)
|
44
|
+
assert_equal(160, @t[1].height)
|
45
45
|
|
46
46
|
@t = TIFF.new(f('plain.tif'))
|
47
|
-
assert_equal
|
48
|
-
assert_equal
|
49
|
-
assert_equal
|
50
|
-
assert_equal
|
47
|
+
assert_equal(23, @t.image_width)
|
48
|
+
assert_equal(24, @t.image_length)
|
49
|
+
assert_equal(23, @t.width)
|
50
|
+
assert_equal(24, @t.height)
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_enumerable
|
54
|
-
assert_equal
|
54
|
+
assert_equal(@t[1], @t.find { |i| i.f_number.nil? })
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_misc_fields
|
58
|
-
assert_equal
|
58
|
+
assert_equal('Canon PowerShot G3', TIFF.new(f('canon-g3.exif')).model)
|
59
59
|
end
|
60
60
|
|
61
61
|
def test_dates
|
@@ -90,12 +90,14 @@ class TIFFTest < Test::Unit::TestCase
|
|
90
90
|
|
91
91
|
def test_gps
|
92
92
|
t = TIFF.new(f('gps.exif'))
|
93
|
-
assert_equal
|
94
|
-
assert_equal
|
95
|
-
assert_equal
|
96
|
-
assert_equal
|
97
|
-
assert_equal
|
98
|
-
assert_equal
|
93
|
+
assert_equal("\2\2\0\0", t.gps_version_id)
|
94
|
+
assert_equal('N', t.gps_latitude_ref)
|
95
|
+
assert_equal('W', t.gps_longitude_ref)
|
96
|
+
assert_equal([5355537.quo(100000), 0.quo(1), 0.quo(1)], t.gps_latitude)
|
97
|
+
assert_equal([678886.quo(100000), 0.quo(1), 0.quo(1)], t.gps_longitude)
|
98
|
+
assert_equal('WGS84', t.gps_map_datum)
|
99
|
+
assert_equal(54, t.gps.latitude.round)
|
100
|
+
assert_equal(-7, t.gps.longitude.round)
|
99
101
|
|
100
102
|
(all_test_exifs - %w(gps user-comment out-of-range negative-exposure-bias-value).map{|v| f("#{v}.exif")}).each do |fname|
|
101
103
|
assert_nil TIFF.new(fname).gps_version_id
|
@@ -170,7 +172,7 @@ class TIFFTest < Test::Unit::TestCase
|
|
170
172
|
end
|
171
173
|
|
172
174
|
def test_user_comment
|
173
|
-
assert_equal
|
175
|
+
assert_equal("Manassas Battlefield", TIFF.new(f('user-comment.exif')).user_comment)
|
174
176
|
end
|
175
177
|
|
176
178
|
def test_handle_out_of_range_offset
|
@@ -180,6 +182,6 @@ class TIFFTest < Test::Unit::TestCase
|
|
180
182
|
end
|
181
183
|
|
182
184
|
def test_negative_exposure_bias_value
|
183
|
-
assert_equal
|
185
|
+
assert_equal(-1.quo(3), TIFF.new(f('negative-exposure-bias-value.exif')).exposure_bias_value)
|
184
186
|
end
|
185
187
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exifr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 1.1.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- R.W. van 't Veer
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-09-12 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|