exifr 1.1.3 → 1.3.8
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 +104 -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 +62 -35
- metadata +64 -35
- data/CHANGELOG +0 -108
- data/README.rdoc +0 -31
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 19c3f1b08e84425d9d0b8a6e6ee11bd2e25190a49ce34d42ea99800f5030fe20
|
4
|
+
data.tar.gz: 9a834f93b1a02e87bf401b09a033b25fb8b05625b0a60a8795e9ee65e1ea064c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b4fc267041e9090a85eb5ca2a0bfc2fae3d36a72aa45ab716209e6e5e896a775957b65251587e16e7c4e3f0e3c2e491805ce226c68b9b189a8b5bdaf506ebc15
|
7
|
+
data.tar.gz: f39334aa7142a3b918edf30fa3bd6424ddc50c1906beb2c9d5bf384bc939fba35b410d2ee801410769929a29895e75b223d443af01d7acf7752fbf96adad0fa6
|
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,18 @@ 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
|
-
raise
|
334
|
+
raise "expected [degrees, minutes, seconds]" unless arr.length == 3
|
306
335
|
super
|
307
336
|
end
|
308
337
|
|
@@ -312,7 +341,13 @@ module EXIFR
|
|
312
341
|
end
|
313
342
|
|
314
343
|
def self.rational(n, d)
|
315
|
-
|
344
|
+
if d == 0
|
345
|
+
n.to_f / d.to_f
|
346
|
+
elsif Rational.respond_to?(:reduce)
|
347
|
+
Rational.reduce(n, d)
|
348
|
+
else
|
349
|
+
n.quo(d)
|
350
|
+
end
|
316
351
|
end
|
317
352
|
|
318
353
|
def self.round(f, n)
|
@@ -325,19 +360,19 @@ module EXIFR
|
|
325
360
|
:date_time_original => time_proc,
|
326
361
|
:date_time_digitized => time_proc,
|
327
362
|
: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 { |
|
363
|
+
:orientation => proc { |x| x.map{|y| ORIENTATIONS[y]} },
|
364
|
+
:gps_latitude => degrees_proc,
|
365
|
+
:gps_longitude => degrees_proc,
|
366
|
+
:gps_dest_latitude => degrees_proc,
|
367
|
+
:gps_dest_longitude => degrees_proc,
|
368
|
+
:shutter_speed_value => proc { |x| x.map { |y| y.abs < 100 ? rational(1, (2 ** y).to_i) : nil } },
|
369
|
+
:aperture_value => proc { |x| x.map { |y| round(1.4142 ** y, 1) } }
|
335
370
|
})
|
336
371
|
|
337
372
|
# Names for all recognized TIFF fields.
|
338
|
-
TAGS =
|
373
|
+
TAGS = [TAG_MAPPING.keys, TAG_MAPPING.values.map{|v|v.values}].flatten.uniq - IFD_TAGS
|
339
374
|
|
340
|
-
# +file+ is a filename or an IO object. Hint: use StringIO when working with slurped data like blobs.
|
375
|
+
# +file+ is a filename or an +IO+ object. Hint: use +StringIO+ when working with slurped data like blobs.
|
341
376
|
def initialize(file)
|
342
377
|
Data.open(file) do |data|
|
343
378
|
@ifds = [IFD.new(data)]
|
@@ -346,10 +381,15 @@ module EXIFR
|
|
346
381
|
@ifds << ifd
|
347
382
|
end
|
348
383
|
|
349
|
-
@jpeg_thumbnails = @ifds.map do |
|
350
|
-
if
|
351
|
-
start, length =
|
352
|
-
|
384
|
+
@jpeg_thumbnails = @ifds.map do |v|
|
385
|
+
if v.jpeg_interchange_format && v.jpeg_interchange_format_length
|
386
|
+
start, length = v.jpeg_interchange_format, v.jpeg_interchange_format_length
|
387
|
+
if Integer === start && Integer === length
|
388
|
+
data[start..(start + length)]
|
389
|
+
else
|
390
|
+
EXIFR.logger.warn("Non numeric JpegInterchangeFormat data")
|
391
|
+
nil
|
392
|
+
end
|
353
393
|
end
|
354
394
|
end.compact
|
355
395
|
end
|
@@ -376,21 +416,33 @@ module EXIFR
|
|
376
416
|
|
377
417
|
if @ifds.first.respond_to?(method)
|
378
418
|
@ifds.first.send(method)
|
379
|
-
elsif TAGS.include?(method
|
419
|
+
elsif TAGS.include?(method)
|
380
420
|
@ifds.first.to_hash[method]
|
381
421
|
else
|
382
422
|
super
|
383
423
|
end
|
384
424
|
end
|
385
425
|
|
386
|
-
def respond_to?(method) # :nodoc:
|
426
|
+
def respond_to?(method, include_all = false) # :nodoc:
|
387
427
|
super ||
|
388
|
-
(defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method)) ||
|
389
|
-
TAGS.include?(method
|
428
|
+
(defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method, include_all)) ||
|
429
|
+
TAGS.include?(method)
|
430
|
+
end
|
431
|
+
|
432
|
+
def methods(regular=true) # :nodoc:
|
433
|
+
if regular
|
434
|
+
(super + TAGS + IFD.instance_methods(false)).uniq
|
435
|
+
else
|
436
|
+
super
|
437
|
+
end
|
390
438
|
end
|
391
439
|
|
392
|
-
def
|
393
|
-
|
440
|
+
def encode_with(coder)
|
441
|
+
coder["ifds"] = @ifds
|
442
|
+
end
|
443
|
+
|
444
|
+
def to_yaml_properties
|
445
|
+
['@ifds']
|
394
446
|
end
|
395
447
|
|
396
448
|
class << self
|
@@ -414,9 +466,12 @@ module EXIFR
|
|
414
466
|
# Get GPS location, altitude and image direction return nil when not available.
|
415
467
|
def gps
|
416
468
|
return nil unless gps_latitude && gps_longitude
|
469
|
+
|
470
|
+
altitude = gps_altitude.is_a?(Array) ? gps_altitude.first : gps_altitude
|
471
|
+
|
417
472
|
GPS.new(gps_latitude.to_f * (gps_latitude_ref == 'S' ? -1 : 1),
|
418
473
|
gps_longitude.to_f * (gps_longitude_ref == 'W' ? -1 : 1),
|
419
|
-
|
474
|
+
altitude && (altitude.to_f * (gps_altitude_ref == "\1" ? -1 : 1)),
|
420
475
|
gps_img_direction && gps_img_direction.to_f)
|
421
476
|
end
|
422
477
|
|
@@ -432,20 +487,23 @@ module EXIFR
|
|
432
487
|
|
433
488
|
pos = offset || @data.readlong(4)
|
434
489
|
num = @data.readshort(pos)
|
435
|
-
pos += 2
|
436
490
|
|
437
|
-
num
|
438
|
-
|
439
|
-
|
440
|
-
|
491
|
+
if pos && num
|
492
|
+
pos += 2
|
493
|
+
|
494
|
+
num.times do
|
495
|
+
add_field(Field.new(@data, pos))
|
496
|
+
pos += 12
|
497
|
+
end
|
441
498
|
|
442
|
-
|
443
|
-
|
444
|
-
|
499
|
+
@offset_next = @data.readlong(pos)
|
500
|
+
end
|
501
|
+
rescue => ex
|
502
|
+
EXIFR.logger.warn("Badly formed IFD: #{ex}")
|
445
503
|
end
|
446
504
|
|
447
505
|
def method_missing(method, *args)
|
448
|
-
super unless args.empty? && TAGS.include?(method
|
506
|
+
super unless args.empty? && TAGS.include?(method)
|
449
507
|
to_hash[method]
|
450
508
|
end
|
451
509
|
|
@@ -469,13 +527,17 @@ module EXIFR
|
|
469
527
|
end
|
470
528
|
|
471
529
|
def next?
|
472
|
-
@offset_next
|
530
|
+
@offset_next && @offset_next > 0 && @offset_next < @data.size
|
473
531
|
end
|
474
532
|
|
475
533
|
def next
|
476
534
|
IFD.new(@data, @offset_next) if next?
|
477
535
|
end
|
478
536
|
|
537
|
+
def encode_with(coder)
|
538
|
+
coder["fields"] = @fields
|
539
|
+
end
|
540
|
+
|
479
541
|
def to_yaml_properties
|
480
542
|
['@fields']
|
481
543
|
end
|
@@ -507,7 +569,7 @@ module EXIFR
|
|
507
569
|
when 6 # signed byte
|
508
570
|
len, pack = count, proc { |d| sign_byte(d) }
|
509
571
|
when 2 # ascii
|
510
|
-
len, pack = count, proc { |d| d.unpack(
|
572
|
+
len, pack = count, proc { |d| d.unpack('Z*') }
|
511
573
|
when 3 # short
|
512
574
|
len, pack = count * 2, proc { |d| d.unpack(data.short + '*') }
|
513
575
|
when 8 # signed short
|
@@ -540,6 +602,8 @@ module EXIFR
|
|
540
602
|
end
|
541
603
|
rationals
|
542
604
|
end
|
605
|
+
else
|
606
|
+
return
|
543
607
|
end
|
544
608
|
|
545
609
|
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,33 @@ 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_lens_model
|
123
|
+
t = TIFF.new(f('sony-a7ii.exif'))
|
124
|
+
assert_equal('FE 16-35mm F4 ZA OSS', t.lens_model)
|
125
|
+
end
|
126
|
+
|
107
127
|
def test_ifd_dispatch
|
108
128
|
assert @t.respond_to?(:f_number)
|
109
|
-
assert @t.
|
110
|
-
assert
|
111
|
-
assert TIFF.instance_methods.include?('f_number')
|
129
|
+
assert @t.methods.include?(:f_number)
|
130
|
+
assert TIFF.instance_methods.include?(:f_number)
|
112
131
|
|
113
|
-
|
132
|
+
assert @t.f_number
|
114
133
|
assert_kind_of Rational, @t.f_number
|
115
|
-
|
134
|
+
assert @t[0].f_number
|
116
135
|
assert_kind_of Rational, @t[0].f_number
|
117
136
|
end
|
118
137
|
|
119
138
|
def test_avoid_dispatch_to_nonexistent_ifds
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
TIFF::TAGS.each { |tag| t.send(tag) }
|
124
|
-
end
|
139
|
+
all_test_tiffs.each do |fname|
|
140
|
+
assert t = TIFF.new(fname)
|
141
|
+
assert TIFF::TAGS.map { |tag| t.send(tag) }
|
125
142
|
end
|
126
143
|
end
|
127
144
|
|
@@ -135,9 +152,7 @@ class TIFFTest < Test::Unit::TestCase
|
|
135
152
|
end
|
136
153
|
|
137
154
|
def test_old_style
|
138
|
-
|
139
|
-
assert_not_nil @t[:f_number]
|
140
|
-
end
|
155
|
+
assert @t[:f_number]
|
141
156
|
end
|
142
157
|
|
143
158
|
def test_yaml_dump_and_load
|
@@ -155,10 +170,8 @@ class TIFFTest < Test::Unit::TestCase
|
|
155
170
|
all_test_tiffs.each do |fname|
|
156
171
|
t = TIFF.new(fname)
|
157
172
|
unless t.jpeg_thumbnails.empty?
|
158
|
-
|
159
|
-
|
160
|
-
JPEG.new(StringIO.new(n))
|
161
|
-
end
|
173
|
+
t.jpeg_thumbnails.each do |n|
|
174
|
+
assert JPEG.new(StringIO.new(n))
|
162
175
|
end
|
163
176
|
count += 1
|
164
177
|
end
|
@@ -171,17 +184,31 @@ class TIFFTest < Test::Unit::TestCase
|
|
171
184
|
assert true
|
172
185
|
end
|
173
186
|
|
187
|
+
def test_should_read_truncated
|
188
|
+
TIFF.new(f('truncated.exif'))
|
189
|
+
assert true
|
190
|
+
end
|
191
|
+
|
174
192
|
def test_user_comment
|
175
193
|
assert_equal("Manassas Battlefield", TIFF.new(f('user-comment.exif')).user_comment)
|
176
194
|
end
|
177
195
|
|
178
196
|
def test_handle_out_of_range_offset
|
179
|
-
|
180
|
-
assert 'NIKON', TIFF.new(f('out-of-range.exif')).make
|
181
|
-
end
|
197
|
+
assert_equal 'NIKON', TIFF.new(f('out-of-range.exif')).make
|
182
198
|
end
|
183
|
-
|
199
|
+
|
184
200
|
def test_negative_exposure_bias_value
|
185
201
|
assert_equal(-1.quo(3), TIFF.new(f('negative-exposure-bias-value.exif')).exposure_bias_value)
|
186
202
|
end
|
203
|
+
|
204
|
+
def test_nul_terminated_strings
|
205
|
+
assert_equal 'GoPro', TIFF.new(f('gopro_hd2.exif')).make
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_methods_method
|
209
|
+
t = TIFF.new(f('gopro_hd2.exif'))
|
210
|
+
assert t.methods.include?(:make)
|
211
|
+
assert t.methods(true).include?(:make)
|
212
|
+
assert ! t.methods(false).include?(:make)
|
213
|
+
end
|
187
214
|
end
|
metadata
CHANGED
@@ -1,81 +1,110 @@
|
|
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.8
|
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-09-24 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
|
-
rubyforge_project:
|
77
|
-
rubygems_version:
|
78
|
-
signing_key:
|
79
|
-
specification_version:
|
80
|
-
summary:
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 2.7.6
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: Read EXIF from JPEG and TIFF images
|
81
110
|
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
|