### Home Classes Methods

### In Files

- lib/prawn/images/png.rb

### Parent

Object

### Methods

- ::new
- #alpha_channel?
- #alpha_channel_bits
- #build_pdf_object
- #colors
- #min_pdf_version
- #pixel_bitlength
- #split_alpha_channel!

### Files

- COPYING
- GPLv2
- GPLv3
- LICENSE

### Class/Module Index

Quicksearch

- Prawn
- Prawn::Core
- Prawn::Core::Page
- Prawn::Core::Page::GraphicsState
- Prawn::Core::Reference
- Prawn::Document
- Prawn::Document::BoundingBox
- Prawn::Document::ColumnBox
- Prawn::Document::GraphicsState
- Prawn::Document::Grid
- Prawn::Document::Internals
- Prawn::Document::PageGeometry
- Prawn::Document::Security
- Prawn::Document::Snapshot
- Prawn::Encoding
- Prawn::Errors
- Prawn::Font
- Prawn::Font::AFM
- Prawn::Font::DFont
- Prawn::Font::TTF
- Prawn::GraphicState
- Prawn::GraphicStateStack
- Prawn::Graphics
- Prawn::Graphics::CapStyle
- Prawn::Graphics::Color
- Prawn::Graphics::Dash
- Prawn::Graphics::Gradient
- Prawn::Graphics::JoinStyle
- Prawn::Graphics::Transformation
- Prawn::Graphics::Transparency
- Prawn::Images
- Prawn::Images::JPG
- Prawn::Images::PNG
- Prawn::Layout
- Prawn::Measurements
- Prawn::Outline
- Prawn::Stamp
- Prawn::Table
- Prawn::Table::Cell
- Prawn::Table::Cell::InTable
- Prawn::Table::Cell::Subtable
- Prawn::Table::Cell::Text
- Prawn::Table::Cells
- Prawn::Text
- Prawn::Text::Box
- Prawn::Text::Formatted
- Prawn::Text::Formatted::Box
- Prawn::Text::Formatted::Fragment
- Prawn::Text::Formatted::Parser
- Arcfour
- Numeric

No matching classes.

# Prawn::Images::PNG

A convenience class that wraps the logic for extracting the parts of a PNG image that we need to embed them in a PDF

### Attributes

alpha_channel[R]
bits[R]
color_type[R]
compression_method[R]
filter_method[R]
height[R]
img_data[R]
interlace_method[R]
palette[R]
scaled_height[RW]
scaled_width[RW]
transparency[R]
width[R]

### Public Class Methods

new(data) click to toggle source

Process a new PNG image

`data` | A binary string of PNG data

    # File lib/prawn/images/png.rb, line 29
    def initialize(data)
      data = StringIO.new(data.dup)

      data.read(8)  # Skip the default header

      @palette  = ""
      @img_data = ""
      @transparency = {}

      loop do
        chunk_size  = data.read(4).unpack("N")[0]
        section     = data.read(4)
        case section
        when 'IHDR'
          # we can grab other interesting values from here (like width,
          # height, etc)
          values = data.read(chunk_size).unpack("NNCCCCC")

          @width              = values[0]
          @height             = values[1]
          @bits               = values[2]
          @color_type         = values[3]
          @compression_method = values[4]
          @filter_method      = values[5]
          @interlace_method   = values[6]
        when 'PLTE'
          @palette << data.read(chunk_size)
        when 'IDAT'
          @img_data << data.read(chunk_size)
        when 'tRNS'
          # This chunk can only occur once and it must occur after the
          # PLTE chunk and before the IDAT chunk
          @transparency = {}
          case @color_type
          when 3
            # Indexed colour, RGB. Each byte in this chunk is an alpha for
            # the palette index in the PLTE ("palette") chunk up until the
            # last non-opaque entry. Set up an array, stretching over all
            # palette entries which will be 0 (opaque) or 1 (transparent).
            @transparency[:indexed]  = data.read(chunk_size).unpack("C*")
            short = 255 - @transparency[:indexed].size
            @transparency[:indexed] += ([255] * short) if short > 0
          when 0
            # Greyscale. Corresponding to entries in the PLTE chunk.
            # Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1
            grayval = data.read(chunk_size).unpack("n").first
            @transparency[:grayscale] = grayval
          when 2
            # True colour with proper alpha channel.
            @transparency[:rgb] = data.read(chunk_size).unpack("nnn")
          end
        when 'IEND'
          # we've got everything we need, exit the loop
          break
        else
          # unknown (or un-important) section, skip over it
          data.seek(data.pos + chunk_size)
        end

        data.read(4)  # Skip the CRC
      end
    end

### Public Instance Methods

alpha_channel?() click to toggle source

    # File lib/prawn/images/png.rb, line 120
    def alpha_channel?
      @color_type == 4 || @color_type == 6
    end

alpha_channel_bits() click to toggle source

Adobe Reader can't handle 16-bit png channels -- chop off the second byte (least significant)

    # File lib/prawn/images/png.rb, line 127
    def alpha_channel_bits
      8
    end

build_pdf_object(document) click to toggle source

Build a PDF object representing this image in `document`, and return a Reference to it.

    # File lib/prawn/images/png.rb, line 134
    def build_pdf_object(document)
      if compression_method != 0
        raise Errors::UnsupportedImageType,
          'PNG uses an unsupported compression method'
      end

      if filter_method != 0
        raise Errors::UnsupportedImageType,
          'PNG uses an unsupported filter method'
      end

      if interlace_method != 0
        raise Errors::UnsupportedImageType,
          'PNG uses unsupported interlace method'
      end

      # some PNG types store the colour and alpha channel data together,
      # which the PDF spec doesn't like, so split it out.
      split_alpha_channel!

      case colors
      when 1
        color = :DeviceGray
      when 3
        color = :DeviceRGB
      else
        raise Errors::UnsupportedImageType,
          "PNG uses an unsupported number of colors (#{png.colors})"
      end

      # build the image dict
      obj = document.ref!(
        :Type             => :XObject,
        :Subtype          => :Image,
        :Height           => height,
        :Width            => width,
        :BitsPerComponent => bits,
        :Length           => img_data.size,
        :Filter           => :FlateDecode
      )

      unless alpha_channel
        obj.data[:DecodeParms] = {:Predictor => 15,
                                  :Colors    => colors,
                                  :BitsPerComponent => bits,
                                  :Columns   => width}
      end

      # append the actual image data to the object as a stream
      obj << img_data

      # sort out the colours of the image
      if palette.empty?
        obj.data[:ColorSpace] = color
      else
        # embed the colour palette in the PDF as a object stream
        palette_obj = document.ref!(:Length => palette.size)
        palette_obj << palette

        # build the color space array for the image
        obj.data[:ColorSpace] = [:Indexed,
                                 :DeviceRGB,
                                 (palette.size / 3) -1,
                                 palette_obj]
      end

      # *************************************
      # add transparency data if necessary
      # *************************************

      # For PNG color types 0, 2 and 3, the transparency data is stored in
      # a dedicated PNG chunk, and is exposed via the transparency attribute
      # of the PNG class.
      if transparency[:grayscale]
        # Use Color Key Masking (spec section 4.8.5)
        # - An array with N elements, where N is two times the number of color
        #   components.
        val = transparency[:grayscale]
        obj.data[:Mask] = [val, val]
      elsif transparency[:rgb]
        # Use Color Key Masking (spec section 4.8.5)
        # - An array with N elements, where N is two times the number of color
        #   components.
        rgb = transparency[:rgb]
        obj.data[:Mask] = rgb.collect { |x| [x,x] }.flatten
      elsif transparency[:indexed]
        # TODO: broken. I was attempting to us Color Key Masking, but I think
        #       we need to construct an SMask i think. Maybe do it inside
        #       the PNG class, and store it in alpha_channel
        #obj.data[:Mask] = transparency[:indexed]
      end

      # For PNG color types 4 and 6, the transparency data is stored as a alpha
      # channel mixed in with the main image data. The PNG class seperates
      # it out for us and makes it available via the alpha_channel attribute
      if alpha_channel?
        smask_obj = document.ref!(
          :Type             => :XObject,
          :Subtype          => :Image,
          :Height           => height,
          :Width            => width,
          :BitsPerComponent => alpha_channel_bits,
          :Length           => alpha_channel.size,
          :Filter           => :FlateDecode,
          :ColorSpace       => :DeviceGray,
          :Decode           => [0, 1]
        )
        smask_obj << alpha_channel
        obj.data[:SMask] = smask_obj
      end

      obj
    end

colors() click to toggle source

number of color components to each pixel

    # File lib/prawn/images/png.rb, line 94
    def colors
      case self.color_type
      when 0, 3, 4
        return 1
      when 2, 6
        return 3
      end
    end

min_pdf_version() click to toggle source

Returns the minimum PDF version required to support this image.

    # File lib/prawn/images/png.rb, line 249
    def min_pdf_version
      if bits > 8
        # 16-bit color only supported in 1.5+ (ISO 32000-1:2008 8.9.5.1)
        1.5
      elsif alpha_channel?
        # Need transparency for SMask
        1.4
      else
        1.0
      end
    end

pixel_bitlength() click to toggle source

number of bits used per pixel

    # File lib/prawn/images/png.rb, line 105
    def pixel_bitlength
      if alpha_channel?
        self.bits * (self.colors + 1)
      else
        self.bits * self.colors
      end
    end

split_alpha_channel!() click to toggle source

split the alpha channel data from the raw image data in images where it's required.

    # File lib/prawn/images/png.rb, line 116
    def split_alpha_channel!
      unfilter_image_data if alpha_channel?
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.