lib/prawn/images/png.rb in prawn-2.4.0 vs lib/prawn/images/png.rb in prawn-2.5.0

- old
+ new

@@ -1,40 +1,79 @@ # encoding: ASCII-8BIT - # frozen_string_literal: true -# png.rb : Extracts the data from a PNG that is needed for embedding -# -# Based on some similar code in PDF::Writer by Austin Ziegler -# -# Copyright April 2008, James Healy. All Rights Reserved. -# -# This is free software. Please see the LICENSE and COPYING files for details. - require 'stringio' module Prawn - module Images - # A convenience class that wraps the logic for extracting the parts - # of a PNG image that we need to embed them in a PDF - # + module Images # rubocop: disable Style/Documentation + # A convenience class that wraps the logic for extracting the parts of a PNG + # image that we need to embed them in a PDF. class PNG < Image # @group Extension API - attr_reader :palette, :img_data, :transparency - attr_reader :width, :height, :bits - attr_reader :color_type, :compression_method, :filter_method - attr_reader :interlace_method, :alpha_channel - attr_accessor :scaled_width, :scaled_height + # Palette data. + # @return [String] + attr_reader :palette + # Image data. + # @return [String] + attr_reader :img_data + + # Transparency data. + # @return [Hash{Symbol => String}] + attr_reader :transparency + + # Image width in pixels. + # @return [Integer] + attr_reader :width + + # Image height in pixels. + # @return [Integer] + attr_reader :height + + # Bits per sample or per palette index. + # @return [Integer] + attr_reader :bits + + # Color type. + # @return [Integer] + attr_reader :color_type + + # Compression method. + # @return [Integer] + attr_reader :compression_method + + # Filter method. + # @return [Integer] + attr_reader :filter_method + + # Interlace method. + # @return [Integer] + attr_reader :interlace_method + + # Extracted alpha-channel. + # @return [String, nil] + attr_reader :alpha_channel + + # Scaled width of the image in PDF points. + # @return [Number] + attr_accessor :scaled_width + + # Scaled height of the image in PDF points. + # @return [Number] + attr_accessor :scaled_height + + # Can this image handler process this image? + # + # @param image_blob [String] + # @return [Boolean] def self.can_render?(image_blob) image_blob[0, 8].unpack('C*') == [137, 80, 78, 71, 13, 10, 26, 10] end # Process a new PNG image # - # <tt>data</tt>:: A binary string of PNG data - # + # @param data [String] A binary string of PNG data. def initialize(data) super() data = StringIO.new(data.dup) data.read(8) # Skip the default header @@ -91,44 +130,52 @@ end @img_data = Zlib::Inflate.inflate(@img_data) end - # number of color components to each pixel + # Number of color components to each pixel. # + # @return [Integer] def colors case color_type when 0, 3, 4 1 when 2, 6 3 end end - # split the alpha channel data from the raw image data in images - # where it's required. + # Split the alpha channel data from the raw image data in images where + # it's required. # + # @private + # @return [void] def split_alpha_channel! if alpha_channel? if color_type == 3 generate_alpha_channel else split_image_data end end end + # Is there an alpha-channel in this image? + # + # @return [Boolean] def alpha_channel? return true if color_type == 4 || color_type == 6 return @transparency.any? if color_type == 3 false end - # Build a PDF object representing this image in +document+, and return + # Build a PDF object representing this image in `document`, and return # a Reference to it. # + # @param document [Prawn::Document] + # @return [PDF::Core::Reference] def build_pdf_object(document) if compression_method != 0 raise Errors::UnsupportedImageType, 'PNG uses an unsupported compression method' end @@ -161,23 +208,23 @@ obj = document.ref!( Type: :XObject, Subtype: :Image, Height: height, Width: width, - BitsPerComponent: bits + BitsPerComponent: bits, ) # append the actual image data to the object as a stream obj << img_data obj.stream.filters << { FlateDecode: { Predictor: 15, Colors: colors, BitsPerComponent: bits, - Columns: width - } + Columns: width, + }, } # sort out the colours of the image if palette.empty? obj.data[:ColorSpace] = color @@ -189,11 +236,11 @@ # build the color space array for the image obj.data[:ColorSpace] = [ :Indexed, :DeviceRGB, (palette.size / 3) - 1, - palette_obj + palette_obj, ] end # ************************************* # add transparency data if necessary @@ -216,39 +263,41 @@ obj.data[:Mask] = rgb.map { |x| [x, x] }.flatten 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 + # separates 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: bits, ColorSpace: :DeviceGray, - Decode: [0, 1] + Decode: [0, 1], ) smask_obj.stream << alpha_channel smask_obj.stream.filters << { FlateDecode: { Predictor: 15, Colors: 1, BitsPerComponent: bits, - Columns: width - } + Columns: width, + }, } obj.data[:SMask] = smask_obj end obj end # Returns the minimum PDF version required to support this image. + # + # @return [Float] 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? @@ -263,36 +312,36 @@ def split_image_data alpha_bytes = bits / 8 color_bytes = colors * bits / 8 - scanline_length = (color_bytes + alpha_bytes) * width + 1 + scanline_length = ((color_bytes + alpha_bytes) * width) + 1 scanlines = @img_data.bytesize / scanline_length pixels = width * height data = StringIO.new(@img_data) data.binmode - color_data = [0x00].pack('C') * (pixels * color_bytes + scanlines) + color_data = [0x00].pack('C') * ((pixels * color_bytes) + scanlines) color = StringIO.new(color_data) color.binmode - @alpha_channel = [0x00].pack('C') * (pixels * alpha_bytes + scanlines) + @alpha_channel = [0x00].pack('C') * ((pixels * alpha_bytes) + scanlines) alpha = StringIO.new(@alpha_channel) alpha.binmode scanlines.times do |line| data.seek(line * scanline_length) filter = data.getbyte - color.putc filter - alpha.putc filter + color.putc(filter) + alpha.putc(filter) width.times do - color.write data.read(color_bytes) - alpha.write data.read(alpha_bytes) + color.write(data.read(color_bytes)) + alpha.write(data.read(alpha_bytes)) end end @img_data = color_data end @@ -317,16 +366,18 @@ scanlines.times do |line| data.seek(line * scanline_length) filter = data.getbyte - alpha.putc filter + alpha.putc(filter) width.times do color = data.read(1).unpack1('C') - alpha.putc alpha_palette[color] + alpha.putc(alpha_palette[color]) end end end end + + Prawn.image_handler.register(Prawn::Images::PNG) end end