TIFF file dimensions in Ruby

Using Ruby, I wanted to get the dimensions of image files without using a full-on image library. I found handy code for PNG, BMP, GIF and JPEG and adapted the JPEG code for TIFF. Saved as gist 431893.
# TIFF.rb # by Paul Schreiber <paulschreiber at gmail.com> # 09 June 2010 # # Based on JPEG.rb by Remco van’t Veer and imagesize by Keisuke Minami <keisuke at rccn.com> # http://snippets.dzone.com/posts/show/805 # http://blog.remvee.net/2005/12/02/hoogte_en_breedte_van_een_JPEG (JPEG # http://rubyforge.org/projects/imagesize/ class TIFF attr_reader :width, :height def initialize(file) if file.kind_of? IO examine(file) else File.open(file, ‘rb’) { |io| examine(io) } end end private def def_read_o(io) io.seek(0, 0) # define Singleton-method definition to IO (byte, offset) def io.read_o(length = 1, offset = nil) self.seek(offset, 0) if offset ret = self.read(length) raise “cannot read!!” unless ret ret end io end def examine(io) img_top = io.read(1024) if not (img_top[0, 4] == “MM\x00\x2a” or img_top[0, 4] == “II\x2a\x00”) raise ‘malformed TIFF’ end img_io = def_read_o(io) endian = if (img_io.read_o(4) =~ /II\x2a\x00/o) then ‘v’ else ‘n’ end # ‘v’ little-endian ‘n’ default to big-endian packspec = [ nil, # nothing (shouldn’t happen) ‘C’, # BYTE (8-bit unsigned integer) nil, # ASCII endian, # SHORT (16-bit unsigned integer) endian.upcase, # LONG (32-bit unsigned integer) nil, # RATIONAL ‘c’, # SBYTE (8-bit signed integer) nil, # UNDEFINED endian, # SSHORT (16-bit unsigned integer) endian.upcase, # SLONG (32-bit unsigned integer) ] offset = img_io.read_o(4).unpack(endian.upcase)[0] # Get offset to IFD ifd = img_io.read_o(2, offset) num_dirent = ifd.unpack(endian)[0] # Make it useful offset += 2 num_dirent = offset + (num_dirent * 12); # Calc. maximum offset of IFD ifd = width = height = nil while(width.nil? || height.nil?) ifd = img_io.read_o(12, offset) # Get first directory entry break if (ifd.nil? || (offset > num_dirent)) offset += 12 tag = ifd.unpack(endian)[0] # …and decode its tag type = ifd[2, 2].unpack(endian)[0] # …and the data type # Check the type for sanity. next if (type > packspec.size + 0) || (packspec[type].nil?) if tag == 0x0100 # Decode the value @width = ifd[8, 4].unpack(packspec[type])[0] elsif tag == 0x0101 # Decode the value @height = ifd[8, 4].unpack(packspec[type])[0] end end raise ‘malformed TIFF’ if @width.nil? || @height.nil? end # examine end # class

Leave a Reply

Your email address will not be published. Required fields are marked *