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