exifr 1.1.3 → 1.3.9
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 +7 -0
- data/Gemfile +2 -0
- data/Rakefile +20 -22
- data/bin/exifr +4 -1
- data/lib/exifr/jpeg.rb +28 -19
- data/lib/exifr/tiff.rb +106 -40
- data/lib/exifr.rb +6 -4
- data/tests/data/gopro_hd2.exif +0 -0
- data/tests/data/gps-altitude.jpg +0 -0
- data/tests/data/samsung-sc-02b.jpg +0 -0
- data/tests/data/sony-a7ii.exif +0 -0
- data/tests/jpeg_test.rb +58 -25
- data/tests/test_helper.rb +22 -4
- data/tests/tiff_test.rb +66 -35
- metadata +63 -35
- data/CHANGELOG +0 -108
- data/README.rdoc +0 -31
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 91531cf59588956e8b9675148197cdca2b8bcbe35edb42476bfe56f73959470b
|
|
4
|
+
data.tar.gz: ddd951e6773d11ce0a0db5214983774b8078e410451920cc441fdd55926c574e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a8ebe84816409a77d19b67d1ac8a244ef63bd3a8046e08f7193e40f8bb01c2866320aa52a3664a43ed07dc6d30c2f17005d33d2408e69574987b2e5127425b0a
|
|
7
|
+
data.tar.gz: 296a144056ee9da865e663c9ef47dfc59811e24485c133a28a2b6978b62b37f7e8419a6c76e6e6aaacb52a03b599f04e18a9715e287f89c9fbff9fbac67a2c63
|
data/Gemfile
ADDED
data/Rakefile
CHANGED
|
@@ -1,39 +1,37 @@
|
|
|
1
|
-
# Copyright (c) 2006
|
|
1
|
+
# Copyright (c) 2006-2020 - R.W. van 't Veer
|
|
2
2
|
|
|
3
|
-
require 'rake/rdoctask'
|
|
4
3
|
require 'rake/testtask'
|
|
5
4
|
|
|
6
5
|
task :default => :test
|
|
7
6
|
|
|
8
|
-
desc 'Generate site'
|
|
9
|
-
task :site => :rdoc do
|
|
10
|
-
system 'rsync -av --delete doc/ remvee@rubyforge.org:/var/www/gforge-projects/exifr'
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
Rake::RDocTask.new do |rd|
|
|
14
|
-
rd.title = 'EXIF Reader for Ruby API Documentation'
|
|
15
|
-
rd.main = "README.rdoc"
|
|
16
|
-
rd.rdoc_dir = "doc/api"
|
|
17
|
-
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
|
|
21
7
|
Rake::TestTask.new do |t|
|
|
22
8
|
t.libs << 'lib' << 'tests'
|
|
23
9
|
t.test_files = FileList['tests/*_test.rb']
|
|
10
|
+
t.warning = true
|
|
24
11
|
end
|
|
25
12
|
|
|
26
13
|
begin
|
|
27
|
-
|
|
14
|
+
begin
|
|
15
|
+
require 'rdoc/task'
|
|
16
|
+
rescue LoadError
|
|
17
|
+
require 'rake/rdoctask'
|
|
18
|
+
end
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
desc 'Generate site'
|
|
21
|
+
task :site => :rdoc do
|
|
22
|
+
system 'rsync -av --delete doc/ remvee@rubyforge.org:/var/www/gforge-projects/exifr'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Rake::RDocTask.new do |rd|
|
|
26
|
+
rd.title = 'EXIF Reader for Ruby API Documentation'
|
|
27
|
+
rd.main = "README.rdoc"
|
|
28
|
+
rd.rdoc_dir = "doc/api"
|
|
29
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
|
32
30
|
end
|
|
33
31
|
|
|
34
|
-
desc 'Remove all artifacts left by testing and packaging'
|
|
35
|
-
task :clean => [:clobber_rdoc, :clobber_rcov]
|
|
36
|
-
rescue LoadError
|
|
37
32
|
desc 'Remove all artifacts left by testing and packaging'
|
|
38
33
|
task :clean => [:clobber_rdoc]
|
|
34
|
+
|
|
35
|
+
rescue StandardError
|
|
36
|
+
nil
|
|
39
37
|
end
|
data/bin/exifr
CHANGED
data/lib/exifr/jpeg.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# Copyright (c) 2006
|
|
1
|
+
# Copyright (c) 2006-2020 - R.W. van 't Veer
|
|
2
2
|
|
|
3
3
|
require 'exifr'
|
|
4
|
+
require 'exifr/tiff'
|
|
4
5
|
require 'stringio'
|
|
6
|
+
require 'delegate'
|
|
5
7
|
|
|
6
8
|
module EXIFR
|
|
7
9
|
# = JPEG decoder
|
|
@@ -56,40 +58,47 @@ module EXIFR
|
|
|
56
58
|
# +method+ does exist for EXIF data +nil+ will be returned.
|
|
57
59
|
def method_missing(method, *args)
|
|
58
60
|
super unless args.empty?
|
|
59
|
-
super unless methods.include?(method
|
|
61
|
+
super unless methods.include?(method)
|
|
60
62
|
@exif.send method if defined?(@exif) && @exif
|
|
61
63
|
end
|
|
62
64
|
|
|
63
|
-
def respond_to?(method) # :nodoc:
|
|
64
|
-
super || methods.include?(method.
|
|
65
|
+
def respond_to?(method, include_all = false) # :nodoc:
|
|
66
|
+
super || methods.include?(method) || (include_all && private_methods.include?(method))
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
def methods # :nodoc:
|
|
68
|
-
|
|
69
|
+
def methods(regular=true) # :nodoc:
|
|
70
|
+
if regular
|
|
71
|
+
super + TIFF::TAGS << :gps
|
|
72
|
+
else
|
|
73
|
+
super
|
|
74
|
+
end
|
|
69
75
|
end
|
|
70
76
|
|
|
71
77
|
class << self
|
|
72
78
|
alias instance_methods_without_jpeg_extras instance_methods
|
|
73
79
|
def instance_methods(include_super = true) # :nodoc:
|
|
74
|
-
instance_methods_without_jpeg_extras(include_super) + TIFF::TAGS <<
|
|
80
|
+
instance_methods_without_jpeg_extras(include_super) + TIFF::TAGS << :gps
|
|
75
81
|
end
|
|
76
82
|
end
|
|
77
83
|
|
|
78
84
|
private
|
|
85
|
+
|
|
86
|
+
class Reader < SimpleDelegator
|
|
87
|
+
def readbyte; readchar; end unless File.method_defined?(:readbyte)
|
|
88
|
+
def readint; (readbyte << 8) + readbyte; end
|
|
89
|
+
def readframe; read(readint - 2); end
|
|
90
|
+
def readsof; [readint, readbyte, readint, readint, readbyte]; end
|
|
91
|
+
def next
|
|
92
|
+
c = readbyte while c != 0xFF
|
|
93
|
+
c = readbyte while c == 0xFF
|
|
94
|
+
c
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
79
98
|
def examine(io)
|
|
80
|
-
|
|
81
|
-
def readbyte; readchar; end unless method_defined?(:readbyte)
|
|
82
|
-
def readint; (readbyte << 8) + readbyte; end
|
|
83
|
-
def readframe; read(readint - 2); end
|
|
84
|
-
def readsof; [readint, readbyte, readint, readint, readbyte]; end
|
|
85
|
-
def next
|
|
86
|
-
c = readbyte while c != 0xFF
|
|
87
|
-
c = readbyte while c == 0xFF
|
|
88
|
-
c
|
|
89
|
-
end
|
|
90
|
-
end unless io.respond_to? :readsof
|
|
99
|
+
io = Reader.new(io)
|
|
91
100
|
|
|
92
|
-
unless io.
|
|
101
|
+
unless io.getbyte == 0xFF && io.getbyte == 0xD8 # SOI
|
|
93
102
|
raise MalformedJPEG, "no start of image marker found"
|
|
94
103
|
end
|
|
95
104
|
|
data/lib/exifr/tiff.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2007
|
|
1
|
+
# Copyright (c) 2007-2017 - R.W. van 't Veer
|
|
2
2
|
|
|
3
3
|
require 'exifr'
|
|
4
4
|
require 'rational'
|
|
@@ -169,7 +169,7 @@ module EXIFR
|
|
|
169
169
|
0x927c => :maker_note,
|
|
170
170
|
0x9286 => :user_comment,
|
|
171
171
|
0x9290 => :subsec_time,
|
|
172
|
-
0x9291 => :
|
|
172
|
+
0x9291 => :subsec_time_original,
|
|
173
173
|
0x9292 => :subsec_time_digitized,
|
|
174
174
|
0xa000 => :flashpix_version,
|
|
175
175
|
0xa001 => :color_space,
|
|
@@ -199,7 +199,10 @@ module EXIFR
|
|
|
199
199
|
0xa40a => :sharpness,
|
|
200
200
|
0xa40b => :device_setting_description,
|
|
201
201
|
0xa40c => :subject_distance_range,
|
|
202
|
-
0xa420 => :image_unique_id
|
|
202
|
+
0xa420 => :image_unique_id,
|
|
203
|
+
0xa433 => :lens_make,
|
|
204
|
+
0xa434 => :lens_model,
|
|
205
|
+
0xa435 => :lens_serial_number
|
|
203
206
|
},
|
|
204
207
|
|
|
205
208
|
:gps => {
|
|
@@ -234,16 +237,28 @@ module EXIFR
|
|
|
234
237
|
0x001c => :gps_area_information,
|
|
235
238
|
0x001d => :gps_date_stamp,
|
|
236
239
|
0x001e => :gps_differential,
|
|
240
|
+
0x001f => :gps_h_positioning_error
|
|
237
241
|
},
|
|
238
242
|
})
|
|
239
243
|
IFD_TAGS = [:image, :exif, :gps] # :nodoc:
|
|
240
244
|
|
|
245
|
+
class << self
|
|
246
|
+
# Callable to create a +Time+ object. Defaults to <tt>proc{|*a|Time.local(*a)}</tt>.
|
|
247
|
+
attr_accessor :mktime_proc
|
|
248
|
+
end
|
|
249
|
+
self.mktime_proc = proc { |*args| Time.local(*args) }
|
|
250
|
+
|
|
241
251
|
time_proc = proc do |value|
|
|
242
|
-
value.map do |
|
|
243
|
-
if
|
|
244
|
-
|
|
252
|
+
value.map do |v|
|
|
253
|
+
if v =~ /^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d{3}))?$/
|
|
254
|
+
begin
|
|
255
|
+
mktime_proc.call($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, $7.to_i * 1000)
|
|
256
|
+
rescue => ex
|
|
257
|
+
EXIFR.logger.warn("Bad date/time value #{v.inspect}: #{ex}")
|
|
258
|
+
nil
|
|
259
|
+
end
|
|
245
260
|
else
|
|
246
|
-
|
|
261
|
+
v
|
|
247
262
|
end
|
|
248
263
|
end
|
|
249
264
|
end
|
|
@@ -259,6 +274,11 @@ module EXIFR
|
|
|
259
274
|
@value
|
|
260
275
|
end
|
|
261
276
|
|
|
277
|
+
# Symbolic value.
|
|
278
|
+
def to_sym
|
|
279
|
+
@type
|
|
280
|
+
end
|
|
281
|
+
|
|
262
282
|
# Debugging output.
|
|
263
283
|
def inspect
|
|
264
284
|
"\#<EXIFR::TIFF::Orientation:#{@type}(#{@value})>"
|
|
@@ -300,9 +320,20 @@ module EXIFR
|
|
|
300
320
|
const_set("#{type}Orientation", ORIENTATIONS[index] = Orientation.new(index, type))
|
|
301
321
|
end
|
|
302
322
|
|
|
323
|
+
degrees_proc = proc do |v|
|
|
324
|
+
begin
|
|
325
|
+
Degrees.new(v)
|
|
326
|
+
rescue => ex
|
|
327
|
+
EXIFR.logger.warn("malformed GPS degrees: #{ex}")
|
|
328
|
+
nil
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
303
332
|
class Degrees < Array
|
|
304
333
|
def initialize(arr)
|
|
305
|
-
|
|
334
|
+
unless arr.length == 3 && arr.all?{|v| Rational === v}
|
|
335
|
+
raise "expected [degrees, minutes, seconds]; got #{arr.inspect}"
|
|
336
|
+
end
|
|
306
337
|
super
|
|
307
338
|
end
|
|
308
339
|
|
|
@@ -312,7 +343,13 @@ module EXIFR
|
|
|
312
343
|
end
|
|
313
344
|
|
|
314
345
|
def self.rational(n, d)
|
|
315
|
-
|
|
346
|
+
if d == 0
|
|
347
|
+
n.to_f / d.to_f
|
|
348
|
+
elsif Rational.respond_to?(:reduce)
|
|
349
|
+
Rational.reduce(n, d)
|
|
350
|
+
else
|
|
351
|
+
n.quo(d)
|
|
352
|
+
end
|
|
316
353
|
end
|
|
317
354
|
|
|
318
355
|
def self.round(f, n)
|
|
@@ -325,19 +362,19 @@ module EXIFR
|
|
|
325
362
|
:date_time_original => time_proc,
|
|
326
363
|
:date_time_digitized => time_proc,
|
|
327
364
|
:date_time => time_proc,
|
|
328
|
-
:orientation => proc { |
|
|
329
|
-
:gps_latitude =>
|
|
330
|
-
:gps_longitude =>
|
|
331
|
-
:gps_dest_latitude =>
|
|
332
|
-
:gps_dest_longitude =>
|
|
333
|
-
:shutter_speed_value => proc { |
|
|
334
|
-
:aperture_value => proc { |
|
|
365
|
+
:orientation => proc { |x| x.map{|y| ORIENTATIONS[y]} },
|
|
366
|
+
:gps_latitude => degrees_proc,
|
|
367
|
+
:gps_longitude => degrees_proc,
|
|
368
|
+
:gps_dest_latitude => degrees_proc,
|
|
369
|
+
:gps_dest_longitude => degrees_proc,
|
|
370
|
+
:shutter_speed_value => proc { |x| x.map { |y| y.abs < 100 ? rational(1, (2 ** y).to_i) : nil } },
|
|
371
|
+
:aperture_value => proc { |x| x.map { |y| round(1.4142 ** y, 1) } }
|
|
335
372
|
})
|
|
336
373
|
|
|
337
374
|
# Names for all recognized TIFF fields.
|
|
338
|
-
TAGS =
|
|
375
|
+
TAGS = [TAG_MAPPING.keys, TAG_MAPPING.values.map{|v|v.values}].flatten.uniq - IFD_TAGS
|
|
339
376
|
|
|
340
|
-
# +file+ is a filename or an IO object. Hint: use StringIO when working with slurped data like blobs.
|
|
377
|
+
# +file+ is a filename or an +IO+ object. Hint: use +StringIO+ when working with slurped data like blobs.
|
|
341
378
|
def initialize(file)
|
|
342
379
|
Data.open(file) do |data|
|
|
343
380
|
@ifds = [IFD.new(data)]
|
|
@@ -346,10 +383,15 @@ module EXIFR
|
|
|
346
383
|
@ifds << ifd
|
|
347
384
|
end
|
|
348
385
|
|
|
349
|
-
@jpeg_thumbnails = @ifds.map do |
|
|
350
|
-
if
|
|
351
|
-
start, length =
|
|
352
|
-
|
|
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
|
|
394
|
+
end
|
|
353
395
|
end
|
|
354
396
|
end.compact
|
|
355
397
|
end
|
|
@@ -376,21 +418,33 @@ module EXIFR
|
|
|
376
418
|
|
|
377
419
|
if @ifds.first.respond_to?(method)
|
|
378
420
|
@ifds.first.send(method)
|
|
379
|
-
elsif TAGS.include?(method
|
|
421
|
+
elsif TAGS.include?(method)
|
|
380
422
|
@ifds.first.to_hash[method]
|
|
381
423
|
else
|
|
382
424
|
super
|
|
383
425
|
end
|
|
384
426
|
end
|
|
385
427
|
|
|
386
|
-
def respond_to?(method) # :nodoc:
|
|
428
|
+
def respond_to?(method, include_all = false) # :nodoc:
|
|
387
429
|
super ||
|
|
388
|
-
(defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method)) ||
|
|
389
|
-
TAGS.include?(method
|
|
430
|
+
(defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method, include_all)) ||
|
|
431
|
+
TAGS.include?(method)
|
|
390
432
|
end
|
|
391
433
|
|
|
392
|
-
def methods # :nodoc:
|
|
393
|
-
|
|
434
|
+
def methods(regular=true) # :nodoc:
|
|
435
|
+
if regular
|
|
436
|
+
(super + TAGS + IFD.instance_methods(false)).uniq
|
|
437
|
+
else
|
|
438
|
+
super
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def encode_with(coder)
|
|
443
|
+
coder["ifds"] = @ifds
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def to_yaml_properties
|
|
447
|
+
['@ifds']
|
|
394
448
|
end
|
|
395
449
|
|
|
396
450
|
class << self
|
|
@@ -414,9 +468,12 @@ module EXIFR
|
|
|
414
468
|
# Get GPS location, altitude and image direction return nil when not available.
|
|
415
469
|
def gps
|
|
416
470
|
return nil unless gps_latitude && gps_longitude
|
|
471
|
+
|
|
472
|
+
altitude = gps_altitude.is_a?(Array) ? gps_altitude.first : gps_altitude
|
|
473
|
+
|
|
417
474
|
GPS.new(gps_latitude.to_f * (gps_latitude_ref == 'S' ? -1 : 1),
|
|
418
475
|
gps_longitude.to_f * (gps_longitude_ref == 'W' ? -1 : 1),
|
|
419
|
-
|
|
476
|
+
altitude && (altitude.to_f * (gps_altitude_ref == "\1" ? -1 : 1)),
|
|
420
477
|
gps_img_direction && gps_img_direction.to_f)
|
|
421
478
|
end
|
|
422
479
|
|
|
@@ -432,20 +489,23 @@ module EXIFR
|
|
|
432
489
|
|
|
433
490
|
pos = offset || @data.readlong(4)
|
|
434
491
|
num = @data.readshort(pos)
|
|
435
|
-
pos += 2
|
|
436
492
|
|
|
437
|
-
num
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
493
|
+
if pos && num
|
|
494
|
+
pos += 2
|
|
495
|
+
|
|
496
|
+
num.times do
|
|
497
|
+
add_field(Field.new(@data, pos))
|
|
498
|
+
pos += 12
|
|
499
|
+
end
|
|
441
500
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
501
|
+
@offset_next = @data.readlong(pos)
|
|
502
|
+
end
|
|
503
|
+
rescue => ex
|
|
504
|
+
EXIFR.logger.warn("Badly formed IFD: #{ex}")
|
|
445
505
|
end
|
|
446
506
|
|
|
447
507
|
def method_missing(method, *args)
|
|
448
|
-
super unless args.empty? && TAGS.include?(method
|
|
508
|
+
super unless args.empty? && TAGS.include?(method)
|
|
449
509
|
to_hash[method]
|
|
450
510
|
end
|
|
451
511
|
|
|
@@ -469,13 +529,17 @@ module EXIFR
|
|
|
469
529
|
end
|
|
470
530
|
|
|
471
531
|
def next?
|
|
472
|
-
@offset_next
|
|
532
|
+
@offset_next && @offset_next > 0 && @offset_next < @data.size
|
|
473
533
|
end
|
|
474
534
|
|
|
475
535
|
def next
|
|
476
536
|
IFD.new(@data, @offset_next) if next?
|
|
477
537
|
end
|
|
478
538
|
|
|
539
|
+
def encode_with(coder)
|
|
540
|
+
coder["fields"] = @fields
|
|
541
|
+
end
|
|
542
|
+
|
|
479
543
|
def to_yaml_properties
|
|
480
544
|
['@fields']
|
|
481
545
|
end
|
|
@@ -507,7 +571,7 @@ module EXIFR
|
|
|
507
571
|
when 6 # signed byte
|
|
508
572
|
len, pack = count, proc { |d| sign_byte(d) }
|
|
509
573
|
when 2 # ascii
|
|
510
|
-
len, pack = count, proc { |d| d.unpack(
|
|
574
|
+
len, pack = count, proc { |d| d.unpack('Z*') }
|
|
511
575
|
when 3 # short
|
|
512
576
|
len, pack = count * 2, proc { |d| d.unpack(data.short + '*') }
|
|
513
577
|
when 8 # signed short
|
|
@@ -540,6 +604,8 @@ module EXIFR
|
|
|
540
604
|
end
|
|
541
605
|
rationals
|
|
542
606
|
end
|
|
607
|
+
else
|
|
608
|
+
return
|
|
543
609
|
end
|
|
544
610
|
|
|
545
611
|
if len && pack && @type != 7
|
data/lib/exifr.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# Copyright (c) 2006
|
|
1
|
+
# Copyright (c) 2006-2020 - R.W. van 't Veer
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
2
4
|
|
|
3
5
|
module EXIFR
|
|
4
6
|
class MalformedImage < StandardError; end
|
|
5
7
|
class MalformedJPEG < MalformedImage; end
|
|
6
8
|
class MalformedTIFF < MalformedImage; end
|
|
7
|
-
end
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
class << self; attr_accessor :logger; end
|
|
11
|
+
self.logger = Logger.new(STDERR)
|
|
12
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/tests/jpeg_test.rb
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
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
|
|
|
7
|
-
class JPEGTest <
|
|
7
|
+
class JPEGTest < TestCase
|
|
8
8
|
def test_initialize
|
|
9
9
|
all_test_jpegs.each do |fname|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
assert_nothing_raised do
|
|
14
|
-
open(fname) { |rd| JPEG.new(rd) }
|
|
15
|
-
end
|
|
16
|
-
assert_nothing_raised do
|
|
17
|
-
JPEG.new(StringIO.new(File.read(fname)))
|
|
18
|
-
end
|
|
10
|
+
assert JPEG.new(fname)
|
|
11
|
+
open(fname) { |rd| assert JPEG.new(rd) }
|
|
12
|
+
assert JPEG.new(StringIO.new(File.read(fname)))
|
|
19
13
|
end
|
|
20
14
|
end
|
|
21
15
|
|
|
22
16
|
def test_raises_malformed_jpeg
|
|
23
|
-
|
|
17
|
+
begin
|
|
18
|
+
JPEG.new(StringIO.new(""))
|
|
19
|
+
rescue MalformedJPEG => ex
|
|
20
|
+
assert ex
|
|
21
|
+
end
|
|
22
|
+
begin
|
|
24
23
|
JPEG.new(StringIO.new("djibberish"))
|
|
24
|
+
rescue MalformedJPEG => ex
|
|
25
|
+
assert ex
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
|
|
@@ -64,8 +65,8 @@ class JPEGTest < Test::Unit::TestCase
|
|
|
64
65
|
def test_exif
|
|
65
66
|
assert ! JPEG.new(f('image.jpg')).exif?
|
|
66
67
|
assert JPEG.new(f('exif.jpg')).exif?
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
assert JPEG.new(f('exif.jpg')).exif.date_time
|
|
69
|
+
assert JPEG.new(f('exif.jpg')).exif.f_number
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def test_to_hash
|
|
@@ -82,21 +83,31 @@ class JPEGTest < Test::Unit::TestCase
|
|
|
82
83
|
|
|
83
84
|
def test_exif_dispatch
|
|
84
85
|
j = JPEG.new(f('exif.jpg'))
|
|
85
|
-
|
|
86
|
-
assert
|
|
87
|
-
assert j.methods.include?('date_time')
|
|
86
|
+
assert JPEG.instance_methods.include?(:date_time)
|
|
87
|
+
assert j.methods.include?(:date_time)
|
|
88
88
|
assert j.respond_to?(:date_time)
|
|
89
|
-
assert j.
|
|
90
|
-
assert_not_nil j.date_time
|
|
89
|
+
assert j.date_time
|
|
91
90
|
assert_kind_of Time, j.date_time
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
assert j.f_number
|
|
94
93
|
assert_kind_of Rational, j.f_number
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
def test_no_method_error
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
JPEG.new(f('image.jpg')).f_number
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
JPEG.new(f('image.jpg')).foo
|
|
101
|
+
rescue NoMethodError => ex
|
|
102
|
+
assert ex
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_methods_method
|
|
107
|
+
j = JPEG.new(f('exif.jpg'))
|
|
108
|
+
assert j.methods.include?(:date_time)
|
|
109
|
+
assert j.methods(true).include?(:date_time)
|
|
110
|
+
assert ! j.methods(false).include?(:date_time)
|
|
100
111
|
end
|
|
101
112
|
|
|
102
113
|
def test_multiple_app1
|
|
@@ -108,9 +119,7 @@ class JPEGTest < Test::Unit::TestCase
|
|
|
108
119
|
all_test_jpegs.each do |fname|
|
|
109
120
|
jpeg = JPEG.new(fname)
|
|
110
121
|
unless jpeg.thumbnail.nil?
|
|
111
|
-
|
|
112
|
-
JPEG.new(StringIO.new(jpeg.thumbnail))
|
|
113
|
-
end
|
|
122
|
+
assert JPEG.new(StringIO.new(jpeg.thumbnail))
|
|
114
123
|
count += 1
|
|
115
124
|
end
|
|
116
125
|
end
|
|
@@ -118,4 +127,28 @@ class JPEGTest < Test::Unit::TestCase
|
|
|
118
127
|
assert count > 0, 'no thumbnails found'
|
|
119
128
|
end
|
|
120
129
|
|
|
130
|
+
def test_gps_with_altitude
|
|
131
|
+
t = JPEG.new(f('gps-altitude.jpg'))
|
|
132
|
+
|
|
133
|
+
assert_equal([Rational(230, 1), Rational(0, 1), Rational(0, 1)], t.gps_altitude)
|
|
134
|
+
assert_equal(230, t.gps.altitude)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_exif_datetime_milliseconds
|
|
138
|
+
if Time.now.strftime('%L') == '%L'
|
|
139
|
+
STDERR.puts("skipping milliseconds test; not supported on this platform")
|
|
140
|
+
return
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
j = JPEG.new(f('exif.jpg'))
|
|
144
|
+
assert_equal('000', j.date_time.strftime('%L'))
|
|
145
|
+
assert_equal('000', j.date_time_original.strftime('%L'))
|
|
146
|
+
assert_equal('000', j.date_time_digitized.strftime('%L'))
|
|
147
|
+
|
|
148
|
+
j = JPEG.new(f('ios-mspix-milliseconds.jpg'))
|
|
149
|
+
assert_equal('978', j.date_time.strftime('%L'))
|
|
150
|
+
assert_equal('978', j.date_time_original.strftime('%L'))
|
|
151
|
+
assert_equal('978', j.date_time_digitized.strftime('%L'))
|
|
152
|
+
end
|
|
153
|
+
|
|
121
154
|
end
|
data/tests/test_helper.rb
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
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
|
-
require 'test/unit'
|
|
6
5
|
require 'stringio'
|
|
7
6
|
require 'pp'
|
|
8
7
|
|
|
8
|
+
TestCase = begin
|
|
9
|
+
tc = begin
|
|
10
|
+
gem 'minitest' rescue nil
|
|
11
|
+
require 'minitest/autorun'
|
|
12
|
+
case
|
|
13
|
+
when defined?(Minitest::Test) ; Minitest::Test
|
|
14
|
+
when defined?(Minitest::Unit::TestCase) ; Minitest::Unit::TestCase
|
|
15
|
+
end
|
|
16
|
+
rescue LoadError
|
|
17
|
+
# nop
|
|
18
|
+
end
|
|
19
|
+
unless tc
|
|
20
|
+
require "test/unit"
|
|
21
|
+
tc = Test::Unit::TestCase
|
|
22
|
+
end
|
|
23
|
+
tc
|
|
24
|
+
end
|
|
25
|
+
|
|
9
26
|
$:.unshift("#{File.dirname(__FILE__)}/../lib")
|
|
10
|
-
require 'exifr'
|
|
27
|
+
require 'exifr/jpeg'
|
|
28
|
+
require 'exifr/tiff'
|
|
11
29
|
include EXIFR
|
|
12
30
|
|
|
31
|
+
EXIFR.logger = Logger.new(StringIO.new)
|
|
13
32
|
|
|
14
33
|
def all_test_jpegs
|
|
15
34
|
Dir[f('*.jpg')]
|
|
16
35
|
end
|
|
17
36
|
|
|
18
|
-
|
|
19
37
|
def all_test_exifs
|
|
20
38
|
Dir[f('*.exif')]
|
|
21
39
|
end
|
data/tests/tiff_test.rb
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
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
|
|
|
7
|
-
class TIFFTest <
|
|
7
|
+
class TIFFTest < TestCase
|
|
8
8
|
def setup
|
|
9
9
|
@t = TIFF.new(f('nikon_d1x.tif'))
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def test_initialize
|
|
13
13
|
all_test_tiffs.each do |fname|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
assert_nothing_raised do
|
|
18
|
-
open(fname) { |rd| TIFF.new(rd) }
|
|
19
|
-
end
|
|
20
|
-
assert_nothing_raised do
|
|
21
|
-
TIFF.new(StringIO.new(File.read(fname)))
|
|
22
|
-
end
|
|
14
|
+
assert TIFF.new(fname)
|
|
15
|
+
open(fname) { |rd| assert TIFF.new(rd) }
|
|
16
|
+
assert TIFF.new(StringIO.new(File.read(fname)))
|
|
23
17
|
end
|
|
24
18
|
end
|
|
25
|
-
|
|
19
|
+
|
|
26
20
|
def test_raises_malformed_tiff
|
|
27
|
-
|
|
21
|
+
begin
|
|
28
22
|
TIFF.new(StringIO.new("djibberish"))
|
|
23
|
+
rescue MalformedTIFF => ex
|
|
24
|
+
assert ex
|
|
29
25
|
end
|
|
30
26
|
end
|
|
31
27
|
|
|
@@ -59,12 +55,20 @@ class TIFFTest < Test::Unit::TestCase
|
|
|
59
55
|
end
|
|
60
56
|
|
|
61
57
|
def test_dates
|
|
62
|
-
(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|
|
|
63
59
|
assert_kind_of Time, TIFF.new(fname).date_time
|
|
64
60
|
end
|
|
65
61
|
assert_nil TIFF.new(f('weird_date.exif')).date_time
|
|
66
62
|
end
|
|
67
63
|
|
|
64
|
+
def test_time_with_zone
|
|
65
|
+
old_proc = TIFF.mktime_proc
|
|
66
|
+
TIFF.mktime_proc = proc { |*args| "TIME-WITH-ZONE" }
|
|
67
|
+
assert_equal "TIME-WITH-ZONE", TIFF.new(f('nikon_d1x.tif')).date_time
|
|
68
|
+
ensure
|
|
69
|
+
TIFF.mktime_proc = old_proc
|
|
70
|
+
end
|
|
71
|
+
|
|
68
72
|
def test_orientation
|
|
69
73
|
tested = 0 # count tests because not all exif samples have an orientation field
|
|
70
74
|
all_test_exifs.each do |fname|
|
|
@@ -80,6 +84,16 @@ class TIFFTest < Test::Unit::TestCase
|
|
|
80
84
|
TIFF::RightBottomOrientation,
|
|
81
85
|
TIFF::LeftBottomOrientation
|
|
82
86
|
].any? { |c| orientation == c }, 'not an orientation'
|
|
87
|
+
assert [
|
|
88
|
+
:TopLeft,
|
|
89
|
+
:TopRight,
|
|
90
|
+
:BottomRight,
|
|
91
|
+
:BottomLeft,
|
|
92
|
+
:LeftTop,
|
|
93
|
+
:RightTop,
|
|
94
|
+
:RightBottom,
|
|
95
|
+
:LeftBottom
|
|
96
|
+
].any? { |c| orientation.to_sym == c }, 'not an orientation symbol'
|
|
83
97
|
assert orientation.respond_to?(:to_i)
|
|
84
98
|
assert orientation.respond_to?(:transform_rmagick)
|
|
85
99
|
tested += 1
|
|
@@ -98,30 +112,37 @@ class TIFFTest < Test::Unit::TestCase
|
|
|
98
112
|
assert_equal('WGS84', t.gps_map_datum)
|
|
99
113
|
assert_equal(54, t.gps.latitude.round)
|
|
100
114
|
assert_equal(-7, t.gps.longitude.round)
|
|
115
|
+
assert_nil(t.gps.altitude)
|
|
101
116
|
|
|
102
117
|
(all_test_exifs - %w(gps user-comment out-of-range negative-exposure-bias-value).map{|v| f("#{v}.exif")}).each do |fname|
|
|
103
118
|
assert_nil TIFF.new(fname).gps_version_id
|
|
104
119
|
end
|
|
105
120
|
end
|
|
106
121
|
|
|
122
|
+
def test_bad_gps
|
|
123
|
+
assert_nil TIFF.new(f('bad_gps.exif')).gps
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_lens_model
|
|
127
|
+
t = TIFF.new(f('sony-a7ii.exif'))
|
|
128
|
+
assert_equal('FE 16-35mm F4 ZA OSS', t.lens_model)
|
|
129
|
+
end
|
|
130
|
+
|
|
107
131
|
def test_ifd_dispatch
|
|
108
132
|
assert @t.respond_to?(:f_number)
|
|
109
|
-
assert @t.
|
|
110
|
-
assert
|
|
111
|
-
assert TIFF.instance_methods.include?('f_number')
|
|
133
|
+
assert @t.methods.include?(:f_number)
|
|
134
|
+
assert TIFF.instance_methods.include?(:f_number)
|
|
112
135
|
|
|
113
|
-
|
|
136
|
+
assert @t.f_number
|
|
114
137
|
assert_kind_of Rational, @t.f_number
|
|
115
|
-
|
|
138
|
+
assert @t[0].f_number
|
|
116
139
|
assert_kind_of Rational, @t[0].f_number
|
|
117
140
|
end
|
|
118
141
|
|
|
119
142
|
def test_avoid_dispatch_to_nonexistent_ifds
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
TIFF::TAGS.each { |tag| t.send(tag) }
|
|
124
|
-
end
|
|
143
|
+
all_test_tiffs.each do |fname|
|
|
144
|
+
assert t = TIFF.new(fname)
|
|
145
|
+
assert TIFF::TAGS.map { |tag| t.send(tag) }
|
|
125
146
|
end
|
|
126
147
|
end
|
|
127
148
|
|
|
@@ -135,9 +156,7 @@ class TIFFTest < Test::Unit::TestCase
|
|
|
135
156
|
end
|
|
136
157
|
|
|
137
158
|
def test_old_style
|
|
138
|
-
|
|
139
|
-
assert_not_nil @t[:f_number]
|
|
140
|
-
end
|
|
159
|
+
assert @t[:f_number]
|
|
141
160
|
end
|
|
142
161
|
|
|
143
162
|
def test_yaml_dump_and_load
|
|
@@ -155,10 +174,8 @@ class TIFFTest < Test::Unit::TestCase
|
|
|
155
174
|
all_test_tiffs.each do |fname|
|
|
156
175
|
t = TIFF.new(fname)
|
|
157
176
|
unless t.jpeg_thumbnails.empty?
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
JPEG.new(StringIO.new(n))
|
|
161
|
-
end
|
|
177
|
+
t.jpeg_thumbnails.each do |n|
|
|
178
|
+
assert JPEG.new(StringIO.new(n))
|
|
162
179
|
end
|
|
163
180
|
count += 1
|
|
164
181
|
end
|
|
@@ -171,17 +188,31 @@ class TIFFTest < Test::Unit::TestCase
|
|
|
171
188
|
assert true
|
|
172
189
|
end
|
|
173
190
|
|
|
191
|
+
def test_should_read_truncated
|
|
192
|
+
TIFF.new(f('truncated.exif'))
|
|
193
|
+
assert true
|
|
194
|
+
end
|
|
195
|
+
|
|
174
196
|
def test_user_comment
|
|
175
197
|
assert_equal("Manassas Battlefield", TIFF.new(f('user-comment.exif')).user_comment)
|
|
176
198
|
end
|
|
177
199
|
|
|
178
200
|
def test_handle_out_of_range_offset
|
|
179
|
-
|
|
180
|
-
assert 'NIKON', TIFF.new(f('out-of-range.exif')).make
|
|
181
|
-
end
|
|
201
|
+
assert_equal 'NIKON', TIFF.new(f('out-of-range.exif')).make
|
|
182
202
|
end
|
|
183
|
-
|
|
203
|
+
|
|
184
204
|
def test_negative_exposure_bias_value
|
|
185
205
|
assert_equal(-1.quo(3), TIFF.new(f('negative-exposure-bias-value.exif')).exposure_bias_value)
|
|
186
206
|
end
|
|
207
|
+
|
|
208
|
+
def test_nul_terminated_strings
|
|
209
|
+
assert_equal 'GoPro', TIFF.new(f('gopro_hd2.exif')).make
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def test_methods_method
|
|
213
|
+
t = TIFF.new(f('gopro_hd2.exif'))
|
|
214
|
+
assert t.methods.include?(:make)
|
|
215
|
+
assert t.methods(true).include?(:make)
|
|
216
|
+
assert ! t.methods(false).include?(:make)
|
|
217
|
+
end
|
|
187
218
|
end
|
metadata
CHANGED
|
@@ -1,81 +1,109 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: exifr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
5
|
-
prerelease:
|
|
4
|
+
version: 1.3.9
|
|
6
5
|
platform: ruby
|
|
7
6
|
authors:
|
|
8
7
|
- R.W. van 't Veer
|
|
9
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
13
|
-
dependencies:
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
date: 2020-10-19 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: test-unit
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 3.1.5
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 3.1.5
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '12'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '12'
|
|
41
|
+
description: EXIF Reader is a module to read EXIF from JPEG and TIFF images.
|
|
42
|
+
email: exifr@remworks.net
|
|
16
43
|
executables:
|
|
17
44
|
- exifr
|
|
18
45
|
extensions: []
|
|
19
|
-
extra_rdoc_files:
|
|
20
|
-
- README.rdoc
|
|
21
|
-
- CHANGELOG
|
|
46
|
+
extra_rdoc_files: []
|
|
22
47
|
files:
|
|
48
|
+
- Gemfile
|
|
23
49
|
- Rakefile
|
|
24
50
|
- bin/exifr
|
|
25
51
|
- lib/exifr.rb
|
|
26
52
|
- lib/exifr/jpeg.rb
|
|
27
53
|
- lib/exifr/tiff.rb
|
|
28
54
|
- tests/data/1x1.jpg
|
|
29
|
-
- tests/data/apple-aperture-1.5.exif
|
|
30
|
-
- tests/data/canon-g3.exif
|
|
31
55
|
- tests/data/Canon_PowerShot_A85.exif
|
|
32
56
|
- tests/data/Casio-EX-S20.exif
|
|
57
|
+
- tests/data/FUJIFILM-FinePix_S3000.exif
|
|
58
|
+
- tests/data/Panasonic-DMC-LC33.exif
|
|
59
|
+
- tests/data/Trust-DC3500_MINI.exif
|
|
60
|
+
- tests/data/apple-aperture-1.5.exif
|
|
61
|
+
- tests/data/bad-shutter_speed_value.exif
|
|
62
|
+
- tests/data/canon-g3.exif
|
|
33
63
|
- tests/data/endless-loop.exif
|
|
34
64
|
- tests/data/exif.jpg
|
|
35
|
-
- tests/data/
|
|
65
|
+
- tests/data/gopro_hd2.exif
|
|
66
|
+
- tests/data/gps-altitude.jpg
|
|
36
67
|
- tests/data/gps.exif
|
|
37
68
|
- tests/data/image.jpg
|
|
38
69
|
- tests/data/multiple-app1.jpg
|
|
39
70
|
- tests/data/negative-exposure-bias-value.exif
|
|
40
71
|
- tests/data/nikon_d1x.tif
|
|
41
72
|
- tests/data/out-of-range.exif
|
|
42
|
-
- tests/data/Panasonic-DMC-LC33.exif
|
|
43
73
|
- tests/data/plain.tif
|
|
44
|
-
- tests/data/
|
|
74
|
+
- tests/data/samsung-sc-02b.jpg
|
|
75
|
+
- tests/data/sony-a7ii.exif
|
|
45
76
|
- tests/data/user-comment.exif
|
|
46
77
|
- tests/data/weird_date.exif
|
|
47
|
-
- tests/data/bad-shutter_speed_value.exif
|
|
48
78
|
- tests/jpeg_test.rb
|
|
49
79
|
- tests/test_helper.rb
|
|
50
80
|
- tests/tiff_test.rb
|
|
51
|
-
- README.rdoc
|
|
52
|
-
- CHANGELOG
|
|
53
81
|
homepage: http://github.com/remvee/exifr/
|
|
54
|
-
licenses:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
licenses:
|
|
83
|
+
- MIT
|
|
84
|
+
metadata:
|
|
85
|
+
bug_tracker_uri: https://github.com/remvee/exifr/issues
|
|
86
|
+
changelog_uri: https://github.com/remvee/exifr/blob/master/CHANGELOG
|
|
87
|
+
documentation_uri: https://remvee.github.io/exifr/api/
|
|
88
|
+
homepage_uri: https://remvee.github.io/exifr/
|
|
89
|
+
source_code_uri: https://github.com/remvee/exifr
|
|
90
|
+
post_install_message:
|
|
91
|
+
rdoc_options: []
|
|
61
92
|
require_paths:
|
|
62
93
|
- lib
|
|
63
94
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
-
none: false
|
|
65
95
|
requirements:
|
|
66
|
-
- -
|
|
96
|
+
- - ">="
|
|
67
97
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
98
|
+
version: 1.8.7
|
|
69
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
-
none: false
|
|
71
100
|
requirements:
|
|
72
|
-
- -
|
|
101
|
+
- - ">="
|
|
73
102
|
- !ruby/object:Gem::Version
|
|
74
103
|
version: '0'
|
|
75
104
|
requirements: []
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
summary: EXIF Reader is a module to read EXIF from JPEG images.
|
|
105
|
+
rubygems_version: 3.1.4
|
|
106
|
+
signing_key:
|
|
107
|
+
specification_version: 4
|
|
108
|
+
summary: Read EXIF from JPEG and TIFF images
|
|
81
109
|
test_files: []
|
data/CHANGELOG
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
EXIF Reader 1.1.3
|
|
2
|
-
* bug fix; "[GH#27] some error when :shutter_speed_value is too big"; thanks to zamia
|
|
3
|
-
|
|
4
|
-
EXIF Reader 1.1.2
|
|
5
|
-
* bug fix; "[GH#25] Incorrect values being extracted for a number of fields"; thanks to Matthew McEachen
|
|
6
|
-
|
|
7
|
-
EXIF Reader 1.1.1
|
|
8
|
-
* feature; "added gps convenience method to make accessing location data easier (degrees to float conversion)"
|
|
9
|
-
* bug fix; "[GH#22] Fix warnings about uninitialized @comment"; thanks to Ryan Greenberg"
|
|
10
|
-
|
|
11
|
-
EXIF Reader 1.0.6
|
|
12
|
-
* bug fix: "[GH#20] `readlong': undefined method `unpack' for nil:NilClass (NoMethodError)"
|
|
13
|
-
|
|
14
|
-
EXIF Reader 1.0.5
|
|
15
|
-
* bug fix: "[GH#19] files opened by TIFF initialize were not being closed"; thanks to Joe Van Overberghe
|
|
16
|
-
|
|
17
|
-
EXIF Reader 1.0.4
|
|
18
|
-
* bug fix: "[GH#17] avoid library search path pollution"
|
|
19
|
-
* enhancement: "[GH#18] add EXIFR::JPEG#app1s method"
|
|
20
|
-
|
|
21
|
-
EXIF Reader 1.0.3
|
|
22
|
-
* enhancement; "raise specific exception to allow better error handling"; thanks to James Miller
|
|
23
|
-
|
|
24
|
-
EXIF Reader 1.0.2
|
|
25
|
-
* bug fix; "[GH#9/12] no block given"; thanks to Ian Leitch
|
|
26
|
-
|
|
27
|
-
EXIF Reader 1.0.1
|
|
28
|
-
* bug fix; "[GH#7] Unable to properly display exposure_bias_value"; thanks to John Krueger
|
|
29
|
-
|
|
30
|
-
EXIF Reader 1.0.0
|
|
31
|
-
* bug fix; "ArgumentError: invalid byte sequence in UTF-8" when reading messy field using Ruby 1.9+
|
|
32
|
-
* enhancement; "[GH#4] more informative inspect for EXIFR::TIFF::Orientation instance"
|
|
33
|
-
* bug fix; "[GH#6] ERROR RuntimeError: can't add a new key into hash during iteration"; thanks to KISHIMOTO, Makoto
|
|
34
|
-
|
|
35
|
-
EXIF Reader 0.10.9
|
|
36
|
-
* bug fix; "[GH#1] user_comment returns nil for jpeg with UserComment"; thanks to Mark Lundquist
|
|
37
|
-
* bug fix; "[GH#2] Nil pointer in tiff.rb"
|
|
38
|
-
* enhancement; "don't read entire files into memory"; thanks to Victor Bogado
|
|
39
|
-
|
|
40
|
-
EXIF Reader 0.10.8
|
|
41
|
-
* feature request; "[#23694] The object interface of JPEG is different from the TIFF one."
|
|
42
|
-
|
|
43
|
-
EXIF Reader 0.10.7
|
|
44
|
-
* bug fix; "[#22403] Wrong file size reported"
|
|
45
|
-
|
|
46
|
-
EXIF Reader 0.10.6.1
|
|
47
|
-
* moved to GitHub
|
|
48
|
-
|
|
49
|
-
EXIF Reader 0.10.6
|
|
50
|
-
* bug fix (thanks to Forian Munz for reporting it); endless loop when reading a malformed EXIF/TIFF
|
|
51
|
-
|
|
52
|
-
EXIF Reader 0.10.5
|
|
53
|
-
* bug fix; "[#15421] duplicate orientation field behavior", first field (of any type) is leading now
|
|
54
|
-
* Ruby 1.9 compatible
|
|
55
|
-
|
|
56
|
-
EXIF Reader 0.10.4
|
|
57
|
-
* Thumbnail extraction; [#15317] Please add thumbnail extraction
|
|
58
|
-
|
|
59
|
-
EXIF Reader 0.10.3
|
|
60
|
-
* YAML friendly; can now safely (de)serialize
|
|
61
|
-
|
|
62
|
-
EXIF Reader 0.10.2
|
|
63
|
-
* bug fix (thanks to Alexander Staubo for providing me with sample data);
|
|
64
|
-
don't fail on out-of-range IFD offsets for Apple Aperture generated JPGs
|
|
65
|
-
|
|
66
|
-
EXIF Reader 0.10.1
|
|
67
|
-
* old style exif access
|
|
68
|
-
|
|
69
|
-
EXIF Reader 0.10
|
|
70
|
-
* TIFF support
|
|
71
|
-
|
|
72
|
-
EXIF Reader 0.9.6
|
|
73
|
-
* bug fix; "[#8458] Conversion from string to Time fails", weird dates will now reflect nil
|
|
74
|
-
|
|
75
|
-
EXIF Reader 0.9.5.1
|
|
76
|
-
* make tinderbox happy by hiding rcov task
|
|
77
|
-
|
|
78
|
-
EXIF Reader 0.9.5
|
|
79
|
-
* patch calls to jpeg through to exif, i.e. jpeg., i.e. jpeg.model == jpeg.exif.model
|
|
80
|
-
* fix exifr commandline utility, needs require 'exifr' now
|
|
81
|
-
* improve test helper
|
|
82
|
-
* reduce size of test images
|
|
83
|
-
* include tests for tinderbox
|
|
84
|
-
|
|
85
|
-
EXIF Reader 0.9.4
|
|
86
|
-
* bug fix (thanks to Benjamin Storrier for providing me with sample date);
|
|
87
|
-
multiple app1 frames will potentially overwrite EXIF tag
|
|
88
|
-
|
|
89
|
-
EXIF Reader 0.9.3
|
|
90
|
-
* bug fix; "[#4876] Unable to extract gpsinfo"
|
|
91
|
-
* one-off bug in TiffHeader found and fixed
|
|
92
|
-
* make "InteroperabilityIndex" available
|
|
93
|
-
|
|
94
|
-
EXIF Reader 0.9.2
|
|
95
|
-
* bug fix; "[#4595] EXIFR::JPEG doesn't support multiple comments", the
|
|
96
|
-
comment property of a JPEG object now contains an array instead of a string
|
|
97
|
-
when multiple COM frames are found
|
|
98
|
-
* EXIF orientation modules including RMagick code to rotate to viewable state
|
|
99
|
-
* access to thumbnail included in EXIF
|
|
100
|
-
* simple commandline utility, "exifr", to view image properties
|
|
101
|
-
* overall code improvements including documentation and tests
|
|
102
|
-
|
|
103
|
-
EXIF Reader 0.9.1
|
|
104
|
-
* bug fix; "4321 Can't create object", division by zero when
|
|
105
|
-
denominator of rational value is zero
|
|
106
|
-
|
|
107
|
-
EXIF Reader 0.9
|
|
108
|
-
* 1st release
|
data/README.rdoc
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
= EXIF Reader
|
|
2
|
-
EXIF Reader is a module to read metadata from JPEG and TIFF images.
|
|
3
|
-
|
|
4
|
-
== Examples
|
|
5
|
-
EXIFR::JPEG.new('IMG_6841.JPG').width # => 2272
|
|
6
|
-
EXIFR::JPEG.new('IMG_6841.JPG').height # => 1704
|
|
7
|
-
EXIFR::JPEG.new('IMG_6841.JPG').exif? # => true
|
|
8
|
-
EXIFR::JPEG.new('IMG_6841.JPG').model # => "Canon PowerShot G3"
|
|
9
|
-
EXIFR::JPEG.new('IMG_6841.JPG').date_time # => Fri Feb 09 16:48:54 +0100 2007
|
|
10
|
-
EXIFR::JPEG.new('IMG_6841.JPG').exposure_time.to_s # => "1/15"
|
|
11
|
-
EXIFR::JPEG.new('IMG_6841.JPG').f_number.to_f # => 2.0
|
|
12
|
-
|
|
13
|
-
EXIFR::TIFF.new('DSC_0218.TIF').width # => 3008
|
|
14
|
-
EXIFR::TIFF.new('DSC_0218.TIF')[1].width # => 160
|
|
15
|
-
EXIFR::TIFF.new('DSC_0218.TIF').model # => "NIKON D1X"
|
|
16
|
-
EXIFR::TIFF.new('DSC_0218.TIF').date_time # => Tue May 23 19:15:32 +0200 2006
|
|
17
|
-
EXIFR::TIFF.new('DSC_0218.TIF').exposure_time.to_s # => "1/100"
|
|
18
|
-
EXIFR::TIFF.new('DSC_0218.TIF').f_number.to_f # => 5.0
|
|
19
|
-
|
|
20
|
-
EXIFR::JPEG.new('enkhuizen.jpg').gps.latitude # => 52.7197888888889
|
|
21
|
-
EXIFR::JPEG.new('enkhuizen.jpg').gps.longitude # => 5.28397777777778
|
|
22
|
-
|
|
23
|
-
== XMP data access
|
|
24
|
-
If you need to access XMP data you can use the xmp gem. More info and
|
|
25
|
-
examples at https://github.com/amberbit/xmp
|
|
26
|
-
|
|
27
|
-
== Author
|
|
28
|
-
R.W. van 't Veer
|
|
29
|
-
|
|
30
|
-
== Copyright
|
|
31
|
-
Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 - R.W. van 't Veer
|