prawn 0.13.0 → 0.13.1
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.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/README.md +1 -0
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.color +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.color +0 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.color +0 -0
- data/lib/pdf/core/object_store.rb +3 -15
- data/lib/pdf/core/pdf_object.rb +8 -33
- data/lib/prawn.rb +1 -2
- data/lib/prawn/document.rb +2 -3
- data/lib/prawn/encoding.rb +1 -2
- data/lib/prawn/font/afm.rb +70 -29
- data/lib/prawn/font/ttf.rb +10 -2
- data/lib/prawn/images/jpg.rb +9 -10
- data/lib/prawn/images/png.rb +46 -118
- data/lib/prawn/text/formatted/arranger.rb +1 -5
- data/lib/prawn/text/formatted/box.rb +1 -1
- data/lib/prawn/text/formatted/fragment.rb +5 -11
- data/lib/prawn/text/formatted/line_wrap.rb +4 -25
- data/lib/prawn/text/formatted/wrap.rb +1 -4
- data/manual/example_file.rb +2 -7
- data/manual/manual/manual.rb +1 -1
- data/prawn.gemspec +0 -1
- data/spec/document_spec.rb +5 -7
- data/spec/extensions/encoding_helpers.rb +2 -3
- data/spec/filters_spec.rb +1 -1
- data/spec/font_spec.rb +3 -2
- data/spec/formatted_text_box_spec.rb +14 -25
- data/spec/images_spec.rb +2 -6
- data/spec/line_wrap_spec.rb +2 -2
- data/spec/outline_spec.rb +10 -10
- data/spec/png_spec.rb +9 -12
- data/spec/text_at_spec.rb +11 -26
- data/spec/text_box_spec.rb +6 -2
- data/spec/text_spec.rb +13 -27
- metadata +5 -20
- data/data/images/16bit.dat +0 -0
- data/data/images/dice.dat +0 -0
- data/data/images/page_white_text.dat +0 -0
- data/lib/prawn/compatibility.rb +0 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97df184b88392e64c3213125e2e3d49ed8921dfd
|
4
|
+
data.tar.gz: ae198cc6d4e658db982a4fcab6119c6c082937d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcf93119b67b6497601c38ebdfce11c7ff22739941a5c2adafe7f0f2636bfeefdb86f7fd6a35285e387bc77dc76896c87b1120e44ce3e357ce99754ac0380d52
|
7
|
+
data.tar.gz: afbfa0adfca9f371ada957a127e7737f6248a3be75f99fc401d1081199d22e9fdeac62e13ea752dd24418c76e29d6aaa3aad4e4bf503433dc585169f03b52e47
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Prawn: Fast, Nimble PDF Generation For Ruby
|
2
2
|
|
3
3
|
[](http://travis-ci.org/prawnpdf/prawn)
|
4
|
+
[](http://badge.fury.io/rb/prawn)
|
4
5
|
|
5
6
|
Prawn is a pure Ruby PDF generation library that provides a lot of great functionality while trying to remain simple and reasonably performant. Here are some of the important features we provide:
|
6
7
|
|
data/data/images/16bit.alpha
CHANGED
Binary file
|
Binary file
|
data/data/images/dice.alpha
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -299,21 +299,9 @@ module PDF
|
|
299
299
|
end
|
300
300
|
end
|
301
301
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
str.unpack("U*")
|
306
|
-
true
|
307
|
-
rescue
|
308
|
-
false
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
312
|
-
ruby_19 do
|
313
|
-
def is_utf8?(str)
|
314
|
-
str.force_encoding("utf-8")
|
315
|
-
str.valid_encoding?
|
316
|
-
end
|
302
|
+
def is_utf8?(str)
|
303
|
+
str.force_encoding(::Encoding::UTF_8)
|
304
|
+
str.valid_encoding?
|
317
305
|
end
|
318
306
|
end
|
319
307
|
end
|
data/lib/pdf/core/pdf_object.rb
CHANGED
@@ -12,40 +12,15 @@ module PDF
|
|
12
12
|
module Core
|
13
13
|
module_function
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
"\xFE\xFF".force_encoding("UTF-16BE") + str.encode("UTF-16BE")
|
19
|
-
end
|
20
|
-
|
21
|
-
# encodes any string into a hex representation. The result is a string
|
22
|
-
# with only 0-9 and a-f characters. That result is valid ASCII so tag
|
23
|
-
# it as such to account for behaviour of different ruby VMs
|
24
|
-
def string_to_hex(str)
|
25
|
-
str.unpack("H*").first.force_encoding("ascii")
|
26
|
-
end
|
27
|
-
else
|
28
|
-
# Ruby 1.8
|
29
|
-
def utf8_to_utf16(str)
|
30
|
-
utf16 = "\xFE\xFF"
|
31
|
-
|
32
|
-
str.codepoints do |cp|
|
33
|
-
if cp < 0x10000 # Basic Multilingual Plane
|
34
|
-
utf16 << [cp].pack("n")
|
35
|
-
else
|
36
|
-
# pull out high/low 10 bits
|
37
|
-
hi, lo = (cp - 0x10000).divmod(2**10)
|
38
|
-
# encode a surrogate pair
|
39
|
-
utf16 << [0xD800 + hi, 0xDC00 + lo].pack("n*")
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
utf16
|
44
|
-
end
|
15
|
+
def utf8_to_utf16(str)
|
16
|
+
"\xFE\xFF".force_encoding(::Encoding::UTF_16BE) + str.encode(::Encoding::UTF_16BE)
|
17
|
+
end
|
45
18
|
|
46
|
-
|
47
|
-
|
48
|
-
|
19
|
+
# encodes any string into a hex representation. The result is a string
|
20
|
+
# with only 0-9 and a-f characters. That result is valid ASCII so tag
|
21
|
+
# it as such to account for behaviour of different ruby VMs
|
22
|
+
def string_to_hex(str)
|
23
|
+
str.unpack("H*").first.force_encoding(::Encoding::US_ASCII)
|
49
24
|
end
|
50
25
|
|
51
26
|
# Serializes Ruby objects to their PDF equivalents. Most primitive objects
|
data/lib/prawn.rb
CHANGED
@@ -20,7 +20,7 @@ end
|
|
20
20
|
require "set"
|
21
21
|
|
22
22
|
module Prawn
|
23
|
-
VERSION = "0.13.
|
23
|
+
VERSION = "0.13.1"
|
24
24
|
|
25
25
|
extend self
|
26
26
|
|
@@ -73,7 +73,6 @@ module Prawn
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
require_relative "prawn/compatibility"
|
77
76
|
require_relative "prawn/errors"
|
78
77
|
|
79
78
|
require_relative "pdf/core"
|
data/lib/prawn/document.rb
CHANGED
@@ -370,7 +370,7 @@ module Prawn
|
|
370
370
|
render_trailer(output)
|
371
371
|
if output.instance_of?(StringIO)
|
372
372
|
str = output.string
|
373
|
-
str.force_encoding(
|
373
|
+
str.force_encoding(::Encoding::ASCII_8BIT)
|
374
374
|
return str
|
375
375
|
else
|
376
376
|
return nil
|
@@ -382,8 +382,7 @@ module Prawn
|
|
382
382
|
# pdf.render_file "foo.pdf"
|
383
383
|
#
|
384
384
|
def render_file(filename)
|
385
|
-
|
386
|
-
File.open(filename,mode) { |f| render(f) }
|
385
|
+
File.open(filename, "wb") { |f| render(f) }
|
387
386
|
end
|
388
387
|
|
389
388
|
# The bounds method returns the current bounding box you are currently in,
|
data/lib/prawn/encoding.rb
CHANGED
@@ -108,8 +108,7 @@ module Prawn
|
|
108
108
|
private
|
109
109
|
|
110
110
|
def load_mapping
|
111
|
-
|
112
|
-
File.open(@mapping_file, mode) do |f|
|
111
|
+
File.open(@mapping_file, "r:BINARY") do |f|
|
113
112
|
f.each do |l|
|
114
113
|
_, single_byte, unicode = *l.match(/([0-9A-Za-z]+);([0-9A-F]{4})/)
|
115
114
|
self.class.mapping["0x#{unicode}".hex] = "0x#{single_byte}".hex if single_byte
|
data/lib/prawn/font/afm.rb
CHANGED
@@ -7,7 +7,6 @@
|
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
9
|
require 'prawn/encoding'
|
10
|
-
require 'afm'
|
11
10
|
|
12
11
|
module Prawn
|
13
12
|
class Font
|
@@ -49,21 +48,23 @@ module Prawn
|
|
49
48
|
file_name << ".afm" unless file_name =~ /\.afm$/
|
50
49
|
file_name = file_name[0] == ?/ ? file_name : find_font(file_name)
|
51
50
|
|
52
|
-
font_data = @@font_data[file_name] ||=
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
51
|
+
font_data = @@font_data[file_name] ||= parse_afm(file_name)
|
52
|
+
@glyph_widths = font_data[:glyph_widths]
|
53
|
+
@glyph_table = font_data[:glyph_table]
|
54
|
+
@bounding_boxes = font_data[:bounding_boxes]
|
55
|
+
@kern_pairs = font_data[:kern_pairs]
|
56
|
+
@kern_pair_table = font_data[:kern_pair_table]
|
57
|
+
@attributes = font_data[:attributes]
|
57
58
|
|
58
|
-
@ascender = @attributes["
|
59
|
-
@descender = @attributes["
|
59
|
+
@ascender = @attributes["ascender"].to_i
|
60
|
+
@descender = @attributes["descender"].to_i
|
60
61
|
@line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
|
61
62
|
end
|
62
63
|
|
63
64
|
# The font bbox, as an array of integers
|
64
65
|
#
|
65
66
|
def bbox
|
66
|
-
@bbox ||= @attributes['
|
67
|
+
@bbox ||= @attributes['fontbbox'].split(/\s+/).map { |e| Integer(e) }
|
67
68
|
end
|
68
69
|
|
69
70
|
# NOTE: String *must* be encoded as WinAnsi
|
@@ -141,7 +142,7 @@ module Prawn
|
|
141
142
|
end
|
142
143
|
|
143
144
|
def symbolic?
|
144
|
-
attributes["
|
145
|
+
attributes["characterset"] == "Special"
|
145
146
|
end
|
146
147
|
|
147
148
|
def find_font(file)
|
@@ -152,6 +153,61 @@ module Prawn
|
|
152
153
|
self.class.metrics_path.join("\n")
|
153
154
|
end
|
154
155
|
|
156
|
+
def parse_afm(file_name)
|
157
|
+
data = {:glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {}}
|
158
|
+
section = []
|
159
|
+
|
160
|
+
File.foreach(file_name) do |line|
|
161
|
+
case line
|
162
|
+
when /^Start(\w+)/
|
163
|
+
section.push $1
|
164
|
+
next
|
165
|
+
when /^End(\w+)/
|
166
|
+
section.pop
|
167
|
+
next
|
168
|
+
end
|
169
|
+
|
170
|
+
case section
|
171
|
+
when ["FontMetrics", "CharMetrics"]
|
172
|
+
next unless line =~ /^CH?\s/
|
173
|
+
|
174
|
+
name = line[/\bN\s+(\.?\w+)\s*;/, 1]
|
175
|
+
data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
|
176
|
+
data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
|
177
|
+
when ["FontMetrics", "KernData", "KernPairs"]
|
178
|
+
next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
|
179
|
+
data[:kern_pairs][[$1, $2]] = $3.to_i
|
180
|
+
when ["FontMetrics", "KernData", "TrackKern"],
|
181
|
+
["FontMetrics", "Composites"]
|
182
|
+
next
|
183
|
+
else
|
184
|
+
parse_generic_afm_attribute(line, data)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# process data parsed from AFM file to build tables which
|
189
|
+
# will be used when measuring and kerning text
|
190
|
+
data[:glyph_table] = (0..255).map do |i|
|
191
|
+
data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
|
192
|
+
end
|
193
|
+
|
194
|
+
character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
|
195
|
+
data[:kern_pair_table] = data[:kern_pairs].inject({}) do |h,p|
|
196
|
+
h[p[0].map { |n| character_hash[n] }] = p[1]
|
197
|
+
h
|
198
|
+
end
|
199
|
+
|
200
|
+
data.each_value { |hash| hash.freeze }
|
201
|
+
data.freeze
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_generic_afm_attribute(line, hash)
|
205
|
+
line =~ /(^\w+)\s+(.*)/
|
206
|
+
key, value = $1.to_s.downcase, $2
|
207
|
+
|
208
|
+
hash[:attributes][key] = hash[:attributes][key] ? Array(hash[:attributes][key]) << value : value
|
209
|
+
end
|
210
|
+
|
155
211
|
# converts a string into an array with spacing offsets
|
156
212
|
# bewteen characters that need to be kerned
|
157
213
|
#
|
@@ -172,27 +228,12 @@ module Prawn
|
|
172
228
|
|
173
229
|
kerned.map { |e|
|
174
230
|
e = (Array === e ? e.pack("C*") : e)
|
175
|
-
e.respond_to?(:force_encoding) ? e.force_encoding(
|
231
|
+
e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
|
176
232
|
}
|
177
233
|
end
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
kern_pairs.inject({}) do |h,p|
|
182
|
-
h[
|
183
|
-
[character_hash[p[0]], character_hash[p[1]]]
|
184
|
-
] = p[2]
|
185
|
-
h
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def build_glyph_table(font_data)
|
190
|
-
(0..255).map do |char|
|
191
|
-
metrics = font_data.metrics_for(char)
|
192
|
-
metrics ? metrics[:wx] : 0
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
234
|
+
|
235
|
+
private
|
236
|
+
|
196
237
|
def unscaled_width_of(string)
|
197
238
|
string.bytes.inject(0) do |s,r|
|
198
239
|
s + @glyph_table[r]
|
data/lib/prawn/font/ttf.rb
CHANGED
@@ -160,7 +160,15 @@ module Prawn
|
|
160
160
|
end
|
161
161
|
|
162
162
|
def normalize_encoding(text)
|
163
|
-
|
163
|
+
begin
|
164
|
+
text.encode(::Encoding::UTF_8)
|
165
|
+
rescue => e
|
166
|
+
puts e
|
167
|
+
raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
|
168
|
+
"#{text.encoding} can not be transparently converted to UTF-8. " +
|
169
|
+
"Please ensure the encoding of the string you are attempting " +
|
170
|
+
"to use is set correctly"
|
171
|
+
end
|
164
172
|
end
|
165
173
|
|
166
174
|
def glyph_present?(char)
|
@@ -171,7 +179,7 @@ module Prawn
|
|
171
179
|
# Returns the number of characters in +str+ (a UTF-8-encoded string).
|
172
180
|
#
|
173
181
|
def character_count(str)
|
174
|
-
str.
|
182
|
+
str.length
|
175
183
|
end
|
176
184
|
|
177
185
|
private
|
data/lib/prawn/images/jpg.rb
CHANGED
@@ -17,8 +17,7 @@ module Prawn
|
|
17
17
|
attr_reader :width, :height, :bits, :channels
|
18
18
|
attr_accessor :scaled_width, :scaled_height
|
19
19
|
|
20
|
-
JPEG_SOF_BLOCKS =
|
21
|
-
JPEG_APP_BLOCKS = %W(\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef)
|
20
|
+
JPEG_SOF_BLOCKS = [0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF]
|
22
21
|
|
23
22
|
def self.can_render?(image_blob)
|
24
23
|
image_blob[0, 3].unpack("C*") == [255, 216, 255]
|
@@ -29,22 +28,22 @@ module Prawn
|
|
29
28
|
# <tt>:data</tt>:: A binary string of JPEG data
|
30
29
|
#
|
31
30
|
def initialize(data)
|
32
|
-
@data = data
|
33
|
-
|
34
|
-
|
31
|
+
@data = data
|
32
|
+
d = StringIO.new(@data)
|
33
|
+
d.binmode
|
35
34
|
|
36
|
-
c_marker =
|
37
|
-
|
35
|
+
c_marker = 0xff # Section marker.
|
36
|
+
d.seek(2) # Skip the first two bytes of JPEG identifier.
|
38
37
|
loop do
|
39
|
-
marker, code, length =
|
38
|
+
marker, code, length = d.read(4).unpack('CCn')
|
40
39
|
raise "JPEG marker not found!" if marker != c_marker
|
41
40
|
|
42
41
|
if JPEG_SOF_BLOCKS.include?(code)
|
43
|
-
@bits, @height, @width, @channels =
|
42
|
+
@bits, @height, @width, @channels = d.read(6).unpack("CnnC")
|
44
43
|
break
|
45
44
|
end
|
46
45
|
|
47
|
-
|
46
|
+
d.seek(length - 2, IO::SEEK_CUR)
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
data/lib/prawn/images/png.rb
CHANGED
@@ -107,34 +107,17 @@ module Prawn
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
# number of bits used per pixel
|
111
|
-
#
|
112
|
-
def pixel_bitlength
|
113
|
-
if alpha_channel?
|
114
|
-
self.bits * (self.colors + 1)
|
115
|
-
else
|
116
|
-
self.bits * self.colors
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
110
|
# split the alpha channel data from the raw image data in images
|
121
111
|
# where it's required.
|
122
112
|
#
|
123
113
|
def split_alpha_channel!
|
124
|
-
|
114
|
+
split_image_data if alpha_channel?
|
125
115
|
end
|
126
116
|
|
127
117
|
def alpha_channel?
|
128
118
|
@color_type == 4 || @color_type == 6
|
129
119
|
end
|
130
120
|
|
131
|
-
# Adobe Reader can't handle 16-bit png channels -- chop off the second
|
132
|
-
# byte (least significant)
|
133
|
-
#
|
134
|
-
def alpha_channel_bits
|
135
|
-
8
|
136
|
-
end
|
137
|
-
|
138
121
|
# Build a PDF object representing this image in +document+, and return
|
139
122
|
# a Reference to it.
|
140
123
|
#
|
@@ -180,18 +163,14 @@ module Prawn
|
|
180
163
|
# append the actual image data to the object as a stream
|
181
164
|
obj << img_data
|
182
165
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
:
|
188
|
-
|
189
|
-
:Colors => colors,
|
190
|
-
:BitsPerComponent => bits,
|
191
|
-
:Columns => width
|
192
|
-
}
|
166
|
+
obj.stream.filters << {
|
167
|
+
:FlateDecode => {
|
168
|
+
:Predictor => 15,
|
169
|
+
:Colors => colors,
|
170
|
+
:BitsPerComponent => bits,
|
171
|
+
:Columns => width
|
193
172
|
}
|
194
|
-
|
173
|
+
}
|
195
174
|
|
196
175
|
# sort out the colours of the image
|
197
176
|
if palette.empty?
|
@@ -243,12 +222,20 @@ module Prawn
|
|
243
222
|
:Subtype => :Image,
|
244
223
|
:Height => height,
|
245
224
|
:Width => width,
|
246
|
-
:BitsPerComponent =>
|
225
|
+
:BitsPerComponent => bits,
|
247
226
|
:ColorSpace => :DeviceGray,
|
248
227
|
:Decode => [0, 1]
|
249
228
|
)
|
250
229
|
smask_obj.stream << alpha_channel
|
251
|
-
|
230
|
+
|
231
|
+
smask_obj.stream.filters << {
|
232
|
+
:FlateDecode => {
|
233
|
+
:Predictor => 15,
|
234
|
+
:Colors => 1,
|
235
|
+
:BitsPerComponent => bits,
|
236
|
+
:Columns => width
|
237
|
+
}
|
238
|
+
}
|
252
239
|
obj.data[:SMask] = smask_obj
|
253
240
|
end
|
254
241
|
|
@@ -270,99 +257,40 @@ module Prawn
|
|
270
257
|
|
271
258
|
private
|
272
259
|
|
273
|
-
def
|
274
|
-
|
260
|
+
def split_image_data
|
261
|
+
alpha_bytes = bits / 8
|
262
|
+
color_bytes = colors * bits / 8
|
275
263
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
pixel_bytes = pixel_bitlength / 8
|
280
|
-
scanline_length = pixel_bytes * self.width + 1
|
281
|
-
row = 0
|
282
|
-
pixels = []
|
283
|
-
row_data = [] # reused for each row of the image
|
284
|
-
paeth, pa, pb, pc = nil
|
285
|
-
|
286
|
-
data.bytes.each do |byte|
|
287
|
-
# accumulate a whole scanline of bytes, and then process it all at once
|
288
|
-
# we could do this with Enumerable#each_slice, but it allocates memory,
|
289
|
-
# and we are trying to avoid that
|
290
|
-
row_data << byte
|
291
|
-
next if row_data.length < scanline_length
|
292
|
-
|
293
|
-
filter = row_data.shift
|
294
|
-
case filter
|
295
|
-
when 0 # None
|
296
|
-
when 1 # Sub
|
297
|
-
row_data.each_with_index do |row_byte, index|
|
298
|
-
left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
|
299
|
-
row_data[index] = (row_byte + left) % 256
|
300
|
-
end
|
301
|
-
when 2 # Up
|
302
|
-
row_data.each_with_index do |row_byte, index|
|
303
|
-
col = (index / pixel_bytes).floor
|
304
|
-
upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
|
305
|
-
row_data[index] = (upper + row_byte) % 256
|
306
|
-
end
|
307
|
-
when 3 # Average
|
308
|
-
row_data.each_with_index do |row_byte, index|
|
309
|
-
col = (index / pixel_bytes).floor
|
310
|
-
upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
|
311
|
-
left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
|
264
|
+
scanline_length = (color_bytes + alpha_bytes) * self.width + 1
|
265
|
+
scanlines = @img_data.bytesize / scanline_length
|
266
|
+
pixels = self.width * self.height
|
312
267
|
|
313
|
-
|
314
|
-
|
315
|
-
when 4 # Paeth
|
316
|
-
left = upper = upper_left = nil
|
317
|
-
row_data.each_with_index do |row_byte, index|
|
318
|
-
col = (index / pixel_bytes).floor
|
319
|
-
|
320
|
-
left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
|
321
|
-
if row.zero?
|
322
|
-
upper = upper_left = 0
|
323
|
-
else
|
324
|
-
upper = pixels[row-1][col][index % pixel_bytes]
|
325
|
-
upper_left = col.zero? ? 0 :
|
326
|
-
pixels[row-1][col-1][index % pixel_bytes]
|
327
|
-
end
|
328
|
-
|
329
|
-
p = left + upper - upper_left
|
330
|
-
pa = (p - left).abs
|
331
|
-
pb = (p - upper).abs
|
332
|
-
pc = (p - upper_left).abs
|
333
|
-
|
334
|
-
paeth = if pa <= pb && pa <= pc
|
335
|
-
left
|
336
|
-
elsif pb <= pc
|
337
|
-
upper
|
338
|
-
else
|
339
|
-
upper_left
|
340
|
-
end
|
341
|
-
|
342
|
-
row_data[index] = (row_byte + paeth) % 256
|
343
|
-
end
|
344
|
-
else
|
345
|
-
raise ArgumentError, "Invalid filter algorithm #{filter}"
|
346
|
-
end
|
268
|
+
data = StringIO.new(@img_data)
|
269
|
+
data.binmode
|
347
270
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
end
|
271
|
+
color_data = [0x00].pack('C') * (pixels * color_bytes + scanlines)
|
272
|
+
color = StringIO.new(color_data)
|
273
|
+
color.binmode
|
274
|
+
|
275
|
+
@alpha_channel = [0x00].pack('C') * (pixels * alpha_bytes + scanlines)
|
276
|
+
alpha = StringIO.new(@alpha_channel)
|
277
|
+
alpha.binmode
|
356
278
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
279
|
+
scanlines.times do |line|
|
280
|
+
data.seek(line * scanline_length)
|
281
|
+
|
282
|
+
filter = data.getbyte
|
283
|
+
|
284
|
+
color.putc filter
|
285
|
+
alpha.putc filter
|
286
|
+
|
287
|
+
self.width.times do
|
288
|
+
color.write data.read(color_bytes)
|
289
|
+
alpha.write data.read(alpha_bytes)
|
364
290
|
end
|
365
291
|
end
|
292
|
+
|
293
|
+
@img_data = color_data
|
366
294
|
end
|
367
295
|
end
|
368
296
|
end
|