=begin

== NAME

imagesize.rb -- A image size scanner for JPEG, GIF, PNG file

Version 1.0.2  Mar. 22 2005

== COPYRIGHT

Copyright (c) 2004 MIZUTANI Tociyuki All Rights Reserved.

This is a free software.
You can distribute/modify this under the terms of the same as Ruby.

== CHANGES

:Version 1.0.2 Mar. 22 2005
  Fix incorrect explanations for #h and #v in PROPERTIES.
  Codes are not changed.

:Version 1.0.1 Apr. 26 2004
  Correct factory pattern.

:Version 1.0.0 Apr. 25 2004
  First publishing.

== USAGE

  require 'imagesize'

  jpegsizefactory = ImageSize.factory('image/jpeg')
  foosize = jpegsizefactory.readsize('foo.jpeg')
  pngsizefactory = ImageSize.factory('image/png')
  barsize = pngsizefactory.readsize('bar.png')
  gifsizefactory = ImageSize.factory('image/gif')
  bazsize = gifsizefactory.readsize('baz.gif')
  p foosize.hv
  p barsize.hv
  p bazsize.hv

== INTERFACES

* imagesizefactory = ImageSize.factory(mimetype)

Create a new factory corresponding to the mimetype.
Now the mimetype can accepts 'image/gif', 'image/png', or 'image/jpeg'.

* imagesize = ImageSize.factory(mimetype)#readsize(filename)

Get a imagesize from a filename's image.

== PROPERTIES

* ImageSize#width
* ImageSize#h

A width.

* ImageSize#height
* ImageSize#v

A height.

* ImageSize#widthheight
* ImageSize#hv

An array of [width, height]

* ImageSize#src

An image file name.

* ImageSize#mimetype

A MIME type

=end

class ImageSize
  attr_reader :src, :mimetype, :width, :height
  def initialize(src, mimetype, width, height)
    @src, @mimetype, @width, @height = src, mimetype, width, height
  end
  def widthheight() [@width, @height] end
  alias h width
  alias v height
  alias hv widthheight
  def inspect
    %Q|\#<ImageSize:#{self.__id__} h:#{@width} v:#{@height} mimetype:"#{@mimetype}" src:"#{@src}">|
  end

  class MyFactory
    def readsize(src)
      w, h = nil, nil
      open(src, "rb") { |f| w, h = scansize(f) }
      if w and h then ImageSize.new(src, mimetype(), w, h) else nil end
    end
  end

  class GifFactory < MyFactory
    def mimetype() 'image/gif' end
    def scansize(f)
      f.read(10).unpack("x6vv")
    end
  end

  class PngFactory < MyFactory
    def mimetype() 'image/png' end
    def scansize(f)
      f.read(24).unpack("x16NN")
    end
  end

  class JpegFactory < MyFactory
    def mimetype() 'image/jpeg' end
    def scansize(f)
      loop do
        nil until f.getc == 0xff
        b = f.getc
        break if b == 0xd9
        next if (b == 0) || ( (b >= 0xd0) && (b <= 0xd8) )
        if ( b >= 0xc0 ) && ( b <= 0xc3 )
          return f.read(7).unpack("x3nn").reverse
        else
          m = f.read(2).unpack("n").first
          f.seek(m-2, File::SEEK_CUR)
        end
      end
    end
  end

  def self.factory(mimetype)
    case mimetype
    when /jpe?g/i then JpegFactory.new
    when /gif/i then GifFactory.new
    when /png/i then PngFactory.new
    end
  end
end
