Skip to content

Instantly share code, notes, and snippets.

@paulschreiber
Created June 9, 2010 18:12
Show Gist options
  • Save paulschreiber/431893 to your computer and use it in GitHub Desktop.
Save paulschreiber/431893 to your computer and use it in GitHub Desktop.
# 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@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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment