Class: Prawn::Images::PNG
Overview
A convenience class that wraps the logic for extracting the parts of a PNG image that we need to embed them in a PDF
Instance Attribute Summary collapse
-
#alpha_channel ⇒ Object
readonly
Returns the value of attribute alpha_channel.
-
#bits ⇒ Object
readonly
Returns the value of attribute bits.
-
#color_type ⇒ Object
readonly
Returns the value of attribute color_type.
-
#compression_method ⇒ Object
readonly
Returns the value of attribute compression_method.
-
#filter_method ⇒ Object
readonly
Returns the value of attribute filter_method.
-
#height ⇒ Object
readonly
Returns the value of attribute height.
-
#img_data ⇒ Object
readonly
Returns the value of attribute img_data.
-
#interlace_method ⇒ Object
readonly
Returns the value of attribute interlace_method.
-
#palette ⇒ Object
readonly
Returns the value of attribute palette.
-
#scaled_height ⇒ Object
Returns the value of attribute scaled_height.
-
#scaled_width ⇒ Object
Returns the value of attribute scaled_width.
-
#transparency ⇒ Object
readonly
Returns the value of attribute transparency.
-
#width ⇒ Object
readonly
Returns the value of attribute width.
Instance Method Summary collapse
- #alpha_channel? ⇒ Boolean
-
#alpha_channel_bits ⇒ Object
Adobe Reader can’t handle 16-bit png channels – chop off the second byte (least significant).
-
#build_pdf_object(document) ⇒ Object
Build a PDF object representing this image in
document
, and return a Reference to it. -
#colors ⇒ Object
number of color components to each pixel.
-
#initialize(data) ⇒ PNG
constructor
Process a new PNG image.
-
#min_pdf_version ⇒ Object
Returns the minimum PDF version required to support this image.
-
#pixel_bitlength ⇒ Object
number of bits used per pixel.
-
#split_alpha_channel! ⇒ Object
split the alpha channel data from the raw image data in images where it’s required.
Methods inherited from Image
#calc_image_dimensions, detect_image_format
Constructor Details
#initialize(data) ⇒ PNG
Process a new PNG image
data
-
A binary string of PNG data
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/prawn/images/png.rb', line 30 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 |
Instance Attribute Details
#alpha_channel ⇒ Object (readonly)
Returns the value of attribute alpha_channel.
23 24 25 |
# File 'lib/prawn/images/png.rb', line 23 def alpha_channel @alpha_channel end |
#bits ⇒ Object (readonly)
Returns the value of attribute bits.
21 22 23 |
# File 'lib/prawn/images/png.rb', line 21 def bits @bits end |
#color_type ⇒ Object (readonly)
Returns the value of attribute color_type.
22 23 24 |
# File 'lib/prawn/images/png.rb', line 22 def color_type @color_type end |
#compression_method ⇒ Object (readonly)
Returns the value of attribute compression_method.
22 23 24 |
# File 'lib/prawn/images/png.rb', line 22 def compression_method @compression_method end |
#filter_method ⇒ Object (readonly)
Returns the value of attribute filter_method.
22 23 24 |
# File 'lib/prawn/images/png.rb', line 22 def filter_method @filter_method end |
#height ⇒ Object (readonly)
Returns the value of attribute height.
21 22 23 |
# File 'lib/prawn/images/png.rb', line 21 def height @height end |
#img_data ⇒ Object (readonly)
Returns the value of attribute img_data.
20 21 22 |
# File 'lib/prawn/images/png.rb', line 20 def img_data @img_data end |
#interlace_method ⇒ Object (readonly)
Returns the value of attribute interlace_method.
23 24 25 |
# File 'lib/prawn/images/png.rb', line 23 def interlace_method @interlace_method end |
#palette ⇒ Object (readonly)
Returns the value of attribute palette.
20 21 22 |
# File 'lib/prawn/images/png.rb', line 20 def palette @palette end |
#scaled_height ⇒ Object
Returns the value of attribute scaled_height.
24 25 26 |
# File 'lib/prawn/images/png.rb', line 24 def scaled_height @scaled_height end |
#scaled_width ⇒ Object
Returns the value of attribute scaled_width.
24 25 26 |
# File 'lib/prawn/images/png.rb', line 24 def scaled_width @scaled_width end |
#transparency ⇒ Object (readonly)
Returns the value of attribute transparency.
20 21 22 |
# File 'lib/prawn/images/png.rb', line 20 def transparency @transparency end |
#width ⇒ Object (readonly)
Returns the value of attribute width.
21 22 23 |
# File 'lib/prawn/images/png.rb', line 21 def width @width end |
Instance Method Details
#alpha_channel? ⇒ Boolean
121 122 123 |
# File 'lib/prawn/images/png.rb', line 121 def alpha_channel? @color_type == 4 || @color_type == 6 end |
#alpha_channel_bits ⇒ Object
Adobe Reader can’t handle 16-bit png channels – chop off the second byte (least significant)
128 129 130 |
# File 'lib/prawn/images/png.rb', line 128 def alpha_channel_bits 8 end |
#build_pdf_object(document) ⇒ Object
Build a PDF object representing this image in document
, and return a Reference to it.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/prawn/images/png.rb', line 135 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 ⇒ Object
number of color components to each pixel
95 96 97 98 99 100 101 102 |
# File 'lib/prawn/images/png.rb', line 95 def colors case self.color_type when 0, 3, 4 return 1 when 2, 6 return 3 end end |
#min_pdf_version ⇒ Object
Returns the minimum PDF version required to support this image.
250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/prawn/images/png.rb', line 250 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 ⇒ Object
number of bits used per pixel
106 107 108 109 110 111 112 |
# File 'lib/prawn/images/png.rb', line 106 def pixel_bitlength if alpha_channel? self.bits * (self.colors + 1) else self.bits * self.colors end end |
#split_alpha_channel! ⇒ Object
split the alpha channel data from the raw image data in images where it’s required.
117 118 119 |
# File 'lib/prawn/images/png.rb', line 117 def split_alpha_channel! unfilter_image_data if alpha_channel? end |