exifr 1.1.3 → 1.3.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|