class Writer::Graphics::ImageInfo

This is based on ImageSize, by Keisuke Minami <keisuke@rccn.com>. It can be found at www.rubycgi.org/tools/index.en.htm

This has been integrated into PDF::Writer because as yet there has been no response to emails asking for my extensions to be integrated and a RubyGem package to be made available.

Constants

JPEG_APP_BLOCKS
JPEG_SOF_BLOCKS
Type

Image Format Constants

XBM_DIMENSIONS_RE
XPM_DIMENSIONS_RE

Attributes

bits[R]
channels[R]
format[R]
get_height[R]
get_type[R]
get_width[R]
height[R]
info[R]
width[R]

Public Class Methods

formats() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 44
def formats
  Formats.constants
end
Also aliased as: type_list
new(data, format = nil) click to toggle source

Receive image & make size. argument is image String or IO

# File lib/pdf/writer/graphics/imageinfo.rb, line 54
def initialize(data, format = nil)
  @data   = data.dup rescue data
  @info   = {}

  if @data.kind_of?(IO)
    @top = @data.read(128)
    @data.seek(0, 0)
      # Define Singleton-method definition to IO (byte, offset)
    def @data.read_o(length = 1, offset = nil)
      self.seek(offset, 0) if offset
      ret = self.read(length)
      raise "cannot read!!" unless ret
      ret
    end
  elsif @data.is_a?(String)
    @top = @data[0, 128]
      # Define Singleton-method definition to String (byte, offset)
    @data.extend(PDF::Writer::OffsetReader)
  else
    raise "argument class error!! #{data.type}"
  end

  if format.nil?
    @format = discover_format
  else
    match = false
    Formats.constants.each { |t| match = true if format == t }
    raise("format is failed. #{format}\n") unless match
    @format = format
  end

  __send__("measure_#@format".intern) unless @format == Formats::OTHER

  @data = data.dup
end
type_list()
Alias for: formats

Private Instance Methods

discover_format() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 102
def discover_format
  if    @top        =~ %r{^GIF8[79]a}
    Formats::GIF
  elsif @top[0, 3]  == "\xff\xd8\xff"
    Formats::JPEG
  elsif @top[0, 8]  == "\x89PNG\x0d\x0a\x1a\x0a"
    Formats::PNG
  elsif @top[0, 3]  == "FWS"
    Formats::SWF
  elsif @top[0, 4]  == "8BPS"
    Formats::PSD
  elsif @top[0, 2]  == 'BM'
    Formats::BMP
  elsif @top[0, 4]  == "MM\x00\x2a"
    Formats::TIFF
  elsif @top[0, 4]  == "II\x2a\x00"
    Formats::TIFF
  elsif @top[0, 12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
    Formats::JP2
  elsif @top        =~ %r{^P[1-7]}
    Formats::PPM
  elsif @top        =~ %r{\#define\s+\S+\s+\d+}
    Formats::XBM
  elsif @top        =~ %r{/\* XPM \*/}
    Formats::XPM
  elsif @top[0] == 10
    Formats::PCX
  else
    Formats::OTHER  # might be WBMP
  end
end
measure_BMP() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 192
def measure_BMP
  # Skip the first 14 bytes of the image.
  @data.read_o(14)
  # Up to the next 16 bytes will be used.
  dim = @data.read_o(16)

  # Get the "size" of the image from the next four bytes.
  size = dim.unpack("V").first # <- UNPACK RETURNS ARRAY, SO GET FIRST ELEMENT

  if size == 12
   @width, @height, @bits = dim.unpack("x4vvx3C")
  elsif size > 12 and (size <= 64 or size == 108)
   @width, @height, @bits = dim.unpack("x4VVv")
  end  
end
measure_GIF() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 135
def measure_GIF
  @data.read_o(6) # Skip GIF8.a
  @width, @height, @bits = @data.read_o(5).unpack('vvC')
  if @bits & 0x80 == 0x80
    @bits = (@bits & 0x07) + 1
  else
    @bits = 0
  end
  @channels = 3
end
measure_JPEG() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 171
def measure_JPEG
  c_marker = "\xff" # Section marker.
  @data.read_o(2)   # Skip the first two bytes of JPEG identifier.
  loop do
    marker, code, length = @data.read_o(4).unpack('aan')
    raise "JPEG marker not found!" if marker != c_marker

    if JPEG_SOF_BLOCKS.include?(code)
      @bits, @height, @width, @channels = @data.read_o(6).unpack("CnnC")
      break
    end

    buffer = @data.read_o(length - 2)

    if JPEG_APP_BLOCKS.include?(code)
      @info["APP#{code.to_i - 0xe0}"] = buffer
    end
  end
end
measure_PBM()
Alias for: measure_PPM
measure_PCX() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 264
def measure_PCX
        header = @data.read_o(128)
        head_part = header.unpack('C4S4')
        @width  = head_part[6] - head_part[4] + 1
        @height = head_part[7] - head_part[5] + 1
end
measure_PGM()
Alias for: measure_PPM
measure_PNG() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 147
def measure_PNG
  @data.read_o(12)
  raise "This file is not PNG." unless @data.read_o(4) == "IHDR"
    # The file information is in the IHDR section.
    #   Offset  Bytes Meaning
    #    0      4     Width
    #    5      4     Height
    #    9      1     Bit Depth
    #   10      1     Compression Method
    #   11      1     Filter Method
    #   12      1     Interlace Method
  ihdr = @data.read_o(13).unpack("NNCCCCC")
  @width                      = ihdr[0]
  @height                     = ihdr[1]
  @bits                       = ihdr[2]
  @info[:color_type]          = ihdr[3]
  @info[:compression_method]  = ihdr[4]
  @info[:filter_method]       = ihdr[5]
  @info[:interlace_method]    = ihdr[6]


end
measure_PPM() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 209
  def measure_PPM
    header = @data.read_o(1024)
    header.gsub!(/^\#[^\n\r]*/m, "")
    md = %r{^(P[1-6])\s+?(\d+)\s+?(\d+)}mo.match(header)

    @width  = md.captures[1]
    @height = md.captures[2]

    case md.captures[0]
    when "P1", "P4"
      @format = "PBM"
    when "P2", "P5"
      @format = "PGM"
    when "P3", "P6"
      @format = "PPM"
#   when "P7"
#     @format = "XV"
#     header =~ /IMGINFO:(\d+)x(\d+)/m
#     width = $1.to_i; height = $2.to_i
    end
  end
Also aliased as: measure_PGM, measure_PBM
measure_PSD() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 259
def measure_PSD
        @width, @height = @data.read_o(26).unpack("x14NN")
end
measure_SWC() click to toggle source

The same as SWF, except that the original data is compressed with Zlib. Disabled for now.

# File lib/pdf/writer/graphics/imageinfo.rb, line 292
def measure_SWC
end
measure_SWF() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 272
def measure_SWF
        header = @data.read_o(9)
        raise "This file is not SWF."  unless header.unpack('a3')[0] == 'FWS'

        bits    = Integer("0b#{header.unpack('@8B5')}")
        header << @data.read_o(bits * 4 / 8 + 1)

        str     = *(header.unpack("@8B#{5 + bits * 4}"))
        last    = 5
        x_min   = Integer("0b#{str[last, bits]}")
        x_max   = Integer("0b#{str[(last + bits), bits]}")
        y_min   = Integer("0b#{str[(last + (2 * bits)), bits]}")
        y_max   = Integer("0b#{str[(last + (3 * bits)), bits]}")
        @width  = (x_max - x_min) / 20
        @height = (y_max - y_min) / 20
end
measure_TIFF() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 296
def measure_TIFF
    # 'v' little-endian
    # 'n' default to big-endian
  endian = (@data.read_o(4) =~ /II\x2a\x00/o) ? 'v' : 'n'

              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)
              ]

    # Find the IFD location.
              ifd_addr    = *(@data.read_o(4).unpack(endian.upcase))
    # Get the number of entries in the IFD.
              ifd         = @data.read_o(2, ifd_addr)
              num_dirent  = *(ifd.unpack(endian))         # Make it useful
              ifd_addr    += 2
              num_dirent  = ifd_addr + (num_dirent * 12)  # Calc. maximum offset of IFD

  loop do
    break if @width and @height

    ifd = @data.read_o(12, ifd_addr)  # Get directory entry.
    break if ifd.nil? or ifd_addr > num_dirent
    ifd_addr += 12

    tag   = *(ifd.unpack(endian))       # ...decode its tag
    type  = *(ifd[2, 2].unpack(endian)) # ... and data type

      # Check the type for sanity.
    next if type > packspec.size or packspec[type].nil?

    case tag
    when 0x0100, 0xa002 # width
      @width  = *(ifd[8, 4].unpack(packspec[type]))
    when 0x0101, 0xa003 # height
      @height = *(ifd[8, 4].unpack(packspec[type]))
    end
  end
      end
measure_XBM() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 238
    def measure_XBM
md = XBM_DIMENSIONS_RE.match(@data.read_o(1024))

@width  = md.captures[0].to_i
@height = md.captures[1].to_i
    end
measure_XPM() click to toggle source
# File lib/pdf/writer/graphics/imageinfo.rb, line 247
    def measure_XPM
while line = @data.read_o(1024)
  md = XPM_DIMENSIONS_RE.match(line)
  if md
    @width  = md.captures[0].to_i
    @height = md.captures[1].to_i
    break
  end
end
    end