exifr 1.3.6 → 1.5.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Rakefile +1 -1
- data/bin/exifr +2 -2
- data/lib/exifr/jpeg.rb +6 -6
- data/lib/exifr/tiff.rb +86 -29
- data/lib/exifr.rb +1 -1
- data/tests/data/bad_gps.exif +0 -0
- data/tests/data/exif.jpg +0 -0
- data/tests/data/ios-mspix-milliseconds.jpg +0 -0
- data/tests/data/truncated.exif +0 -0
- data/tests/jpeg_test.rb +22 -2
- data/tests/test_helper.rb +1 -1
- data/tests/tiff_test.rb +29 -4
- metadata +15 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42fbc8bb5f1d6bf15e8aff2b8b662549cf021dd0703f117c4da9be306ffe22c8
|
|
4
|
+
data.tar.gz: 212e0905aacf857cefd312fa428963a306a83be9ca53e0f572d5300fc4c79b2c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdc4e04a08191d42d37b2fa283316729dfd2295ecca28edec8aa7d32b915625ca8992c68f5a03fd84eb7e4726c595e9c2e7f44744822b2e995f9bdee9249f92a
|
|
7
|
+
data.tar.gz: '090a90bd64c694434a9fed13cb3994b1a04da04e323ee3b8600d416ad7d0df0c19040aa92806985cb7e6da1e46a22e833cc88fb07b55faf8a89cc3e1172f7908'
|
data/Gemfile
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
source
|
|
1
|
+
source "https://rubygems.org"
|
|
2
2
|
gemspec
|
data/Rakefile
CHANGED
data/bin/exifr
CHANGED
|
@@ -15,7 +15,7 @@ def pp_jpeg(fname)
|
|
|
15
15
|
l << [k, v.inspect] if v
|
|
16
16
|
end
|
|
17
17
|
ks[4..-1].each do |k|
|
|
18
|
-
v = jpeg.exif.
|
|
18
|
+
v = jpeg.exif.send(k)
|
|
19
19
|
l << [k, v.inspect] if v
|
|
20
20
|
end
|
|
21
21
|
pp(fname, l)
|
|
@@ -27,7 +27,7 @@ def pp_tiff(fname)
|
|
|
27
27
|
l = []
|
|
28
28
|
l << ['width', img.width] << ['height', img.height]
|
|
29
29
|
img.to_hash.keys.map{|a|a.to_s}.sort{|a,b|a<=>b}.each do |key|
|
|
30
|
-
l << [key, img.
|
|
30
|
+
l << [key, img.send(key).inspect]
|
|
31
31
|
end
|
|
32
32
|
pp(tiff.size == 1 ? fname : "#{fname}[#{index}]", l)
|
|
33
33
|
end
|
data/lib/exifr/jpeg.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2006-
|
|
1
|
+
# Copyright (c) 2006-2020 - R.W. van 't Veer
|
|
2
2
|
|
|
3
3
|
require 'exifr'
|
|
4
4
|
require 'exifr/tiff'
|
|
@@ -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
|
@@ -153,6 +153,9 @@ module EXIFR
|
|
|
153
153
|
0x9000 => :exif_version,
|
|
154
154
|
0x9003 => :date_time_original,
|
|
155
155
|
0x9004 => :date_time_digitized,
|
|
156
|
+
0x9010 => :offset_time,
|
|
157
|
+
0x9011 => :offset_time_original,
|
|
158
|
+
0x9012 => :offset_time_digitized,
|
|
156
159
|
0x9101 => :components_configuration,
|
|
157
160
|
0x9102 => :compressed_bits_per_pixel,
|
|
158
161
|
0x9201 => :shutter_speed_value,
|
|
@@ -331,7 +334,9 @@ module EXIFR
|
|
|
331
334
|
|
|
332
335
|
class Degrees < Array
|
|
333
336
|
def initialize(arr)
|
|
334
|
-
|
|
337
|
+
unless arr.length == 3 && arr.all?{|v| Rational === v}
|
|
338
|
+
raise "expected [degrees, minutes, seconds]; got #{arr.inspect}"
|
|
339
|
+
end
|
|
335
340
|
super
|
|
336
341
|
end
|
|
337
342
|
|
|
@@ -373,7 +378,7 @@ module EXIFR
|
|
|
373
378
|
TAGS = [TAG_MAPPING.keys, TAG_MAPPING.values.map{|v|v.values}].flatten.uniq - IFD_TAGS
|
|
374
379
|
|
|
375
380
|
# +file+ is a filename or an +IO+ object. Hint: use +StringIO+ when working with slurped data like blobs.
|
|
376
|
-
def initialize(file)
|
|
381
|
+
def initialize(file, load_thumbnails: true)
|
|
377
382
|
Data.open(file) do |data|
|
|
378
383
|
@ifds = [IFD.new(data)]
|
|
379
384
|
while ifd = @ifds.last.next
|
|
@@ -381,12 +386,21 @@ module EXIFR
|
|
|
381
386
|
@ifds << ifd
|
|
382
387
|
end
|
|
383
388
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
if load_thumbnails
|
|
390
|
+
@jpeg_thumbnails = @ifds.map do |v|
|
|
391
|
+
if v.jpeg_interchange_format && v.jpeg_interchange_format_length
|
|
392
|
+
start, length = v.jpeg_interchange_format, v.jpeg_interchange_format_length
|
|
393
|
+
if Integer === start && Integer === length
|
|
394
|
+
data[start..(start + length)]
|
|
395
|
+
else
|
|
396
|
+
EXIFR.logger.warn("Non numeric JpegInterchangeFormat data")
|
|
397
|
+
nil
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end.compact
|
|
401
|
+
else
|
|
402
|
+
@jpeg_thumbnails = []
|
|
403
|
+
end
|
|
390
404
|
end
|
|
391
405
|
end
|
|
392
406
|
|
|
@@ -470,29 +484,65 @@ module EXIFR
|
|
|
470
484
|
gps_img_direction && gps_img_direction.to_f)
|
|
471
485
|
end
|
|
472
486
|
|
|
487
|
+
# File changed date time (with UTC offset when available).
|
|
488
|
+
def date_time
|
|
489
|
+
date_time_with_zone(:date_time, :offset_time)
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# Date and time original image generated (with UTC offset when
|
|
493
|
+
# available).
|
|
494
|
+
def date_time_original
|
|
495
|
+
date_time_with_zone(:date_time_original, :offset_time_original)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# Date and time image digitized (with UTC offset when available).
|
|
499
|
+
def date_time_digitized
|
|
500
|
+
date_time_with_zone(:date_time_digitized, :offset_time_digitized)
|
|
501
|
+
end
|
|
502
|
+
|
|
473
503
|
def inspect # :nodoc:
|
|
474
504
|
@ifds.inspect
|
|
475
505
|
end
|
|
476
506
|
|
|
507
|
+
private
|
|
508
|
+
|
|
509
|
+
def date_time_with_zone(date_time_field, offset_time_field)
|
|
510
|
+
if self[date_time_field] && self[date_time_field].is_a?(Time) && self[offset_time_field]
|
|
511
|
+
Time.new(
|
|
512
|
+
self[date_time_field].year,
|
|
513
|
+
self[date_time_field].month,
|
|
514
|
+
self[date_time_field].day,
|
|
515
|
+
self[date_time_field].hour,
|
|
516
|
+
self[date_time_field].min,
|
|
517
|
+
self[date_time_field].sec,
|
|
518
|
+
self[offset_time_field]
|
|
519
|
+
)
|
|
520
|
+
else
|
|
521
|
+
self[date_time_field]
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
477
525
|
class IFD # :nodoc:
|
|
478
|
-
attr_reader :type, :fields, :offset
|
|
526
|
+
attr_reader :type, :raw_fields, :fields, :offset
|
|
479
527
|
|
|
480
528
|
def initialize(data, offset = nil, type = :image)
|
|
481
|
-
@data, @offset, @type, @fields = data, offset, type, {}
|
|
529
|
+
@data, @offset, @type, @raw_fields, @fields = data, offset, type, {}, {}
|
|
482
530
|
|
|
483
531
|
pos = offset || @data.readlong(4)
|
|
484
532
|
num = @data.readshort(pos)
|
|
485
|
-
pos += 2
|
|
486
533
|
|
|
487
|
-
num
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
534
|
+
if pos && num
|
|
535
|
+
pos += 2
|
|
536
|
+
|
|
537
|
+
num.times do
|
|
538
|
+
add_field(Field.new(@data, pos))
|
|
539
|
+
pos += 12
|
|
540
|
+
end
|
|
491
541
|
|
|
492
|
-
|
|
542
|
+
@offset_next = @data.readlong(pos)
|
|
543
|
+
end
|
|
493
544
|
rescue => ex
|
|
494
545
|
EXIFR.logger.warn("Badly formed IFD: #{ex}")
|
|
495
|
-
@offset_next = 0
|
|
496
546
|
end
|
|
497
547
|
|
|
498
548
|
def method_missing(method, *args)
|
|
@@ -520,7 +570,7 @@ module EXIFR
|
|
|
520
570
|
end
|
|
521
571
|
|
|
522
572
|
def next?
|
|
523
|
-
@offset_next
|
|
573
|
+
@offset_next && @offset_next > 0 && @offset_next < @data.size
|
|
524
574
|
end
|
|
525
575
|
|
|
526
576
|
def next
|
|
@@ -537,15 +587,16 @@ module EXIFR
|
|
|
537
587
|
|
|
538
588
|
private
|
|
539
589
|
def add_field(field)
|
|
540
|
-
return
|
|
541
|
-
|
|
590
|
+
return if @raw_fields.include?(field.tag) # first encountered value wins
|
|
591
|
+
@raw_fields[field.tag] = field.value
|
|
542
592
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
593
|
+
return unless tag = TAG_MAPPING[@type][field.tag]
|
|
594
|
+
@fields[tag] = if IFD_TAGS.include?(tag)
|
|
595
|
+
IFD.new(@data, field.offset, tag)
|
|
596
|
+
else
|
|
597
|
+
value = ADAPTERS[tag][field.value]
|
|
598
|
+
value.kind_of?(Array) && value.size == 1 ? value.first : value
|
|
599
|
+
end
|
|
549
600
|
end
|
|
550
601
|
end
|
|
551
602
|
|
|
@@ -595,6 +646,12 @@ module EXIFR
|
|
|
595
646
|
end
|
|
596
647
|
rationals
|
|
597
648
|
end
|
|
649
|
+
when 11 # float
|
|
650
|
+
len, pack = count * 4, proc { |d| d.unpack(data.float + '*') }
|
|
651
|
+
when 12 # double
|
|
652
|
+
len, pack = count * 8, proc { |d| d.unpack(data.double + '*') }
|
|
653
|
+
else
|
|
654
|
+
return
|
|
598
655
|
end
|
|
599
656
|
|
|
600
657
|
if len && pack && @type != 7
|
|
@@ -627,7 +684,7 @@ module EXIFR
|
|
|
627
684
|
end
|
|
628
685
|
|
|
629
686
|
class Data #:nodoc:
|
|
630
|
-
attr_reader :short, :long, :file
|
|
687
|
+
attr_reader :short, :long, :float, :double, :file
|
|
631
688
|
|
|
632
689
|
def initialize(file)
|
|
633
690
|
@io = file.respond_to?(:read) ? file : (@file = File.open(file, 'rb'))
|
|
@@ -635,8 +692,8 @@ module EXIFR
|
|
|
635
692
|
@pos = 0
|
|
636
693
|
|
|
637
694
|
case self[0..1]
|
|
638
|
-
when 'II'; @short, @long = 'v', 'V'
|
|
639
|
-
when 'MM'; @short, @long = 'n', 'N'
|
|
695
|
+
when 'II'; @short, @long, @float, @double = 'v', 'V', 'e', 'E'
|
|
696
|
+
when 'MM'; @short, @long, @float, @double = 'n', 'N', 'g', 'G'
|
|
640
697
|
else
|
|
641
698
|
raise MalformedTIFF, "no byte order information found"
|
|
642
699
|
end
|
data/lib/exifr.rb
CHANGED
|
Binary file
|
data/tests/data/exif.jpg
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/tests/jpeg_test.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2006-
|
|
3
|
+
# Copyright (c) 2006-2020 - R.W. van 't Veer
|
|
4
4
|
|
|
5
5
|
require 'test_helper'
|
|
6
6
|
|
|
@@ -107,7 +107,7 @@ class JPEGTest < TestCase
|
|
|
107
107
|
j = JPEG.new(f('exif.jpg'))
|
|
108
108
|
assert j.methods.include?(:date_time)
|
|
109
109
|
assert j.methods(true).include?(:date_time)
|
|
110
|
-
|
|
110
|
+
assert ! j.methods(false).include?(:date_time)
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def test_multiple_app1
|
|
@@ -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
|
|
|
@@ -151,4 +158,17 @@ class JPEGTest < TestCase
|
|
|
151
158
|
assert_equal('978', j.date_time_digitized.strftime('%L'))
|
|
152
159
|
end
|
|
153
160
|
|
|
161
|
+
def test_offset_times
|
|
162
|
+
j = JPEG.new(f('exif.jpg'))
|
|
163
|
+
assert_equal('-01:00', j.offset_time)
|
|
164
|
+
assert_equal('+01:00', j.offset_time_original)
|
|
165
|
+
assert_equal('+02:00', j.offset_time_digitized)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def test_data_time_with_utc_offset
|
|
169
|
+
j = JPEG.new(f('exif.jpg'))
|
|
170
|
+
assert_equal(Time.new(2004, 9, 9, 15, 14, 26, '-01:00'), j.date_time)
|
|
171
|
+
assert_equal(Time.new(2004, 9, 9, 15, 14, 26, '+01:00'), j.date_time_original)
|
|
172
|
+
assert_equal(Time.new(2004, 9, 9, 15, 14, 26, '+02:00'), j.date_time_digitized)
|
|
173
|
+
end
|
|
154
174
|
end
|
data/tests/test_helper.rb
CHANGED
data/tests/tiff_test.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2006-
|
|
3
|
+
# Copyright (c) 2006-2020 - R.W. van 't Veer
|
|
4
4
|
|
|
5
5
|
require 'test_helper'
|
|
6
6
|
|
|
@@ -55,7 +55,7 @@ class TIFFTest < TestCase
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def test_dates
|
|
58
|
-
(all_test_tiffs - [f('weird_date.exif'), f('plain.tif'), f('endless-loop.exif')]).each do |fname|
|
|
58
|
+
(all_test_tiffs - [f('weird_date.exif'), f('plain.tif'), f('endless-loop.exif'), f('truncated.exif')]).each do |fname|
|
|
59
59
|
assert_kind_of Time, TIFF.new(fname).date_time
|
|
60
60
|
end
|
|
61
61
|
assert_nil TIFF.new(f('weird_date.exif')).date_time
|
|
@@ -119,6 +119,10 @@ class TIFFTest < TestCase
|
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
def test_bad_gps
|
|
123
|
+
assert_nil TIFF.new(f('bad_gps.exif')).gps
|
|
124
|
+
end
|
|
125
|
+
|
|
122
126
|
def test_lens_model
|
|
123
127
|
t = TIFF.new(f('sony-a7ii.exif'))
|
|
124
128
|
assert_equal('FE 16-35mm F4 ZA OSS', t.lens_model)
|
|
@@ -161,7 +165,12 @@ class TIFFTest < TestCase
|
|
|
161
165
|
all_test_tiffs.each do |fname|
|
|
162
166
|
t = TIFF.new(fname)
|
|
163
167
|
y = YAML.dump(t)
|
|
164
|
-
|
|
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
|
|
165
174
|
end
|
|
166
175
|
end
|
|
167
176
|
|
|
@@ -179,11 +188,23 @@ class TIFFTest < TestCase
|
|
|
179
188
|
assert count > 0, 'no thumbnails found'
|
|
180
189
|
end
|
|
181
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
|
+
|
|
182
198
|
def test_should_not_loop_endlessly
|
|
183
199
|
TIFF.new(f('endless-loop.exif'))
|
|
184
200
|
assert true
|
|
185
201
|
end
|
|
186
202
|
|
|
203
|
+
def test_should_read_truncated
|
|
204
|
+
TIFF.new(f('truncated.exif'))
|
|
205
|
+
assert true
|
|
206
|
+
end
|
|
207
|
+
|
|
187
208
|
def test_user_comment
|
|
188
209
|
assert_equal("Manassas Battlefield", TIFF.new(f('user-comment.exif')).user_comment)
|
|
189
210
|
end
|
|
@@ -204,6 +225,10 @@ class TIFFTest < TestCase
|
|
|
204
225
|
t = TIFF.new(f('gopro_hd2.exif'))
|
|
205
226
|
assert t.methods.include?(:make)
|
|
206
227
|
assert t.methods(true).include?(:make)
|
|
207
|
-
|
|
228
|
+
assert ! t.methods(false).include?(:make)
|
|
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
|
|
208
233
|
end
|
|
209
234
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: exifr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- R.W. van 't Veer
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: test-unit
|
|
@@ -30,14 +29,14 @@ dependencies:
|
|
|
30
29
|
requirements:
|
|
31
30
|
- - "~>"
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
32
|
+
version: '12'
|
|
34
33
|
type: :development
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
39
|
+
version: '12'
|
|
41
40
|
description: EXIF Reader is a module to read EXIF from JPEG and TIFF images.
|
|
42
41
|
email: exifr@remworks.net
|
|
43
42
|
executables:
|
|
@@ -59,6 +58,7 @@ files:
|
|
|
59
58
|
- tests/data/Trust-DC3500_MINI.exif
|
|
60
59
|
- tests/data/apple-aperture-1.5.exif
|
|
61
60
|
- tests/data/bad-shutter_speed_value.exif
|
|
61
|
+
- tests/data/bad_gps.exif
|
|
62
62
|
- tests/data/canon-g3.exif
|
|
63
63
|
- tests/data/endless-loop.exif
|
|
64
64
|
- tests/data/exif.jpg
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- tests/data/gps-altitude.jpg
|
|
67
67
|
- tests/data/gps.exif
|
|
68
68
|
- tests/data/image.jpg
|
|
69
|
+
- tests/data/ios-mspix-milliseconds.jpg
|
|
69
70
|
- tests/data/multiple-app1.jpg
|
|
70
71
|
- tests/data/negative-exposure-bias-value.exif
|
|
71
72
|
- tests/data/nikon_d1x.tif
|
|
@@ -73,16 +74,20 @@ files:
|
|
|
73
74
|
- tests/data/plain.tif
|
|
74
75
|
- tests/data/samsung-sc-02b.jpg
|
|
75
76
|
- tests/data/sony-a7ii.exif
|
|
77
|
+
- tests/data/truncated.exif
|
|
76
78
|
- tests/data/user-comment.exif
|
|
77
79
|
- tests/data/weird_date.exif
|
|
78
80
|
- tests/jpeg_test.rb
|
|
79
81
|
- tests/test_helper.rb
|
|
80
82
|
- tests/tiff_test.rb
|
|
81
|
-
homepage: http://
|
|
83
|
+
homepage: http://codeberg.org/rwv/exifr/
|
|
82
84
|
licenses:
|
|
83
85
|
- MIT
|
|
84
|
-
metadata:
|
|
85
|
-
|
|
86
|
+
metadata:
|
|
87
|
+
bug_tracker_uri: https://codeberg.org/rwv/exifr/issues
|
|
88
|
+
changelog_uri: https://codeberg.org/rwv/exifr/raw/branch/master/CHANGELOG
|
|
89
|
+
documentation_uri: https://www.rubydoc.info/gems/exifr
|
|
90
|
+
homepage_uri: https://codeberg.org/rwv/exifr
|
|
86
91
|
rdoc_options: []
|
|
87
92
|
require_paths:
|
|
88
93
|
- lib
|
|
@@ -90,16 +95,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
90
95
|
requirements:
|
|
91
96
|
- - ">="
|
|
92
97
|
- !ruby/object:Gem::Version
|
|
93
|
-
version:
|
|
98
|
+
version: '2.0'
|
|
94
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
100
|
requirements:
|
|
96
101
|
- - ">="
|
|
97
102
|
- !ruby/object:Gem::Version
|
|
98
103
|
version: '0'
|
|
99
104
|
requirements: []
|
|
100
|
-
|
|
101
|
-
rubygems_version: 2.7.6
|
|
102
|
-
signing_key:
|
|
105
|
+
rubygems_version: 3.6.9
|
|
103
106
|
specification_version: 4
|
|
104
107
|
summary: Read EXIF from JPEG and TIFF images
|
|
105
108
|
test_files: []
|