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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/README.md +1 -0
  4. data/data/images/16bit.alpha +0 -0
  5. data/data/images/16bit.color +0 -0
  6. data/data/images/dice.alpha +0 -0
  7. data/data/images/dice.color +0 -0
  8. data/data/images/page_white_text.alpha +0 -0
  9. data/data/images/page_white_text.color +0 -0
  10. data/lib/pdf/core/object_store.rb +3 -15
  11. data/lib/pdf/core/pdf_object.rb +8 -33
  12. data/lib/prawn.rb +1 -2
  13. data/lib/prawn/document.rb +2 -3
  14. data/lib/prawn/encoding.rb +1 -2
  15. data/lib/prawn/font/afm.rb +70 -29
  16. data/lib/prawn/font/ttf.rb +10 -2
  17. data/lib/prawn/images/jpg.rb +9 -10
  18. data/lib/prawn/images/png.rb +46 -118
  19. data/lib/prawn/text/formatted/arranger.rb +1 -5
  20. data/lib/prawn/text/formatted/box.rb +1 -1
  21. data/lib/prawn/text/formatted/fragment.rb +5 -11
  22. data/lib/prawn/text/formatted/line_wrap.rb +4 -25
  23. data/lib/prawn/text/formatted/wrap.rb +1 -4
  24. data/manual/example_file.rb +2 -7
  25. data/manual/manual/manual.rb +1 -1
  26. data/prawn.gemspec +0 -1
  27. data/spec/document_spec.rb +5 -7
  28. data/spec/extensions/encoding_helpers.rb +2 -3
  29. data/spec/filters_spec.rb +1 -1
  30. data/spec/font_spec.rb +3 -2
  31. data/spec/formatted_text_box_spec.rb +14 -25
  32. data/spec/images_spec.rb +2 -6
  33. data/spec/line_wrap_spec.rb +2 -2
  34. data/spec/outline_spec.rb +10 -10
  35. data/spec/png_spec.rb +9 -12
  36. data/spec/text_at_spec.rb +11 -26
  37. data/spec/text_box_spec.rb +6 -2
  38. data/spec/text_spec.rb +13 -27
  39. metadata +5 -20
  40. data/data/images/16bit.dat +0 -0
  41. data/data/images/dice.dat +0 -0
  42. data/data/images/page_white_text.dat +0 -0
  43. data/lib/prawn/compatibility.rb +0 -91
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad2fdbe5f0c7d9075d0a6467217a996304aa4983
4
- data.tar.gz: 6db5219bdb341f2c8d36c3cc0aca48720adbdccc
3
+ metadata.gz: 97df184b88392e64c3213125e2e3d49ed8921dfd
4
+ data.tar.gz: ae198cc6d4e658db982a4fcab6119c6c082937d1
5
5
  SHA512:
6
- metadata.gz: c2795eaa023f044fdf99b91f79a4e56f181c8dcb13fd73112dcf181156234230117e17221521634c728e60c92a7f05a607f16cec47cd72eff9a77995e91f750f
7
- data.tar.gz: cdf2c886e7bea9946289a11fa1f33c4574884ca65d2eebbaec6967016bc31aee008a9048fccecb73756a4950a6b4e686c838ddbf5e3c3c258bd1dd2ed6fea379
6
+ metadata.gz: fcf93119b67b6497601c38ebdfce11c7ff22739941a5c2adafe7f0f2636bfeefdb86f7fd6a35285e387bc77dc76896c87b1120e44ce3e357ce99754ac0380d52
7
+ data.tar.gz: afbfa0adfca9f371ada957a127e7737f6248a3be75f99fc401d1081199d22e9fdeac62e13ea752dd24418c76e29d6aaa3aad4e4bf503433dc585169f03b52e47
data/Gemfile CHANGED
@@ -3,7 +3,6 @@ source "https://rubygems.org"
3
3
  gem "ttfunk", "~>1.0.3"
4
4
  gem "pdf-reader", "~> 1.2"
5
5
  gem "ruby-rc4"
6
- gem "afm"
7
6
 
8
7
  group :development do
9
8
  gem "coderay", "~> 1.0.7"
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Prawn: Fast, Nimble PDF Generation For Ruby
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/prawnpdf/prawn.png)](http://travis-ci.org/prawnpdf/prawn)
4
+ [![Gem Version](https://badge.fury.io/rb/prawn.png)](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
 
Binary file
Binary file
Binary file
Binary file
@@ -299,21 +299,9 @@ module PDF
299
299
  end
300
300
  end
301
301
 
302
- ruby_18 do
303
- def is_utf8?(str)
304
- begin
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
@@ -12,40 +12,15 @@ module PDF
12
12
  module Core
13
13
  module_function
14
14
 
15
- if "".respond_to?(:encode)
16
- # Ruby 1.9+
17
- def utf8_to_utf16(str)
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
- def string_to_hex(str)
47
- str.unpack("H*").first
48
- end
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
@@ -20,7 +20,7 @@ end
20
20
  require "set"
21
21
 
22
22
  module Prawn
23
- VERSION = "0.13.0"
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"
@@ -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("ASCII-8BIT") if str.respond_to?(: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
- Kernel.const_defined?("Encoding") ? mode = "wb:ASCII-8BIT" : mode = "wb"
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,
@@ -108,8 +108,7 @@ module Prawn
108
108
  private
109
109
 
110
110
  def load_mapping
111
- RUBY_VERSION >= "1.9" ? mode = "r:BINARY" : mode = "r"
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
@@ -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] ||= ::AFM::Font.new(file_name)
53
- @glyph_table = build_glyph_table(font_data)
54
- @kern_pairs = font_data.kern_pairs
55
- @kern_pair_table = build_kern_pair_table(@kern_pairs)
56
- @attributes = font_data.metadata
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["Ascender"].to_i
59
- @descender = @attributes["Descender"].to_i
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['FontBBox'].split(/\s+/).map { |e| Integer(e) }
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["CharacterSet"] == "Special"
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("Windows-1252") : e
231
+ e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
176
232
  }
177
233
  end
178
-
179
- def build_kern_pair_table(kern_pairs)
180
- character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
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]
@@ -160,7 +160,15 @@ module Prawn
160
160
  end
161
161
 
162
162
  def normalize_encoding(text)
163
- text.normalize_to_utf8
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.unicode_length
182
+ str.length
175
183
  end
176
184
 
177
185
  private
@@ -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 = %W(\xc0 \xc1 \xc2 \xc3 \xc5 \xc6 \xc7 \xc9 \xca \xcb \xcd \xce \xcf)
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.dup
33
- ruby_19 { data.force_encoding("binary") }
34
- data = StringIO.new(data)
31
+ @data = data
32
+ d = StringIO.new(@data)
33
+ d.binmode
35
34
 
36
- c_marker = "\xff" # Section marker.
37
- data.read(2) # Skip the first two bytes of JPEG identifier.
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 = data.read(4).unpack('aan')
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 = data.read(6).unpack("CnnC")
42
+ @bits, @height, @width, @channels = d.read(6).unpack("CnnC")
44
43
  break
45
44
  end
46
45
 
47
- data.read(length - 2)
46
+ d.seek(length - 2, IO::SEEK_CUR)
48
47
  end
49
48
  end
50
49
 
@@ -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
- unfilter_image_data if alpha_channel?
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
- if alpha_channel
184
- obj.stream.compress!
185
- else
186
- obj.stream.filters << {
187
- :FlateDecode => {
188
- :Predictor => 15,
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
- end
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 => alpha_channel_bits,
225
+ :BitsPerComponent => bits,
247
226
  :ColorSpace => :DeviceGray,
248
227
  :Decode => [0, 1]
249
228
  )
250
229
  smask_obj.stream << alpha_channel
251
- smask_obj.stream.compress!
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 unfilter_image_data
274
- data = @img_data.dup
260
+ def split_image_data
261
+ alpha_bytes = bits / 8
262
+ color_bytes = colors * bits / 8
275
263
 
276
- @img_data = ""
277
- @alpha_channel = ""
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
- row_data[index] = (row_byte + ((left + upper)/2).floor) % 256
314
- end
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
- s = []
349
- row_data.each_slice pixel_bytes do |slice|
350
- s << slice
351
- end
352
- pixels << s
353
- row += 1
354
- row_data.clear
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
- # convert the pixel data to separate strings for colours and alpha
358
- color_byte_size = self.colors * self.bits / 8
359
- alpha_byte_size = alpha_channel_bits / 8
360
- pixels.each do |this_row|
361
- this_row.each do |pixel|
362
- @img_data << pixel[0, color_byte_size].pack("C*")
363
- @alpha_channel << pixel[color_byte_size, alpha_byte_size].pack("C*")
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