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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91531cf59588956e8b9675148197cdca2b8bcbe35edb42476bfe56f73959470b
4
- data.tar.gz: ddd951e6773d11ce0a0db5214983774b8078e410451920cc441fdd55926c574e
3
+ metadata.gz: 2799bf290120d558fe48495596a987705abb4bc394622e459d597a746c5b57d6
4
+ data.tar.gz: e84597a7f20fa3f45219372403851390679841a27f90e50b57bf4d0aba88d1be
5
5
  SHA512:
6
- metadata.gz: a8ebe84816409a77d19b67d1ac8a244ef63bd3a8046e08f7193e40f8bb01c2866320aa52a3664a43ed07dc6d30c2f17005d33d2408e69574987b2e5127425b0a
7
- data.tar.gz: 296a144056ee9da865e663c9ef47dfc59811e24485c133a28a2b6978b62b37f7e8419a6c76e6e6aaacb52a03b599f04e18a9715e287f89c9fbff9fbac67a2c63
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
- @jpeg_thumbnails = @ifds.map do |v|
387
- if v.jpeg_interchange_format && v.jpeg_interchange_format_length
388
- start, length = v.jpeg_interchange_format, v.jpeg_interchange_format_length
389
- if Integer === start && Integer === length
390
- data[start..(start + length)]
391
- else
392
- EXIFR.logger.warn("Non numeric JpegInterchangeFormat data")
393
- nil
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
- end.compact
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 unless tag = TAG_MAPPING[@type][field.tag]
550
- return if @fields[tag]
553
+ return if @raw_fields.include?(field.tag) # first encountered value wins
554
+ @raw_fields[field.tag] = field.value
551
555
 
552
- if IFD_TAGS.include? tag
553
- @fields[tag] = IFD.new(@data, field.offset, tag)
554
- else
555
- value = ADAPTERS[tag][field.value]
556
- @fields[tag] = value.kind_of?(Array) && value.size == 1 ? value.first : value
557
- end
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
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
- assert_literally_equal t.to_hash, YAML.load(y).to_hash
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.3.9
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: 2020-10-19 00:00:00.000000000 Z
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: 1.8.7
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.1.4
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