exifr 1.3.9 → 1.4.0
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.
- checksums.yaml +4 -4
- data/lib/exifr/jpeg.rb +5 -5
- data/lib/exifr/tiff.rb +33 -24
- data/tests/data/bad_gps.exif +0 -0
- data/tests/data/ios-mspix-milliseconds.jpg +0 -0
- data/tests/data/truncated.exif +0 -0
- data/tests/jpeg_test.rb +7 -0
- data/tests/tiff_test.rb +17 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2799bf290120d558fe48495596a987705abb4bc394622e459d597a746c5b57d6
|
4
|
+
data.tar.gz: e84597a7f20fa3f45219372403851390679841a27f90e50b57bf4d0aba88d1be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6eef386c693e4ba34e715dbc8c1334a48dbc2d454abeda218beed9cf4184a868089576e5253f3f592a3c0ab4efc9e0315da908332bfb962b070f0519ea549db
|
7
|
+
data.tar.gz: 421ecfff8bffbb3300b48ab7e0c670f3980ce7d39c33c079d7b53096ae7527d6ee47edaf151396979f96c78bb7fd841c31227ab66ceea9dc0590fd90f8b76c14
|
data/lib/exifr/jpeg.rb
CHANGED
@@ -29,11 +29,11 @@ module EXIFR
|
|
29
29
|
attr_reader :app1s
|
30
30
|
|
31
31
|
# +file+ is a filename or an IO object. Hint: use StringIO when working with slurped data like blobs.
|
32
|
-
def initialize(file)
|
32
|
+
def initialize(file, load_thumbnails: true)
|
33
33
|
if file.kind_of? String
|
34
|
-
File.open(file, 'rb') { |io| examine(io) }
|
34
|
+
File.open(file, 'rb') { |io| examine(io, load_thumbnails: load_thumbnails) }
|
35
35
|
else
|
36
|
-
examine(file.dup)
|
36
|
+
examine(file.dup, load_thumbnails: load_thumbnails)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -95,7 +95,7 @@ module EXIFR
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
def examine(io)
|
98
|
+
def examine(io, load_thumbnails: true)
|
99
99
|
io = Reader.new(io)
|
100
100
|
|
101
101
|
unless io.getbyte == 0xFF && io.getbyte == 0xD8 # SOI
|
@@ -121,7 +121,7 @@ module EXIFR
|
|
121
121
|
|
122
122
|
if app1 = @app1s.find { |d| d[0..5] == "Exif\0\0" }
|
123
123
|
@exif_data = app1[6..-1]
|
124
|
-
@exif = TIFF.new(StringIO.new(@exif_data))
|
124
|
+
@exif = TIFF.new(StringIO.new(@exif_data), load_thumbnails: load_thumbnails)
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
data/lib/exifr/tiff.rb
CHANGED
@@ -375,7 +375,7 @@ module EXIFR
|
|
375
375
|
TAGS = [TAG_MAPPING.keys, TAG_MAPPING.values.map{|v|v.values}].flatten.uniq - IFD_TAGS
|
376
376
|
|
377
377
|
# +file+ is a filename or an +IO+ object. Hint: use +StringIO+ when working with slurped data like blobs.
|
378
|
-
def initialize(file)
|
378
|
+
def initialize(file, load_thumbnails: true)
|
379
379
|
Data.open(file) do |data|
|
380
380
|
@ifds = [IFD.new(data)]
|
381
381
|
while ifd = @ifds.last.next
|
@@ -383,17 +383,21 @@ module EXIFR
|
|
383
383
|
@ifds << ifd
|
384
384
|
end
|
385
385
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
386
|
+
if load_thumbnails
|
387
|
+
@jpeg_thumbnails = @ifds.map do |v|
|
388
|
+
if v.jpeg_interchange_format && v.jpeg_interchange_format_length
|
389
|
+
start, length = v.jpeg_interchange_format, v.jpeg_interchange_format_length
|
390
|
+
if Integer === start && Integer === length
|
391
|
+
data[start..(start + length)]
|
392
|
+
else
|
393
|
+
EXIFR.logger.warn("Non numeric JpegInterchangeFormat data")
|
394
|
+
nil
|
395
|
+
end
|
394
396
|
end
|
395
|
-
end
|
396
|
-
|
397
|
+
end.compact
|
398
|
+
else
|
399
|
+
@jpeg_thumbnails = []
|
400
|
+
end
|
397
401
|
end
|
398
402
|
end
|
399
403
|
|
@@ -482,10 +486,10 @@ module EXIFR
|
|
482
486
|
end
|
483
487
|
|
484
488
|
class IFD # :nodoc:
|
485
|
-
attr_reader :type, :fields, :offset
|
489
|
+
attr_reader :type, :raw_fields, :fields, :offset
|
486
490
|
|
487
491
|
def initialize(data, offset = nil, type = :image)
|
488
|
-
@data, @offset, @type, @fields = data, offset, type, {}
|
492
|
+
@data, @offset, @type, @raw_fields, @fields = data, offset, type, {}, {}
|
489
493
|
|
490
494
|
pos = offset || @data.readlong(4)
|
491
495
|
num = @data.readshort(pos)
|
@@ -546,15 +550,16 @@ module EXIFR
|
|
546
550
|
|
547
551
|
private
|
548
552
|
def add_field(field)
|
549
|
-
return
|
550
|
-
|
553
|
+
return if @raw_fields.include?(field.tag) # first encountered value wins
|
554
|
+
@raw_fields[field.tag] = field.value
|
551
555
|
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
556
|
+
return unless tag = TAG_MAPPING[@type][field.tag]
|
557
|
+
@fields[tag] = if IFD_TAGS.include?(tag)
|
558
|
+
IFD.new(@data, field.offset, tag)
|
559
|
+
else
|
560
|
+
value = ADAPTERS[tag][field.value]
|
561
|
+
value.kind_of?(Array) && value.size == 1 ? value.first : value
|
562
|
+
end
|
558
563
|
end
|
559
564
|
end
|
560
565
|
|
@@ -604,6 +609,10 @@ module EXIFR
|
|
604
609
|
end
|
605
610
|
rationals
|
606
611
|
end
|
612
|
+
when 11 # float
|
613
|
+
len, pack = count * 4, proc { |d| d.unpack(data.float + '*') }
|
614
|
+
when 12 # double
|
615
|
+
len, pack = count * 8, proc { |d| d.unpack(data.double + '*') }
|
607
616
|
else
|
608
617
|
return
|
609
618
|
end
|
@@ -638,7 +647,7 @@ module EXIFR
|
|
638
647
|
end
|
639
648
|
|
640
649
|
class Data #:nodoc:
|
641
|
-
attr_reader :short, :long, :file
|
650
|
+
attr_reader :short, :long, :float, :double, :file
|
642
651
|
|
643
652
|
def initialize(file)
|
644
653
|
@io = file.respond_to?(:read) ? file : (@file = File.open(file, 'rb'))
|
@@ -646,8 +655,8 @@ module EXIFR
|
|
646
655
|
@pos = 0
|
647
656
|
|
648
657
|
case self[0..1]
|
649
|
-
when 'II'; @short, @long = 'v', 'V'
|
650
|
-
when 'MM'; @short, @long = 'n', 'N'
|
658
|
+
when 'II'; @short, @long, @float, @double = 'v', 'V', 'e', 'E'
|
659
|
+
when 'MM'; @short, @long, @float, @double = 'n', 'N', 'g', 'G'
|
651
660
|
else
|
652
661
|
raise MalformedTIFF, "no byte order information found"
|
653
662
|
end
|
Binary file
|
Binary file
|
Binary file
|
data/tests/jpeg_test.rb
CHANGED
@@ -127,6 +127,13 @@ class JPEGTest < TestCase
|
|
127
127
|
assert count > 0, 'no thumbnails found'
|
128
128
|
end
|
129
129
|
|
130
|
+
def test_skippable_thumbnail
|
131
|
+
all_test_jpegs.each do |fname|
|
132
|
+
jpeg = JPEG.new(fname, load_thumbnails: false)
|
133
|
+
assert jpeg.thumbnail.nil?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
130
137
|
def test_gps_with_altitude
|
131
138
|
t = JPEG.new(f('gps-altitude.jpg'))
|
132
139
|
|
data/tests/tiff_test.rb
CHANGED
@@ -165,7 +165,12 @@ class TIFFTest < TestCase
|
|
165
165
|
all_test_tiffs.each do |fname|
|
166
166
|
t = TIFF.new(fname)
|
167
167
|
y = YAML.dump(t)
|
168
|
-
|
168
|
+
v = if YAML.respond_to?(:unsafe_load)
|
169
|
+
YAML.unsafe_load(y)
|
170
|
+
else
|
171
|
+
YAML.load(y)
|
172
|
+
end
|
173
|
+
assert_literally_equal t.to_hash, v.to_hash
|
169
174
|
end
|
170
175
|
end
|
171
176
|
|
@@ -183,6 +188,13 @@ class TIFFTest < TestCase
|
|
183
188
|
assert count > 0, 'no thumbnails found'
|
184
189
|
end
|
185
190
|
|
191
|
+
def test_skippable_jpeg_thumbnails
|
192
|
+
all_test_tiffs.each do |fname|
|
193
|
+
t = TIFF.new(fname, load_thumbnails: false)
|
194
|
+
assert t.jpeg_thumbnails.empty?
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
186
198
|
def test_should_not_loop_endlessly
|
187
199
|
TIFF.new(f('endless-loop.exif'))
|
188
200
|
assert true
|
@@ -215,4 +227,8 @@ class TIFFTest < TestCase
|
|
215
227
|
assert t.methods(true).include?(:make)
|
216
228
|
assert ! t.methods(false).include?(:make)
|
217
229
|
end
|
230
|
+
|
231
|
+
def test_unknow_field
|
232
|
+
assert_equal [1, 1, 1, 1], TIFF.new(f('plain.tif')).first.raw_fields[0x0153] # TIFF Tag SampleFormat
|
233
|
+
end
|
218
234
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exifr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- R.W. van 't Veer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-unit
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- tests/data/Trust-DC3500_MINI.exif
|
60
60
|
- tests/data/apple-aperture-1.5.exif
|
61
61
|
- tests/data/bad-shutter_speed_value.exif
|
62
|
+
- tests/data/bad_gps.exif
|
62
63
|
- tests/data/canon-g3.exif
|
63
64
|
- tests/data/endless-loop.exif
|
64
65
|
- tests/data/exif.jpg
|
@@ -66,6 +67,7 @@ files:
|
|
66
67
|
- tests/data/gps-altitude.jpg
|
67
68
|
- tests/data/gps.exif
|
68
69
|
- tests/data/image.jpg
|
70
|
+
- tests/data/ios-mspix-milliseconds.jpg
|
69
71
|
- tests/data/multiple-app1.jpg
|
70
72
|
- tests/data/negative-exposure-bias-value.exif
|
71
73
|
- tests/data/nikon_d1x.tif
|
@@ -73,6 +75,7 @@ files:
|
|
73
75
|
- tests/data/plain.tif
|
74
76
|
- tests/data/samsung-sc-02b.jpg
|
75
77
|
- tests/data/sony-a7ii.exif
|
78
|
+
- tests/data/truncated.exif
|
76
79
|
- tests/data/user-comment.exif
|
77
80
|
- tests/data/weird_date.exif
|
78
81
|
- tests/jpeg_test.rb
|
@@ -95,14 +98,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
98
|
requirements:
|
96
99
|
- - ">="
|
97
100
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
101
|
+
version: '2.0'
|
99
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
103
|
requirements:
|
101
104
|
- - ">="
|
102
105
|
- !ruby/object:Gem::Version
|
103
106
|
version: '0'
|
104
107
|
requirements: []
|
105
|
-
rubygems_version: 3.
|
108
|
+
rubygems_version: 3.4.6
|
106
109
|
signing_key:
|
107
110
|
specification_version: 4
|
108
111
|
summary: Read EXIF from JPEG and TIFF images
|