prawn 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
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