lib/tiff.rb in exifr-0.10.7 vs lib/tiff.rb in exifr-0.10.8
- old
+ new
@@ -1,6 +1,6 @@
-# Copyright (c) 2007, 2008 - R.W. van 't Veer
+# Copyright (c) 2007, 2008, 2009 - R.W. van 't Veer
require 'rational'
module EXIFR
# = TIFF decoder
@@ -300,26 +300,14 @@
})
# Names for all recognized TIFF fields.
TAGS = ([TAG_MAPPING.keys, TAG_MAPPING.values.map{|v|v.values}].flatten.uniq - IFD_TAGS).map{|v|v.to_s}
- # +file+ is a filename or an IO object.
+ # +file+ is a filename or an IO object. Hint: use StringIO when working with slurped data like blobs.
def initialize(file)
- data = file.respond_to?(:read) ? file.read : File.open(file, 'rb') { |io| io.read }
+ data = Data.new(file)
- class << data
- attr_accessor :short, :long
- def readshort(pos); self[pos..(pos + 1)].unpack(@short)[0]; end
- def readlong(pos); self[pos..(pos + 3)].unpack(@long)[0]; end
- end
-
- case data[0..1]
- when 'II'; data.short, data.long = 'v', 'V'
- when 'MM'; data.short, data.long = 'n', 'N'
- else; raise 'no II or MM marker found'
- end
-
@ifds = [IFD.new(data)]
while ifd = @ifds.last.next
break if @ifds.find{|i| i.offset == ifd.offset}
@ifds << ifd
end
@@ -462,12 +450,13 @@
class Field # :nodoc:
attr_reader :tag, :offset, :value
def initialize(data, pos)
@tag, count, @offset = data.readshort(pos), data.readlong(pos + 4), data.readlong(pos + 8)
+ @type = data.readshort(pos + 2)
- case data.readshort(pos + 2)
+ case @type
when 1, 6 # byte, signed byte
# TODO handle signed bytes
len, pack = count, proc { |d| d }
when 2 # ascii
len, pack = count, proc { |d| d.strip }
@@ -475,10 +464,18 @@
# TODO handle signed
len, pack = count * 2, proc { |d| d.unpack(data.short + '*') }
when 4, 9 # long, signed long
# TODO handle signed
len, pack = count * 4, proc { |d| d.unpack(data.long + '*') }
+ when 7 # undefined
+ # UserComment
+ if @tag == 0x9286
+ len, pack = count, proc { |d| d.strip }
+ len -= 8 # reduce to account for first 8 bytes
+ start = len > 4 ? @offset + 8 : (pos + 8) # UserComment first 8-bytes is char code
+ @value = [pack[data[start..(start + len - 1)]]].flatten
+ end
when 5, 10
len, pack = count * 8, proc do |d|
r = []
d.unpack(data.long + '*').each_with_index do |v,i|
i % 2 == 0 ? r << [v] : r.last << v
@@ -491,13 +488,60 @@
end
end
end
end
- if len && pack
+ if len && pack && @type != 7
start = len > 4 ? @offset : (pos + 8)
@value = [pack[data[start..(start + len - 1)]]].flatten
end
+ end
+ end
+
+ class Data #:nodoc:
+ attr_reader :short, :long
+
+ def initialize(file)
+ @file = file.respond_to?(:read) ? file : File.open(file, 'rb')
+ @buffer = ''
+ @pos = 0
+
+ case self[0..1]
+ when 'II'; @short, @long = 'v', 'V'
+ when 'MM'; @short, @long = 'n', 'N'
+ else; raise 'no II or MM marker found'
+ end
+ end
+
+ def [](pos)
+ unless pos.respond_to?(:begin) && pos.respond_to?(:end)
+ pos = pos..pos
+ end
+
+ if pos.begin < @pos || pos.end >= @pos + @buffer.size
+ read_for(pos)
+ end
+
+ @buffer[(pos.begin - @pos)..(pos.end - @pos)]
+ end
+
+ def readshort(pos)
+ self[pos..(pos + 1)].unpack(@short)[0]
+ end
+
+ def readlong(pos)
+ self[pos..(pos + 3)].unpack(@long)[0]
+ end
+
+ def size
+ @file.seek(0, IO::SEEK_END)
+ @file.pos
+ end
+
+ private
+ def read_for(pos)
+ @file.seek(@pos = pos.begin)
+ @buffer = @file.read([pos.end - pos.begin, 4096].max)
end
end
end
end