class FastImage
def self.size(uri, options={})
If set to true causes an exception to be raised if the image size cannot be found for any reason.
[:raise_on_failure]
Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
[:timeout]
=== Supported options
=> raises FastImage::SizeNotFound
FastImage.size("http://stephensykes.com/images/faulty.jpg", :raise_on_failure=>true)
=> raises FastImage::ImageFetchFailure
FastImage.size("http://stephensykes.com/favicon.ico", :raise_on_failure=>true, :timeout=>0.01)
=> raises FastImage::UnknownImageType
FastImage.size("http://stephensykes.com/favicon.ico", :raise_on_failure=>true)
=> raises FastImage::ImageFetchFailure
FastImage.size("http://pennysmalls.com/does_not_exist", :raise_on_failure=>true)
=> nil
FastImage.size("http://pennysmalls.com/does_not_exist")
=> [882, 470]
FastImage.size("test/fixtures/test.jpg")
=> [512, 512]
FastImage.size("http://www-ece.rice.edu/~wakin/images/lena512.bmp")
=> [500, 375]
FastImage.size("http://farm4.static.flickr.com/3023/3047236863_9dce98b836.jpg")
=> [16, 16]
FastImage.size("http://stephensykes.com/images/pngimage")
=> [266, 56]
FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
require 'fastimage'
=== Example
FastImage knows about GIF, JPEG, BMP and PNG files.
:raise_on_failure => true in the options.
If you wish FastImage to raise if it cannot size the image for any reason, then pass
This can be changed by passing a :timeout => number_of_seconds in the options.
By default there is a timeout of 2 seconds for opening and reading from a remote server.
It will return nil if the image could not be fetched, or if the image type was not recognised.
Returns an array containing the width and height of the image.
def self.size(uri, options={}) new(uri, options).size end
def self.type(uri, options={})
If set to true causes an exception to be raised if the image type cannot be found for any reason.
[:raise_on_failure]
Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
[:timeout]
=== Supported options
=> nil
FastImage.type("http://pennysmalls.com/does_not_exist")
=> :jpeg
FastImage.type("test/fixtures/test.jpg")
=> :bmp
FastImage.type("http://www-ece.rice.edu/~wakin/images/lena512.bmp")
=> :jpeg
FastImage.type("http://farm4.static.flickr.com/3023/3047236863_9dce98b836.jpg")
=> :png
FastImage.type("http://stephensykes.com/images/pngimage")
=> :gif
FastImage.type("http://stephensykes.com/images/ss.com_x.gif")
require 'fastimage'
=== Example
:raise_on_failure => true in the options.
If you wish FastImage to raise if it cannot find the type of the image for any reason, then pass
This can be changed by passing a :timeout => number_of_seconds in the options.
By default there is a timeout of 2 seconds for opening and reading from a remote server.
It will return nil if the image could not be fetched, or if the image type was not recognised.
Returns an symbol indicating the image type fetched from a uri.
def self.type(uri, options={}) new(uri, options.merge(:type_only=>true)).type end
def fetch_using_http
def fetch_using_http setup_http @http.request_get(@parsed_uri.request_uri) do |res| raise ImageFetchFailure unless res.is_a?(Net::HTTPSuccess) res.read_body do |str| break if parse_packet(str) end end end
def fetch_using_open_uri
def fetch_using_open_uri open(@uri) do |s| while str = s.read(LocalFileChunkSize) break if parse_packet(str) end end end
def get_byte
def get_byte get_chars(1).unpack("C")[0] end
def get_chars(n)
def get_chars(n) if @strpos + n - 1 >= @str.size @unused_str = @str[@strpos..-1] raise MoreCharsNeeded else result = @str[@strpos..(@strpos + n - 1)] @strpos += n result end end
def initialize(uri, options={})
def initialize(uri, options={}) @property = options[:type_only] ? :type : :size @timeout = options[:timeout] || DefaultTimeout @uri = uri begin @parsed_uri = URI.parse(uri) rescue URI::InvalidURIError fetch_using_open_uri else if @parsed_uri.scheme == "http" || @parsed_uri.scheme == "https" fetch_using_http else fetch_using_open_uri end end raise SizeNotFound if options[:raise_on_failure] && @property == :size && !@size rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET, ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT raise ImageFetchFailure if options[:raise_on_failure] rescue NoMethodError # 1.8.7p248 can raise this due to a net/http bug raise ImageFetchFailure if options[:raise_on_failure] rescue UnknownImageType raise UnknownImageType if options[:raise_on_failure] end
def parse_packet(str)
returns true once result is achieved
def parse_packet(str) @str = (@unused_str || "") + str @strpos = 0 begin result = send("parse_#{@property}") if result instance_variable_set("@#{@property}", result) true end rescue MoreCharsNeeded false end end
def parse_size
def parse_size @type = parse_type unless @type @strpos = 0 send("parse_size_for_#{@type}") end
def parse_size_for_bmp
def parse_size_for_bmp d = get_chars(29)[14..28] d.unpack("C")[0] == 40 ? d[4..-1].unpack('LL') : d[4..8].unpack('SS') end
def parse_size_for_gif
def parse_size_for_gif get_chars(11)[6..10].unpack('SS') end
def parse_size_for_jpeg
def parse_size_for_jpeg loop do @state = case @state when nil get_chars(2) :started when :started get_byte == 0xFF ? :sof : :started when :sof c = get_byte if (0xe0..0xef).include?(c) :skipframe elsif [0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF].detect {|r| r.include? c} :readsize else :skipframe end when :skipframe @skip_chars = read_int(get_chars(2)) - 2 :do_skip when :do_skip get_chars(@skip_chars) :started when :readsize s = get_chars(7) return [read_int(s[5..6]), read_int(s[3..4])] end end end
def parse_size_for_png
def parse_size_for_png get_chars(25)[16..24].unpack('NN') end
def parse_type
def parse_type case get_chars(2) when "BM" :bmp when "GI" :gif when 0xff.chr + 0xd8.chr :jpeg when 0x89.chr + "P" :png else raise UnknownImageType end end
def read_int(str)
def read_int(str) size_bytes = str.unpack("CC") (size_bytes[0] << 8) + size_bytes[1] end
def setup_http
def setup_http @http = Net::HTTP.new(@parsed_uri.host, @parsed_uri.port) @http.use_ssl = (@parsed_uri.scheme == "https") @http.verify_mode = OpenSSL::SSL::VERIFY_NONE @http.open_timeout = @timeout @http.read_timeout = @timeout end