# encoding: BINARY # frozen_string_literal: true require 'stringio' # Determine image format and size class ImageSize class FormatError < StandardError; end # Array joining with 'x' 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 = if data_or_io.is_a?(String) StringIO.new(data_or_io) elsif data_or_io.respond_to?(:read) && data_or_io.respond_to?(:eof?) data_or_io else raise ArgumentError, "expected data as String or an object responding to read and eof?, got #{data_or_io.class}" end @data = String.new # not frozen end CHUNK = 1024 def [](offset, length) while !@io.eof? && @data.length < offset + length data = @io.read(CHUNK) break unless data data.force_encoding(@data.encoding) if data.respond_to?(:encoding) @data << data end @data[offset, length] end end # Given path to image finds its format, width and height def self.path(path) File.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 any class responding to read and eof? or data as String, finds its format and dimensions def initialize(data) ir = ImageReader.new(data) @format = detect_format(ir) return unless @format @width, @height = send("size_of_#{@format}", ir) end # Image format attr_reader :format # Image width attr_reader :width alias_method :w, :width # Image height attr_reader :height alias_method :h, :height # get image width and height as an array which to_s method returns "#{width}x#{height}" def size Size.new([width, height]) if format end private SVG_R = /]*)>/.freeze XML_R = /<\?xml|