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