prawn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +340 -0
- data/LICENSE +56 -0
- data/README +30 -0
- data/Rakefile +83 -0
- data/data/fonts/Activa.ttf +0 -0
- data/data/fonts/Chalkboard.ttf +0 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/Dustismo_Roman.ttf +0 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/data/fonts/gkai00mp.ttf +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/shift_jis_text.txt +1 -0
- data/examples/addressbook.csv +6 -0
- data/examples/alignment.rb +16 -0
- data/examples/bounding_boxes.pdf +62 -0
- data/examples/bounding_boxes.rb +30 -0
- data/examples/canvas.pdf +81 -0
- data/examples/canvas.rb +12 -0
- data/examples/cell.rb +27 -0
- data/examples/currency.csv +1834 -0
- data/examples/curves.rb +10 -0
- data/examples/fancy_table.rb +48 -0
- data/examples/font_size.rb +19 -0
- data/examples/hexagon.rb +14 -0
- data/examples/image.pdf +0 -0
- data/examples/image.rb +23 -0
- data/examples/image2.rb +13 -0
- data/examples/inline_styles.pdf +117 -0
- data/examples/kerning.rb +27 -0
- data/examples/line.rb +31 -0
- data/examples/multi_page_layout.rb +14 -0
- data/examples/on_page_start.rb +17 -0
- data/examples/page_geometry.rb +28 -0
- data/examples/polygons.rb +16 -0
- data/examples/ruport_formatter.rb +47 -0
- data/examples/ruport_helpers.rb +17 -0
- data/examples/russian_boxes.rb +34 -0
- data/examples/simple_text.rb +15 -0
- data/examples/simple_text_ttf.rb +16 -0
- data/examples/sjis.rb +19 -0
- data/examples/table.rb +45 -0
- data/examples/table_bench.rb +92 -0
- data/examples/text_flow.rb +65 -0
- data/examples/utf8.rb +12 -0
- data/lib/prawn.rb +33 -0
- data/lib/prawn/compatibility.rb +33 -0
- data/lib/prawn/document.rb +334 -0
- data/lib/prawn/document/bounding_box.rb +253 -0
- data/lib/prawn/document/page_geometry.rb +78 -0
- data/lib/prawn/document/table.rb +253 -0
- data/lib/prawn/document/text.rb +346 -0
- data/lib/prawn/errors.rb +33 -0
- data/lib/prawn/font.rb +5 -0
- data/lib/prawn/font/cmap.rb +59 -0
- data/lib/prawn/font/metrics.rb +414 -0
- data/lib/prawn/font/wrapping.rb +45 -0
- data/lib/prawn/graphics.rb +285 -0
- data/lib/prawn/graphics/cell.rb +226 -0
- data/lib/prawn/images.rb +241 -0
- data/lib/prawn/images/jpg.rb +43 -0
- data/lib/prawn/images/png.rb +178 -0
- data/lib/prawn/pdf_object.rb +64 -0
- data/lib/prawn/reference.rb +47 -0
- data/spec/bounding_box_spec.rb +120 -0
- data/spec/box_calculation_spec.rb +17 -0
- data/spec/document_spec.rb +152 -0
- data/spec/graphics_spec.rb +250 -0
- data/spec/images_spec.rb +42 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/metrics_spec.rb +60 -0
- data/spec/pdf_object_spec.rb +102 -0
- data/spec/png_spec.rb +35 -0
- data/spec/reference_spec.rb +29 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/table_spec.rb +145 -0
- data/spec/text_spec.rb +190 -0
- data/vendor/font_ttf/ttf.rb +20 -0
- data/vendor/font_ttf/ttf/datatypes.rb +189 -0
- data/vendor/font_ttf/ttf/encodings.rb +140 -0
- data/vendor/font_ttf/ttf/exceptions.rb +28 -0
- data/vendor/font_ttf/ttf/file.rb +290 -0
- data/vendor/font_ttf/ttf/fontchunk.rb +77 -0
- data/vendor/font_ttf/ttf/table/cmap.rb +408 -0
- data/vendor/font_ttf/ttf/table/cvt.rb +49 -0
- data/vendor/font_ttf/ttf/table/fpgm.rb +48 -0
- data/vendor/font_ttf/ttf/table/gasp.rb +88 -0
- data/vendor/font_ttf/ttf/table/glyf.rb +452 -0
- data/vendor/font_ttf/ttf/table/head.rb +86 -0
- data/vendor/font_ttf/ttf/table/hhea.rb +96 -0
- data/vendor/font_ttf/ttf/table/hmtx.rb +98 -0
- data/vendor/font_ttf/ttf/table/kern.rb +186 -0
- data/vendor/font_ttf/ttf/table/loca.rb +75 -0
- data/vendor/font_ttf/ttf/table/maxp.rb +81 -0
- data/vendor/font_ttf/ttf/table/name.rb +222 -0
- data/vendor/font_ttf/ttf/table/os2.rb +172 -0
- data/vendor/font_ttf/ttf/table/post.rb +120 -0
- data/vendor/font_ttf/ttf/table/prep.rb +27 -0
- data/vendor/font_ttf/ttf/table/vhea.rb +45 -0
- data/vendor/font_ttf/ttf/table/vmtx.rb +36 -0
- metadata +180 -0
data/lib/prawn/images.rb
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# encoding: ASCII-8BIT
|
|
2
|
+
# images.rb : Implements PDF image embedding
|
|
3
|
+
#
|
|
4
|
+
# Copyright April 2008, James Healy, Gregory Brown. All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
7
|
+
|
|
8
|
+
require 'digest/sha1'
|
|
9
|
+
|
|
10
|
+
module Prawn
|
|
11
|
+
|
|
12
|
+
module Images
|
|
13
|
+
|
|
14
|
+
# add the image at filename to the current page. Currently only
|
|
15
|
+
# JPG and PNG files are supported.
|
|
16
|
+
#
|
|
17
|
+
# Arguments:
|
|
18
|
+
# <tt>filename</tt>:: the path to the file to be embedded
|
|
19
|
+
#
|
|
20
|
+
# Options:
|
|
21
|
+
# <tt>:at</tt>:: the location of the top left corner of the image [current position]
|
|
22
|
+
# <tt>:height</tt>:: the height of the image [actual height of the image]
|
|
23
|
+
# <tt>:width</tt>:: the width of the image [actual width of the image]
|
|
24
|
+
# <tt>:scale</tt>:: scale the dimensions of the image proportionally
|
|
25
|
+
#
|
|
26
|
+
# Prawn::Document.generate("image2.pdf", :page_layout => :landscape) do
|
|
27
|
+
# pigs = "#{Prawn::BASEDIR}/data/images/pigs.jpg"
|
|
28
|
+
# image pigs, :at => [50,450], :width => 450
|
|
29
|
+
#
|
|
30
|
+
# dice = "#{Prawn::BASEDIR}/data/images/dice.png"
|
|
31
|
+
# image dice, :at => [50, 450], :scale => 0.75
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# If only one of :width / :height are provided, the image will be scaled
|
|
35
|
+
# proportionally. When both are provided, the image will be stretched to
|
|
36
|
+
# fit the dimensions without maintaining the aspect ratio.
|
|
37
|
+
#
|
|
38
|
+
def image(filename, options={})
|
|
39
|
+
raise ArgumentError, "#{filename} not found" unless File.file?(filename)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
read_mode = ruby_18 { "rb" } || ruby_19 { "rb:ASCII-8BIT" }
|
|
43
|
+
image_content = File.open(filename, read_mode) { |f| f.read }
|
|
44
|
+
|
|
45
|
+
image_sha1 = Digest::SHA1.hexdigest(image_content)
|
|
46
|
+
|
|
47
|
+
# register the fact that the current page uses images
|
|
48
|
+
proc_set :ImageC
|
|
49
|
+
|
|
50
|
+
# if this image has already been embedded, just reuse it
|
|
51
|
+
image_obj = image_registry[image_sha1]
|
|
52
|
+
|
|
53
|
+
if image_registry[image_sha1]
|
|
54
|
+
info = image_registry[image_sha1][:info]
|
|
55
|
+
image_obj = image_registry[image_sha1][:obj]
|
|
56
|
+
else
|
|
57
|
+
# build the image object and embed the raw data
|
|
58
|
+
image_obj = case detect_image_format(image_content)
|
|
59
|
+
when :jpg then
|
|
60
|
+
info = Prawn::Images::JPG.new(image_content)
|
|
61
|
+
build_jpg_object(image_content, info)
|
|
62
|
+
when :png then
|
|
63
|
+
info = Prawn::Images::PNG.new(image_content)
|
|
64
|
+
build_png_object(image_content, info)
|
|
65
|
+
end
|
|
66
|
+
image_registry[image_sha1] = {:obj => image_obj, :info => info}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# find where the image will be placed and how big it will be
|
|
70
|
+
x,y = translate(options[:at])
|
|
71
|
+
w,h = calc_image_dimensions(info, options)
|
|
72
|
+
|
|
73
|
+
# add a reference to the image object to the current page
|
|
74
|
+
# resource list and give it a label
|
|
75
|
+
label = "I#{next_image_id}"
|
|
76
|
+
page_xobjects.merge!( label => image_obj )
|
|
77
|
+
|
|
78
|
+
# add the image to the current page
|
|
79
|
+
instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
|
|
80
|
+
add_content instruct % [ w, h, x, y - h, label ]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def build_jpg_object(data, jpg)
|
|
86
|
+
color_space = case jpg.channels
|
|
87
|
+
when 1
|
|
88
|
+
:DeviceGray
|
|
89
|
+
when 4
|
|
90
|
+
:DeviceCMYK
|
|
91
|
+
else
|
|
92
|
+
:DeviceRGB
|
|
93
|
+
end
|
|
94
|
+
obj = ref(:Type => :XObject,
|
|
95
|
+
:Subtype => :Image,
|
|
96
|
+
:Filter => :DCTDecode,
|
|
97
|
+
:ColorSpace => color_space,
|
|
98
|
+
:BitsPerComponent => jpg.bits,
|
|
99
|
+
:Width => jpg.width,
|
|
100
|
+
:Height => jpg.height,
|
|
101
|
+
:Length => data.size )
|
|
102
|
+
obj << data
|
|
103
|
+
return obj
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def build_png_object(data, png)
|
|
107
|
+
|
|
108
|
+
if png.compression_method != 0
|
|
109
|
+
raise ArgumentError, 'PNG uses an unsupported compression method'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if png.filter_method != 0
|
|
113
|
+
raise ArgumentError, 'PNG uses an unsupported filter method'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if png.interlace_method != 0
|
|
117
|
+
raise ArgumentError, 'PNG uses unsupported interlace method'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if png.bits > 8
|
|
121
|
+
raise ArgumentError, 'PNG uses more than 8 bits'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
case png.color_type
|
|
125
|
+
when 0
|
|
126
|
+
ncolor = 1
|
|
127
|
+
color = :DeviceGray
|
|
128
|
+
when 2
|
|
129
|
+
ncolor = 3
|
|
130
|
+
color = :DeviceRGB
|
|
131
|
+
when 3
|
|
132
|
+
ncolor = 1
|
|
133
|
+
color = :DeviceRGB
|
|
134
|
+
when 6
|
|
135
|
+
ncolor = 3
|
|
136
|
+
color = :DeviceRGB
|
|
137
|
+
else
|
|
138
|
+
raise ArgumentError, "PNG has unsupported color type"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# build the image dict
|
|
142
|
+
obj = ref(:Type => :XObject,
|
|
143
|
+
:Subtype => :Image,
|
|
144
|
+
:Height => png.height,
|
|
145
|
+
:Width => png.width,
|
|
146
|
+
:BitsPerComponent => png.bits,
|
|
147
|
+
:Length => png.img_data.size,
|
|
148
|
+
:Filter => :FlateDecode
|
|
149
|
+
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
unless png.alpha_channel
|
|
153
|
+
obj.data[:DecodeParms] = {:Predictor => 15,
|
|
154
|
+
:Colors => ncolor,
|
|
155
|
+
:Columns => png.width}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# append the actual image data to the object as a stream
|
|
159
|
+
obj << png.img_data
|
|
160
|
+
|
|
161
|
+
# sort out the colours of the image
|
|
162
|
+
if png.palette.empty?
|
|
163
|
+
obj.data[:ColorSpace] = color
|
|
164
|
+
else
|
|
165
|
+
# embed the colour palette in the PDF as a object stream
|
|
166
|
+
palette_obj = ref(:Length => png.palette.size)
|
|
167
|
+
palette_obj << png.palette
|
|
168
|
+
|
|
169
|
+
# build the color space array for the image
|
|
170
|
+
obj.data[:ColorSpace] = [:Indexed,
|
|
171
|
+
:DeviceRGB,
|
|
172
|
+
(png.palette.size / 3) -1,
|
|
173
|
+
palette_obj]
|
|
174
|
+
|
|
175
|
+
# add transparency data if necessary
|
|
176
|
+
#if png.transparency && png.transparency[:type] == 'indexed'
|
|
177
|
+
# obj.data[:Mask] = png.transparency[:data]
|
|
178
|
+
#end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
if png.alpha_channel
|
|
182
|
+
smask_obj = ref(:Type => :XObject,
|
|
183
|
+
:Subtype => :Image,
|
|
184
|
+
:Height => png.height,
|
|
185
|
+
:Width => png.width,
|
|
186
|
+
:BitsPerComponent => 8,
|
|
187
|
+
:Length => png.alpha_channel.size,
|
|
188
|
+
:Filter => :FlateDecode,
|
|
189
|
+
:ColorSpace => :DeviceGray,
|
|
190
|
+
:Decode => [0, 1]
|
|
191
|
+
)
|
|
192
|
+
smask_obj << png.alpha_channel
|
|
193
|
+
obj.data[:SMask] = smask_obj
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
return obj
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def calc_image_dimensions(info, options)
|
|
200
|
+
# TODO: allow the image to be aligned in a box
|
|
201
|
+
w = options[:width] || info.width
|
|
202
|
+
h = options[:height] || info.height
|
|
203
|
+
|
|
204
|
+
if options[:width] && !options[:height]
|
|
205
|
+
wp = w / info.width.to_f
|
|
206
|
+
w = info.width * wp
|
|
207
|
+
h = info.height * wp
|
|
208
|
+
elsif options[:height] && !options[:width]
|
|
209
|
+
hp = h / info.height.to_f
|
|
210
|
+
w = info.width * hp
|
|
211
|
+
h = info.height * hp
|
|
212
|
+
elsif options[:scale]
|
|
213
|
+
w = info.width * options[:scale]
|
|
214
|
+
h = info.height * options[:scale]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
[w,h]
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def detect_image_format(content)
|
|
221
|
+
top = content[0,128]
|
|
222
|
+
|
|
223
|
+
if top[0, 3] == "\xff\xd8\xff"
|
|
224
|
+
return :jpg
|
|
225
|
+
elsif top[0, 8] == "\x89PNG\x0d\x0a\x1a\x0a"
|
|
226
|
+
return :png
|
|
227
|
+
else
|
|
228
|
+
raise ArgumentError, "Unsupported Image Type"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def image_registry
|
|
233
|
+
@image_registry ||= {}
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def next_image_id
|
|
237
|
+
@image_counter ||= 0
|
|
238
|
+
@image_counter += 1
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# encoding: ASCII-8BIT
|
|
2
|
+
|
|
3
|
+
# jpg.rb : Extracts the data from a JPG that is needed for embedding
|
|
4
|
+
#
|
|
5
|
+
# Copyright April 2008, James Healy. All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
8
|
+
|
|
9
|
+
require 'stringio'
|
|
10
|
+
|
|
11
|
+
module Prawn
|
|
12
|
+
module Images
|
|
13
|
+
# A convenience class that wraps the logic for extracting the parts
|
|
14
|
+
# of a PNG image that we need to embed them in a PDF
|
|
15
|
+
class JPG #:nodoc:
|
|
16
|
+
attr_reader :width, :height, :bits, :channels
|
|
17
|
+
|
|
18
|
+
JPEG_SOF_BLOCKS = %W(\xc0 \xc1 \xc2 \xc3 \xc5 \xc6 \xc7 \xc9 \xca \xcb \xcd \xce \xcf)
|
|
19
|
+
JPEG_APP_BLOCKS = %W(\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef)
|
|
20
|
+
|
|
21
|
+
# Process a new JPG image
|
|
22
|
+
#
|
|
23
|
+
# <tt>:data</tt>:: A string containing a full PNG file
|
|
24
|
+
def initialize(data)
|
|
25
|
+
data = StringIO.new(data.dup)
|
|
26
|
+
|
|
27
|
+
c_marker = "\xff" # Section marker.
|
|
28
|
+
data.read(2) # Skip the first two bytes of JPEG identifier.
|
|
29
|
+
loop do
|
|
30
|
+
marker, code, length = data.read(4).unpack('aan')
|
|
31
|
+
raise "JPEG marker not found!" if marker != c_marker
|
|
32
|
+
|
|
33
|
+
if JPEG_SOF_BLOCKS.include?(code)
|
|
34
|
+
@bits, @height, @width, @channels = data.read(6).unpack("CnnC")
|
|
35
|
+
break
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
buffer = data.read(length - 2)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# png.rb : Extracts the data from a PNG that is needed for embedding
|
|
4
|
+
#
|
|
5
|
+
# Based on some similar code in PDF::Writer by Austin Ziegler
|
|
6
|
+
#
|
|
7
|
+
# Copyright April 2008, James Healy. All Rights Reserved.
|
|
8
|
+
#
|
|
9
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
10
|
+
|
|
11
|
+
require 'stringio'
|
|
12
|
+
|
|
13
|
+
module Prawn
|
|
14
|
+
module Images
|
|
15
|
+
# A convenience class that wraps the logic for extracting the parts
|
|
16
|
+
# of a PNG image that we need to embed them in a PDF
|
|
17
|
+
class PNG #:nodoc:
|
|
18
|
+
attr_reader :palette, :img_data, :transparency
|
|
19
|
+
attr_reader :width, :height, :bits
|
|
20
|
+
attr_reader :color_type, :compression_method, :filter_method
|
|
21
|
+
attr_reader :interlace_method, :alpha_channel
|
|
22
|
+
|
|
23
|
+
# Process a new PNG image
|
|
24
|
+
#
|
|
25
|
+
# <tt>:data</tt>:: A string containing a full PNG file
|
|
26
|
+
def initialize(data)
|
|
27
|
+
data = StringIO.new(data.dup)
|
|
28
|
+
|
|
29
|
+
data.read(8) # Skip the default header
|
|
30
|
+
|
|
31
|
+
@palette = ""
|
|
32
|
+
@img_data = ""
|
|
33
|
+
|
|
34
|
+
loop do
|
|
35
|
+
chunk_size = data.read(4).unpack("N")[0]
|
|
36
|
+
section = data.read(4)
|
|
37
|
+
case section
|
|
38
|
+
when 'IHDR'
|
|
39
|
+
# we can grab other interesting values from here (like width,
|
|
40
|
+
# height, etc)
|
|
41
|
+
values = data.read(chunk_size).unpack("NNCCCCC")
|
|
42
|
+
|
|
43
|
+
@width = values[0]
|
|
44
|
+
@height = values[1]
|
|
45
|
+
@bits = values[2]
|
|
46
|
+
@color_type = values[3]
|
|
47
|
+
@compression_method = values[4]
|
|
48
|
+
@filter_method = values[5]
|
|
49
|
+
@interlace_method = values[6]
|
|
50
|
+
when 'PLTE'
|
|
51
|
+
@palette << data.read(chunk_size)
|
|
52
|
+
when 'IDAT'
|
|
53
|
+
@img_data << data.read(chunk_size)
|
|
54
|
+
when 'tRNS'
|
|
55
|
+
# This chunk can only occur once and it must occur after the
|
|
56
|
+
# PLTE chunk and before the IDAT chunk
|
|
57
|
+
@transparency = {}
|
|
58
|
+
case @color_type
|
|
59
|
+
when 3
|
|
60
|
+
# Indexed colour, RGB. Each byte in this chunk is an alpha for
|
|
61
|
+
# the palette index in the PLTE ("palette") chunk up until the
|
|
62
|
+
# last non-opaque entry. Set up an array, stretching over all
|
|
63
|
+
# palette entries which will be 0 (opaque) or 1 (transparent).
|
|
64
|
+
@transparency[:type] = 'indexed'
|
|
65
|
+
@transparency[:data] = data.read(chunk_size).unpack("C*")
|
|
66
|
+
when 0
|
|
67
|
+
# Greyscale. Corresponding to entries in the PLTE chunk.
|
|
68
|
+
# Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1
|
|
69
|
+
@transparency[:grayscale] = data.read(2).unpack("n")
|
|
70
|
+
@transparency[:type] = 'indexed'
|
|
71
|
+
when 2
|
|
72
|
+
# True colour with proper alpha channel.
|
|
73
|
+
@transparency[:rgb] = data.read(6).unpack("nnn")
|
|
74
|
+
end
|
|
75
|
+
when 'IEND'
|
|
76
|
+
# we've got everything we need, exit the loop
|
|
77
|
+
break
|
|
78
|
+
else
|
|
79
|
+
# unknown (or un-important) section, skip over it
|
|
80
|
+
data.seek(data.pos + chunk_size)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
data.read(4) # Skip the CRC
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# if our img_data contains alpha channel data, split it out
|
|
87
|
+
unfilter_image_data if @color_type == 6
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def paeth(a, b, c) # left, above, upper left
|
|
93
|
+
p = a + b - c
|
|
94
|
+
pa = (p - a).abs
|
|
95
|
+
pb = (p - b).abs
|
|
96
|
+
pc = (p - c).abs
|
|
97
|
+
|
|
98
|
+
return a if pa <= pb && pa <= pc
|
|
99
|
+
return b if pb <= pc
|
|
100
|
+
c
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def unfilter_image_data
|
|
104
|
+
data = Zlib::Inflate.inflate(@img_data).unpack 'C*'
|
|
105
|
+
@img_data = ""
|
|
106
|
+
@alpha_channel = ""
|
|
107
|
+
scanline_length = 4 * @width + 1 # for filter
|
|
108
|
+
row = 0
|
|
109
|
+
pixels = []
|
|
110
|
+
until data.empty? do
|
|
111
|
+
row_data = data.slice! 0, scanline_length
|
|
112
|
+
filter = row_data.shift
|
|
113
|
+
case filter
|
|
114
|
+
when 0 then # None
|
|
115
|
+
when 1 then # Sub
|
|
116
|
+
row_data.each_with_index do |byte, index|
|
|
117
|
+
left = index < 4 ? 0 : row_data[index - 4]
|
|
118
|
+
row_data[index] = (byte + left) % 256
|
|
119
|
+
#p [byte, left, row_data[index]]
|
|
120
|
+
end
|
|
121
|
+
when 2 then # Up
|
|
122
|
+
row_data.each_with_index do |byte, index|
|
|
123
|
+
col = index / 4
|
|
124
|
+
upper = row == 0 ? 0 : pixels[row-1][col][index % 4]
|
|
125
|
+
row_data[index] = (upper + byte) % 256
|
|
126
|
+
end
|
|
127
|
+
when 3 then # Average
|
|
128
|
+
row_data.each_with_index do |byte, index|
|
|
129
|
+
col = index / 4
|
|
130
|
+
upper = row == 0 ? 0 : pixels[row-1][col][index % 4]
|
|
131
|
+
left = index < 4 ? 0 : row_data[index - 4]
|
|
132
|
+
|
|
133
|
+
row_data[index] = (byte + ((left + upper)/2).floor) % 256
|
|
134
|
+
end
|
|
135
|
+
when 4 then # Paeth
|
|
136
|
+
left = upper = upper_left = nil
|
|
137
|
+
row_data.each_with_index do |byte, index|
|
|
138
|
+
col = index / 4
|
|
139
|
+
|
|
140
|
+
left = index < 4 ? 0 : row_data[index - 4]
|
|
141
|
+
if row == 0 then
|
|
142
|
+
upper = upper_left = 0
|
|
143
|
+
else
|
|
144
|
+
upper = pixels[row-1][col][index % 4]
|
|
145
|
+
upper_left = col == 0 ? 0 :
|
|
146
|
+
pixels[row-1][col-1][index % 4]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
paeth = paeth left, upper, upper_left
|
|
150
|
+
row_data[index] = (byte + paeth) % 256
|
|
151
|
+
#p [byte, paeth, row_data[index]]
|
|
152
|
+
end
|
|
153
|
+
else
|
|
154
|
+
raise ArgumentError, "Invalid filter algorithm #{filter}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
pixels << []
|
|
158
|
+
row_data.each_slice 4 do |slice|
|
|
159
|
+
pixels.last << slice
|
|
160
|
+
end
|
|
161
|
+
row += 1
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# convert the pixel data to seperate strings for colours and alpha
|
|
165
|
+
pixels.each do |row|
|
|
166
|
+
row.each do |pixel|
|
|
167
|
+
@img_data << pixel[0,3].pack("C*")
|
|
168
|
+
@alpha_channel << pixel[3]
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# compress the data
|
|
173
|
+
@img_data = Zlib::Deflate.deflate(@img_data)
|
|
174
|
+
@alpha_channel = Zlib::Deflate.deflate(@alpha_channel)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|