# -- coding: ASCII-8BIT -- # require 'stringio' require 'tempfile' class ImageSize class FormatError < StandardError; end class Size < Array # join using 'x' def to_s join('x') end end class ImageReader # :nodoc: attr_reader :data def initialize(data_or_io) @io = case data_or_io when IO, StringIO, Tempfile data_or_io.dup.tap(&:rewind) when String StringIO.new(data_or_io) else raise ArgumentError.new("expected instance of IO, StringIO, Tempfile or String, got #{data_or_io.class}") end @read = 0 @data = '' end def close @io.rewind @io.close if IO === @io end CHUNK = 1024 def [](offset, length) while offset + length > @read @read += CHUNK if data = @io.read(CHUNK) if data.respond_to?(:encoding) data.force_encoding(@data.encoding) end @data << data end end @data[offset, length] end end # Given path to image finds its format, width and height def self.path(path) open(path, 'rb'){ |f| new(f) } end # Used for svg def self.dpi @dpi || 72 end # Used for svg def self.dpi=(dpi) @dpi = dpi.to_f end # Given image as IO, StringIO, Tempfile or String finds its format and dimensions def initialize(data) ir = ImageReader.new(data) if @format = detect_format(ir) @width, @height = self.send("size_of_#{@format}", ir) end ir.close end # Image format attr_reader :format # Image width attr_reader :width alias :w :width # Image height attr_reader :height alias :h :height # get image width and height as an array which to_s method returns "#{width}x#{height}" def size if format Size.new([width, height]) end end private SVG_R = /]*)>/ def detect_format(ir) head = ir[0, 1024] case when head =~ /^GIF8[7,9]a/ then :gif when head[0, 8] == "\211PNG\r\n\032\n" then :png when head[0, 2] == "\377\330" then :jpeg when head[0, 2] == 'BM' then :bmp when head =~ /^P[1-7]/ then :ppm when head =~ /\#define\s+\S+\s+\d+/ then :xbm when head[0, 4] == "II*\000" then :tiff when head[0, 4] == "MM\000*" then :tiff when head =~ /\/\* XPM \*\// then :xpm when head[0, 4] == '8BPS' then :psd when head =~ /^[FC]WS/ then :swf when head[SVG_R] || head =~ /<\?xml|