prawn 0.1.1 → 0.1.2

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/Rakefile CHANGED
@@ -1,16 +1,18 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
- require 'spec/rake/spectask'
3
+ require 'rake/testtask'
4
4
  require "rake/rdoctask"
5
5
  require "rake/gempackagetask"
6
6
 
7
- PRAWN_VERSION = "0.1.1"
7
+ PRAWN_VERSION = "0.1.2"
8
8
 
9
- task :default => [:spec]
10
-
11
- desc "Run all specs"
12
- Spec::Rake::SpecTask.new('spec') do |t|
13
- t.spec_files = FileList['spec/**/*.rb']
9
+ task :default => [:test]
10
+
11
+ desc "Run all tests, test-spec and mocha required"
12
+ Rake::TestTask.new do |test|
13
+ test.libs << "spec"
14
+ test.test_files = Dir[ "spec/*_spec.rb" ]
15
+ test.verbose = true
14
16
  end
15
17
 
16
18
  desc "Show library's code statistics"
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ x^E�A�1DQ$ !p$DB$D������3�_���,(�d�_ˣ��j�wa����W9$촙w�աɳ�Km��Wg��۹#���M�h��D�#C�_[A���ş�z��r<:�<l�5V�(�L����c�2w?��s3 Q���b ��;��O����ݼUD�W�
Binary file
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ # PNG files come in different flavours - 5 of them. This example embeds
4
+ # one of each type as proof that they all work.
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
7
+ require "prawn"
8
+
9
+ images = [
10
+ ["Type 0", "#{Prawn::BASEDIR}/data/images/web-links.png"],
11
+ ["Type 2", "#{Prawn::BASEDIR}/data/images/ruport.png"],
12
+ ["Type 3", "#{Prawn::BASEDIR}/data/images/rails.png"],
13
+ ["Type 4", "#{Prawn::BASEDIR}/data/images/page_white_text.png"],
14
+ ["Type 6", "#{Prawn::BASEDIR}/data/images/dice.png"],
15
+ ]
16
+
17
+ Prawn::Document.generate("png_types.pdf", :page_size => "A5") do
18
+ images.each do |header, file|
19
+ start_new_page unless header.include?("0")
20
+ text header
21
+ image file, :at => [50,450]
22
+ end
23
+ end
@@ -23,11 +23,12 @@ end
23
23
 
24
24
  require 'ttf'
25
25
 
26
- module Prawn
26
+ module Prawn
27
27
  file = __FILE__
28
28
  file = File.readlink(file) if File.symlink?(file)
29
29
  dir = File.dirname(file)
30
30
 
31
31
  # The base source directory for Prawn as installed on the system
32
- BASEDIR = File.expand_path(File.join(dir, '..'))
32
+ BASEDIR = File.expand_path(File.join(dir, '..'))
33
+ VERSION = "0.1.2"
33
34
  end
@@ -59,6 +59,7 @@ module Prawn
59
59
  # <tt>:top_margin</tt>:: Sets the top margin in points [ 0.5 inch]
60
60
  # <tt>:bottom_margin</tt>:: Sets the bottom margin in points [0.5 inch]
61
61
  # <tt>:skip_page_creation</tt>:: Creates a document without starting the first page [false]
62
+ # <tt>:compress</tt>:: Compresses content streams before rendering them [false]
62
63
  #
63
64
  #
64
65
  # # New document, US Letter paper, portrait orientation
@@ -77,9 +78,10 @@ module Prawn
77
78
  @pages = ref(:Type => :Pages, :Count => 0, :Kids => [])
78
79
  @root = ref(:Type => :Catalog, :Pages => @pages)
79
80
  @page_start_proc = options[:on_page_start]
80
- @page_stop_proc = options[:on_page_end]
81
+ @page_stop_proc = options[:on_page_stop]
81
82
  @page_size = options[:page_size] || "LETTER"
82
83
  @page_layout = options[:page_layout] || :portrait
84
+ @compress = options[:compress] || false
83
85
 
84
86
  @margins = { :left => options[:left_margin] || 36,
85
87
  :right => options[:right_margin] || 36,
@@ -239,6 +241,11 @@ module Prawn
239
241
  yield
240
242
  fields.each { |f| send("#{f}=", stored[f]) }
241
243
  end
244
+
245
+ def compression_enabled?
246
+ @compress
247
+ end
248
+
242
249
 
243
250
  private
244
251
 
@@ -286,6 +293,7 @@ module Prawn
286
293
  def finish_page_content
287
294
  @page_stop_proc[self] if @page_stop_proc
288
295
  add_content "Q"
296
+ @page_content.compress_stream if compression_enabled?
289
297
  @page_content.data[:Length] = @page_content.stream.size
290
298
  end
291
299
 
@@ -37,16 +37,35 @@ module Prawn
37
37
  # text will be kerned by default. You can disable this feature by passing
38
38
  # <tt>:kerning => false</tt>.
39
39
  #
40
+ # == Encoding
41
+ #
40
42
  # Note that strings passed to this function should be encoded as UTF-8.
41
- # If you get unexpected characters appearing in your rendered
42
- # document, check this.
43
+ # If you get unexpected characters appearing in your rendered document,
44
+ # check this.
45
+ #
46
+ # If the current font is a built-in one, although the string must be
47
+ # encoded as UTF-8, only characters that are available in ISO-8859-1
48
+ # are allowed.
43
49
  #
44
50
  # If an empty box is rendered to your PDF instead of the character you
45
51
  # wanted it usually means the current font doesn't include that character.
46
52
  #
47
53
  def text(text,options={})
54
+ # ensure a valid font is selected
55
+ font "Helvetica" unless fonts[@font]
56
+
57
+ # we'll be messing with the strings encoding, don't change the users
58
+ # original string
59
+ text = text.dup
60
+
48
61
  # check the string is encoded sanely
49
- normalize_encoding(text)
62
+ # - UTF-8 for TTF fonts
63
+ # - ISO-8859-1 for Built-In fonts
64
+ if using_builtin_font?
65
+ normalize_builtin_encoding(text)
66
+ else
67
+ normalize_ttf_encoding(text)
68
+ end
50
69
 
51
70
  if options.key?(:kerning)
52
71
  options[:kerning] = false unless font_metrics.has_kerning_data?
@@ -54,9 +73,6 @@ module Prawn
54
73
  options[:kerning] = true if font_metrics.has_kerning_data?
55
74
  end
56
75
 
57
- # ensure a valid font is selected
58
- font "Helvetica" unless fonts[@font]
59
-
60
76
  return wrapped_text(text,options) unless options[:at]
61
77
 
62
78
  x,y = translate(options[:at])
@@ -151,7 +167,8 @@ module Prawn
151
167
  alias_method :font_size=, :font_size!
152
168
 
153
169
  private
154
-
170
+
171
+
155
172
  # The current font_size being used in the document.
156
173
  #
157
174
  def current_font_size
@@ -281,11 +298,25 @@ module Prawn
281
298
  return basename
282
299
  end
283
300
 
284
- def normalize_encoding(text)
301
+ # built-in fonts only work with latin encoding, so translate the string
302
+ def normalize_builtin_encoding(text)
303
+ if text.respond_to?(:encode!)
304
+ text.encode!("ISO-8859-1")
305
+ else
306
+ require 'iconv'
307
+ text.replace Iconv.conv('ISO-8859-1', 'utf-8', text)
308
+ end
309
+ rescue
310
+ raise Prawn::Errors::IncompatibleStringEncoding, "When using a " +
311
+ "builtin font, only characters that exist in " +
312
+ "WinAnsi/ISO-8859-1 are allowed."
313
+ end
314
+
315
+ def normalize_ttf_encoding(text)
285
316
  # TODO: if the current font is a built in one, we can't use the utf-8
286
317
  # string provided by the user. We should convert it to WinAnsi or
287
318
  # MacRoman or some such.
288
- if text.respond_to?(:"encode!")
319
+ if text.respond_to?(:encode!)
289
320
  # if we're running under a M17n aware VM, ensure the string provided is
290
321
  # UTF-8 (by converting it if necessary)
291
322
  begin
@@ -316,7 +347,7 @@ module Prawn
316
347
  fonts[name] ||= ref(:Type => :Font,
317
348
  :Subtype => :Type1,
318
349
  :BaseFont => name.to_sym,
319
- :Encoding => :MacRomanEncoding)
350
+ :Encoding => :WinAnsiEncoding)
320
351
  return name
321
352
  end
322
353
 
@@ -341,6 +372,9 @@ module Prawn
341
372
  @fonts ||= {}
342
373
  end
343
374
 
375
+ def using_builtin_font?
376
+ fonts[@font].data[:Subtype] == :Type1
377
+ end
344
378
  end
345
379
  end
346
380
  end
@@ -89,6 +89,8 @@ module Prawn
89
89
  Float(bbox[3] - bbox[1]) * font_size / 1000.0
90
90
  end
91
91
 
92
+ # calculates the width of the supplied string.
93
+ # String *must* be encoded as iso-8859-1
92
94
  def string_width(string, font_size, options = {})
93
95
  scale = font_size / 1000.0
94
96
 
@@ -101,14 +103,18 @@ module Prawn
101
103
  end
102
104
  end
103
105
  else
104
- string.unpack("U*").inject(0) do |s,r|
106
+ string.unpack("C*").inject(0) do |s,r|
105
107
  s + latin_glyphs_table[r]
106
108
  end * scale
107
109
  end
108
110
  end
109
111
 
112
+ # converts a string into an array with spacing offsets
113
+ # bewteen characters that need to be kerned
114
+ #
115
+ # String *must* be encoded as iso-8859-1
110
116
  def kern(string)
111
- kerned = string.unpack("U*").inject([]) do |a,r|
117
+ kerned = string.unpack("C*").inject([]) do |a,r|
112
118
  if a.last.is_a? Array
113
119
  if kern = latin_kern_pairs_table[[a.last.last, r]]
114
120
  a << kern << [r]
@@ -122,7 +128,8 @@ module Prawn
122
128
  end
123
129
 
124
130
  kerned.map { |r|
125
- i = r.is_a?(Array) ? r.pack("U*") : r
131
+ i = r.is_a?(Array) ? r.pack("C*") : r
132
+ i.force_encoding("ISO-8859-1") if i.respond_to?(:force_encoding)
126
133
  i.is_a?(Numeric) ? -i : i
127
134
  }
128
135
  end
@@ -178,6 +185,10 @@ module Prawn
178
185
  false
179
186
  end
180
187
 
188
+ # perform any changes to the string that need to happen
189
+ # before it is rendered to the canvas
190
+ #
191
+ # String *must* be encoded as iso-8859-1
181
192
  def convert_text(text, options={})
182
193
  options[:kerning] ? kern(text) : text
183
194
  end
@@ -145,22 +145,12 @@ module Prawn
145
145
  raise ArgumentError, 'PNG uses more than 8 bits'
146
146
  end
147
147
 
148
- case png.color_type
149
- when 0
150
- ncolor = 1
148
+ case png.pixel_bytes
149
+ when 1
151
150
  color = :DeviceGray
152
- when 2
153
- ncolor = 3
154
- color = :DeviceRGB
155
151
  when 3
156
- ncolor = 1
157
- color = :DeviceRGB
158
- when 6
159
- ncolor = 3
160
- color = :DeviceRGB
161
- else
162
- raise ArgumentError, "PNG has unsupported color type"
163
- end
152
+ color = :DeviceRGB
153
+ end
164
154
 
165
155
  # build the image dict
166
156
  obj = ref(:Type => :XObject,
@@ -175,7 +165,7 @@ module Prawn
175
165
 
176
166
  unless png.alpha_channel
177
167
  obj.data[:DecodeParms] = {:Predictor => 15,
178
- :Colors => ncolor,
168
+ :Colors => png.pixel_bytes,
179
169
  :Columns => png.width}
180
170
  end
181
171
 
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # encoding: ASCII-8BIT
2
2
 
3
3
  # png.rb : Extracts the data from a PNG that is needed for embedding
4
4
  #
@@ -84,11 +84,22 @@ module Prawn
84
84
  end
85
85
 
86
86
  # if our img_data contains alpha channel data, split it out
87
- unfilter_image_data if @color_type == 6
87
+ unfilter_image_data if alpha_channel?
88
+ end
89
+
90
+ def pixel_bytes
91
+ case @color_type
92
+ when 0, 4 then 1
93
+ when 1, 2, 6 then 3
94
+ end
88
95
  end
89
96
 
90
97
  private
91
98
 
99
+ def alpha_channel?
100
+ @color_type == 4 || @color_type == 6
101
+ end
102
+
92
103
  def paeth(a, b, c) # left, above, upper left
93
104
  p = a + b - c
94
105
  pa = (p - a).abs
@@ -104,46 +115,49 @@ module Prawn
104
115
  data = Zlib::Inflate.inflate(@img_data).unpack 'C*'
105
116
  @img_data = ""
106
117
  @alpha_channel = ""
107
- scanline_length = 4 * @width + 1 # for filter
118
+
119
+ # each pixel has the color bytes, plus a byte of alpha channel
120
+ pixel_length = pixel_bytes + 1
121
+ scanline_length = pixel_length * @width + 1 # for filter
108
122
  row = 0
109
123
  pixels = []
110
124
  until data.empty? do
111
125
  row_data = data.slice! 0, scanline_length
112
126
  filter = row_data.shift
113
127
  case filter
114
- when 0 then # None
115
- when 1 then # Sub
128
+ when 0 # None
129
+ when 1 # Sub
116
130
  row_data.each_with_index do |byte, index|
117
- left = index < 4 ? 0 : row_data[index - 4]
131
+ left = index < pixel_length ? 0 : row_data[index - pixel_length]
118
132
  row_data[index] = (byte + left) % 256
119
133
  #p [byte, left, row_data[index]]
120
134
  end
121
- when 2 then # Up
135
+ when 2 # Up
122
136
  row_data.each_with_index do |byte, index|
123
- col = index / 4
124
- upper = row == 0 ? 0 : pixels[row-1][col][index % 4]
137
+ col = index / pixel_length
138
+ upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_length]
125
139
  row_data[index] = (upper + byte) % 256
126
140
  end
127
- when 3 then # Average
141
+ when 3 # Average
128
142
  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]
143
+ col = index / pixel_length
144
+ upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_length]
145
+ left = index < pixel_length ? 0 : row_data[index - pixel_length]
132
146
 
133
147
  row_data[index] = (byte + ((left + upper)/2).floor) % 256
134
148
  end
135
- when 4 then # Paeth
149
+ when 4 # Paeth
136
150
  left = upper = upper_left = nil
137
151
  row_data.each_with_index do |byte, index|
138
- col = index / 4
152
+ col = index / pixel_length
139
153
 
140
- left = index < 4 ? 0 : row_data[index - 4]
154
+ left = index < pixel_length ? 0 : row_data[index - pixel_length]
141
155
  if row == 0 then
142
156
  upper = upper_left = 0
143
157
  else
144
- upper = pixels[row-1][col][index % 4]
158
+ upper = pixels[row-1][col][index % pixel_length]
145
159
  upper_left = col == 0 ? 0 :
146
- pixels[row-1][col-1][index % 4]
160
+ pixels[row-1][col-1][index % pixel_length]
147
161
  end
148
162
 
149
163
  paeth = paeth left, upper, upper_left
@@ -155,7 +169,7 @@ module Prawn
155
169
  end
156
170
 
157
171
  pixels << []
158
- row_data.each_slice 4 do |slice|
172
+ row_data.each_slice pixel_length do |slice|
159
173
  pixels.last << slice
160
174
  end
161
175
  row += 1
@@ -164,8 +178,8 @@ module Prawn
164
178
  # convert the pixel data to seperate strings for colours and alpha
165
179
  pixels.each do |row|
166
180
  row.each do |pixel|
167
- @img_data << pixel[0,3].pack("C*")
168
- @alpha_channel << pixel[3]
181
+ @img_data << pixel[0,pixel_bytes].pack("C*")
182
+ @alpha_channel << pixel.last
169
183
  end
170
184
  end
171
185
 
@@ -6,6 +6,8 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
+ require 'zlib'
10
+
9
11
  module Prawn
10
12
 
11
13
  class Reference #:nodoc:
@@ -17,6 +19,7 @@ module Prawn
17
19
  @identifier = id
18
20
  @gen = 0
19
21
  @data = data
22
+ @compressed = false
20
23
  end
21
24
 
22
25
  def object
@@ -29,13 +32,19 @@ module Prawn
29
32
  end
30
33
 
31
34
  def <<(data)
35
+ raise 'Cannot add data to a stream that is compressed' if @compressed
32
36
  (@stream ||= "") << data
33
37
  end
34
38
 
35
39
  def to_s
36
40
  "#{@identifier} #{gen} R"
37
41
  end
38
-
42
+
43
+ def compress_stream
44
+ @stream = Zlib::Deflate.deflate(@stream)
45
+ @data[:Filter] = :FlateDecode
46
+ @compressed = true
47
+ end
39
48
  end
40
49
 
41
50
  module_function
@@ -11,7 +11,7 @@ describe "A bounding box" do
11
11
  @pdf.text "The rain in Spain falls mainly on the plains."
12
12
  end
13
13
 
14
- @pdf.y.should be_close(458.384, 0.001)
14
+ @pdf.y.should.be.close 458.384, 0.001
15
15
  end
16
16
 
17
17
  end
@@ -49,7 +49,7 @@ describe "When beginning each new page" do
49
49
  it "should execute the lambda specified by on_page_start" do
50
50
  on_start = mock("page_start_proc")
51
51
 
52
- on_start.should_receive(:[]).exactly(3).times
52
+ on_start.expects(:[]).times(3)
53
53
 
54
54
  pdf = Prawn::Document.new(:on_page_start => on_start)
55
55
  pdf.start_new_page
@@ -65,14 +65,38 @@ describe "When ending each page" do
65
65
 
66
66
  on_end = mock("page_end_proc")
67
67
 
68
- on_end.should_receive(:[]).exactly(3).times
68
+ on_end.expects(:[]).times(3)
69
69
 
70
- pdf = Prawn::Document.new(:on_page_end => on_end)
70
+ pdf = Prawn::Document.new(:on_page_stop => on_end)
71
71
  pdf.start_new_page
72
72
  pdf.start_new_page
73
73
  pdf.render
74
74
  end
75
75
 
76
+ it "should not compress the page content stream if compression is disabled" do
77
+
78
+ pdf = Prawn::Document.new(:compress => false)
79
+ content_stub = pdf.ref({})
80
+ content_stub.stubs(:compress_stream).returns(true)
81
+ content_stub.expects(:compress_stream).never
82
+
83
+ pdf.instance_variable_set("@page_content", content_stub)
84
+ pdf.text "Hi There" * 20
85
+ pdf.render
86
+ end
87
+
88
+ it "should compress the page content stream if compression is enabled" do
89
+
90
+ pdf = Prawn::Document.new(:compress => true)
91
+ content_stub = pdf.ref({})
92
+ content_stub.stubs(:compress_stream).returns(true)
93
+ content_stub.expects(:compress_stream).once
94
+
95
+ pdf.instance_variable_set("@page_content", content_stub)
96
+ pdf.text "Hi There" * 20
97
+ pdf.render
98
+ end
99
+
76
100
  end
77
101
 
78
102
  class PageDetails
@@ -132,8 +156,8 @@ describe "The mask() feature" do
132
156
  @pdf.mask(:y, :line_width) do
133
157
  @pdf.y = y + 1
134
158
  @pdf.line_width = line_width + 1
135
- @pdf.y.should_not == y
136
- @pdf.line_width.should_not == line_width
159
+ @pdf.y.should.not == y
160
+ @pdf.line_width.should.not == line_width
137
161
  end
138
162
  @pdf.y.should == y
139
163
  @pdf.line_width.should == line_width
@@ -146,7 +170,7 @@ describe "The render() feature" do
146
170
  @pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
147
171
  @pdf.line [100,100], [200,200]
148
172
  str = @pdf.render
149
- str.encoding.to_s.should eql("ASCII-8BIT")
173
+ str.encoding.to_s.should == "ASCII-8BIT"
150
174
  end
151
175
  end
152
176
  end
@@ -230,21 +230,21 @@ describe "When using painting shortcuts" do
230
230
  before(:each) { create_pdf }
231
231
 
232
232
  it "should convert stroke_some_method(args) into some_method(args); stroke" do
233
- @pdf.should_receive(:line_to).with([100,100])
234
- @pdf.should_receive(:stroke)
233
+ @pdf.expects(:line_to).with([100,100])
234
+ @pdf.expects(:stroke)
235
235
 
236
236
  @pdf.stroke_line_to [100,100]
237
237
  end
238
238
 
239
239
  it "should convert fill_some_method(args) into some_method(args); fill" do
240
- @pdf.should_receive(:line_to).with([100,100])
241
- @pdf.should_receive(:fill)
240
+ @pdf.expects(:line_to).with([100,100])
241
+ @pdf.expects(:fill)
242
242
 
243
243
  @pdf.fill_line_to [100,100]
244
244
  end
245
245
 
246
246
  it "should not break method_missing" do
247
247
  lambda { @pdf.i_have_a_pretty_girlfriend_named_jia }.
248
- should raise_error(NoMethodError)
248
+ should.raise(NoMethodError)
249
249
  end
250
250
  end
@@ -33,10 +33,10 @@ describe "the image() function" do
33
33
  images = observer(ImageObserver)
34
34
 
35
35
  # there should be 2 images in the page resources
36
- images.page_xobjects.first.size.should eql(2)
36
+ images.page_xobjects.first.size.should == 2
37
37
 
38
38
  # but only 1 image xobject
39
- @output.scan(/\/Type \/XObject/).size.should eql(1)
39
+ @output.scan(/\/Type \/XObject/).size.should == 1
40
40
  end
41
41
  end
42
42
 
@@ -16,10 +16,10 @@ describe "When reading a JPEG file" do
16
16
  it "should read the basic attributes correctly" do
17
17
  jpg = Prawn::Images::JPG.new(@img_data)
18
18
 
19
- jpg.width.should eql(604)
20
- jpg.height.should eql(453)
21
- jpg.bits.should eql(8)
22
- jpg.channels.should eql(3)
19
+ jpg.width.should == 604
20
+ jpg.height.should == 453
21
+ jpg.bits.should == 8
22
+ jpg.channels.should == 3
23
23
  end
24
24
  end
25
25
 
@@ -1,4 +1,4 @@
1
- # encoding: ASCII-8BIT
1
+ # encoding: utf-8
2
2
 
3
3
  require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
4
 
@@ -6,23 +6,24 @@ describe "adobe font metrics" do
6
6
 
7
7
  setup do
8
8
  @times = Prawn::Font::Metrics["Times-Roman"]
9
+ @iconv = Iconv.new('ISO-8859-1', 'utf-8')
9
10
  end
10
11
 
11
12
  it "should calculate string width taking into account accented characters" do
12
- @times.string_width("é", 12).should == @times.string_width("e", 12)
13
+ @times.string_width(@iconv.iconv("é"), 12).should == @times.string_width("e", 12)
13
14
  end
14
15
 
15
16
  it "should calculate string width taking into account kerning pairs" do
16
- @times.string_width("To", 12).should == 13.332
17
- @times.string_width("To", 12, :kerning => true).should == 12.372
18
- @times.string_width("Tö", 12, :kerning => true).should == 12.372
17
+ @times.string_width(@iconv.iconv("To"), 12).should == 13.332
18
+ @times.string_width(@iconv.iconv("To"), 12, :kerning => true).should == 12.372
19
+ @times.string_width(@iconv.iconv("Tö"), 12, :kerning => true).should == 12.372
19
20
  end
20
21
 
21
22
  it "should kern a string" do
22
- @times.kern("To").should == ["T", 80, "o"]
23
- @times.kern("Télé").should == ["T", 70, "\303\251l\303\251"]
24
- @times.kern("Technology").should == ["T", 70, "echnology"]
25
- @times.kern("Technology...").should == ["T", 70, "echnology", 65, "..."]
23
+ @times.kern(@iconv.iconv("To")).should == ["T", 80, "o"]
24
+ @times.kern(@iconv.iconv("Télé")).should == ["T", 70, @iconv.iconv("élé")]
25
+ @times.kern(@iconv.iconv("Technology")).should == ["T", 70, "echnology"]
26
+ @times.kern(@iconv.iconv("Technology...")).should == ["T", 70, "echnology", 65, "..."]
26
27
  end
27
28
 
28
29
  end
@@ -57,4 +58,4 @@ describe "ttf font metrics" do
57
58
  "\000\021\000\021\000\021"]
58
59
  end
59
60
 
60
- end
61
+ end
@@ -48,7 +48,7 @@ describe "PDF Object Serialization" do
48
48
 
49
49
  it "should not convert a whitespace containing Ruby symbol to a PDF name" do
50
50
  lambda { Prawn::PdfObject(:"My Symbol With Spaces") }.
51
- should raise_error(Prawn::Errors::FailedObjectConversion)
51
+ should.raise(Prawn::Errors::FailedObjectConversion)
52
52
  end
53
53
 
54
54
  it "should convert a Ruby array to PDF Array when inside a content stream" do
@@ -91,7 +91,7 @@ describe "PDF Object Serialization" do
91
91
 
92
92
  it "should not allow keys other than strings or symbols for PDF dicts" do
93
93
  lambda { Prawn::PdfObject(:foo => :bar, :baz => :bang, 1 => 4) }.
94
- should raise_error(Prawn::Errors::FailedObjectConversion)
94
+ should.raise(Prawn::Errors::FailedObjectConversion)
95
95
  end
96
96
 
97
97
  it "should convert a Prawn::Reference to a PDF indirect object reference" do
@@ -1,35 +1,161 @@
1
- # encoding: utf-8
1
+ # encoding: ASCII-8BIT
2
2
 
3
3
  # Spec'ing the PNG class. Not complete yet - still needs to check the
4
4
  # contents of palette and transparency to ensure they're correct.
5
5
  # Need to find files that have these sections first.
6
+ #
7
+ # see http://www.w3.org/TR/PNG/ for a detailed description of the PNG spec,
8
+ # particuarly Table 11.1 for the different color types
6
9
 
7
- require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
10
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
8
11
 
9
- describe "When reading an RGB PNG file" do
12
+ describe "When reading a greyscale PNG file (color type 0)" do
13
+
14
+ before(:each) do
15
+ @filename = "#{Prawn::BASEDIR}/data/images/web-links.png"
16
+ @data_filename = "#{Prawn::BASEDIR}/data/images/web-links.dat"
17
+ @img_data = File.open(@filename, "rb") { |f| f.read }
18
+ end
19
+
20
+ it "should read the attributes from the header chunk correctly" do
21
+ png = Prawn::Images::PNG.new(@img_data)
22
+
23
+ png.width.should == 21
24
+ png.height.should == 14
25
+ png.bits.should == 8
26
+ png.color_type.should == 0
27
+ png.compression_method.should == 0
28
+ png.filter_method.should == 0
29
+ png.interlace_method.should == 0
30
+ end
31
+
32
+ it "should read the image data chunk correctly" do
33
+ png = Prawn::Images::PNG.new(@img_data)
34
+ data = File.open(@data_filename, "rb") { |f| f.read }
35
+ png.img_data.should == data
36
+ end
37
+ end
38
+
39
+ describe "When reading an RGB PNG file (color type 2)" do
10
40
 
11
41
  before(:each) do
12
42
  @filename = "#{Prawn::BASEDIR}/data/images/ruport.png"
13
43
  @data_filename = "#{Prawn::BASEDIR}/data/images/ruport_data.dat"
14
44
  @img_data = File.open(@filename, "rb") { |f| f.read }
15
45
  end
16
-
46
+
47
+ it "should read the attributes from the header chunk correctly" do
48
+ png = Prawn::Images::PNG.new(@img_data)
49
+
50
+ png.width.should == 258
51
+ png.height.should == 105
52
+ png.bits.should == 8
53
+ png.color_type.should == 2
54
+ png.compression_method.should == 0
55
+ png.filter_method.should == 0
56
+ png.interlace_method.should == 0
57
+ end
58
+
59
+ it "should read the image data chunk correctly" do
60
+ png = Prawn::Images::PNG.new(@img_data)
61
+ data = File.open(@data_filename, "rb") { |f| f.read }
62
+ png.img_data.should == data
63
+ end
64
+ end
65
+
66
+ # TODO: describe "When reading an indexed color PNG file wiih transparency (color type 3)"
67
+
68
+ describe "When reading an indexed color PNG file (color type 3)" do
69
+
70
+ before(:each) do
71
+ @filename = "#{Prawn::BASEDIR}/data/images/rails.png"
72
+ @data_filename = "#{Prawn::BASEDIR}/data/images/rails.dat"
73
+ @img_data = File.open(@filename, "rb") { |f| f.read }
74
+ end
75
+
17
76
  it "should read the attributes from the header chunk correctly" do
18
77
  png = Prawn::Images::PNG.new(@img_data)
19
-
20
- png.width.should eql(258)
21
- png.height.should eql(105)
22
- png.bits.should eql(8)
23
- png.color_type.should eql(2)
24
- png.compression_method.should eql(0)
25
- png.filter_method.should eql(0)
26
- png.interlace_method.should eql(0)
78
+
79
+ png.width.should == 50
80
+ png.height.should == 64
81
+ png.bits.should == 8
82
+ png.color_type.should == 3
83
+ png.compression_method.should == 0
84
+ png.filter_method.should == 0
85
+ png.interlace_method.should == 0
27
86
  end
28
87
 
29
88
  it "should read the image data chunk correctly" do
30
89
  png = Prawn::Images::PNG.new(@img_data)
31
90
  data = File.open(@data_filename, "rb") { |f| f.read }
32
- png.img_data.should eql(data)
33
- end
91
+ png.img_data.should == data
92
+ end
93
+ end
94
+
95
+ describe "When reading a greyscale+alpha PNG file (color type 4)" do
96
+
97
+ before(:each) do
98
+ @filename = "#{Prawn::BASEDIR}/data/images/page_white_text.png"
99
+ @data_filename = "#{Prawn::BASEDIR}/data/images/page_white_text.dat"
100
+ @alpha_data_filename = "#{Prawn::BASEDIR}/data/images/page_white_text.alpha"
101
+ @img_data = File.open(@filename, "rb") { |f| f.read }
102
+ end
103
+
104
+ it "should read the attributes from the header chunk correctly" do
105
+ png = Prawn::Images::PNG.new(@img_data)
106
+
107
+ png.width.should == 16
108
+ png.height.should == 16
109
+ png.bits.should == 8
110
+ png.color_type.should == 4
111
+ png.compression_method.should == 0
112
+ png.filter_method.should == 0
113
+ png.interlace_method.should == 0
114
+ end
115
+
116
+ it "should correctly return the raw image data (with no alpha channel) from the image data chunk" do
117
+ png = Prawn::Images::PNG.new(@img_data)
118
+ data = File.open(@data_filename, rb_flag) { |f| f.read }
119
+ png.img_data.should == data
120
+ end
121
+
122
+ it "should correctly extract the alpha channel data from the image data chunk" do
123
+ png = Prawn::Images::PNG.new(@img_data)
124
+ data = File.open(@alpha_data_filename, rb_flag) { |f| f.read }
125
+ png.alpha_channel.should == data
126
+ end
34
127
  end
35
128
 
129
+ describe "When reading an RGB+alpha PNG file (color type 6)" do
130
+
131
+ before(:each) do
132
+ @filename = "#{Prawn::BASEDIR}/data/images/dice.png"
133
+ @data_filename = "#{Prawn::BASEDIR}/data/images/dice.dat"
134
+ @alpha_data_filename = "#{Prawn::BASEDIR}/data/images/dice.alpha"
135
+ @img_data = File.open(@filename, "rb") { |f| f.read }
136
+ end
137
+
138
+ it "should read the attributes from the header chunk correctly" do
139
+ png = Prawn::Images::PNG.new(@img_data)
140
+
141
+ png.width.should == 320
142
+ png.height.should == 240
143
+ png.bits.should == 8
144
+ png.color_type.should == 6
145
+ png.compression_method.should == 0
146
+ png.filter_method.should == 0
147
+ png.interlace_method.should == 0
148
+ end
149
+
150
+ it "should correctly return the raw image data (with no alpha channel) from the image data chunk" do
151
+ png = Prawn::Images::PNG.new(@img_data)
152
+ data = File.open(@data_filename, rb_flag) { |f| f.read }
153
+ png.img_data.should == data
154
+ end
155
+
156
+ it "should correctly extract the alpha channel data from the image data chunk" do
157
+ png = Prawn::Images::PNG.new(@img_data)
158
+ data = File.open(@alpha_data_filename, rb_flag) { |f| f.read }
159
+ png.alpha_channel.should == data
160
+ end
161
+ end
@@ -26,4 +26,17 @@ describe "A Reference object" do
26
26
  "\nBT\n/F1 12 Tf\n72 712 Td\n( A stream ) Tj\nET" +
27
27
  "\nendstream\nendobj\n"
28
28
  end
29
+
30
+ it "should compress a stream upon request" do
31
+ ref = Prawn::Reference(2,{})
32
+ ref << "Hi There " * 20
33
+
34
+ cref = Prawn::Reference(2,{})
35
+ cref << "Hi There " * 20
36
+ cref.compress_stream
37
+
38
+ assert cref.stream.size < ref.stream.size,
39
+ "compressed stream expected to be smaller than source but wasn't"
40
+ cref.data[:Filter].should == :FlateDecode
41
+ end
29
42
  end
@@ -3,12 +3,19 @@
3
3
  puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}"
4
4
 
5
5
  require "rubygems"
6
- require "spec"
6
+ require "test/spec"
7
+ require "mocha"
7
8
  $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
8
9
  require "prawn"
9
10
  gem 'pdf-reader', ">=0.7.3"
10
11
  require "pdf/reader"
11
12
 
13
+ module Prawn
14
+ class Document
15
+ public :ref
16
+ end
17
+ end
18
+
12
19
  def create_pdf
13
20
  @pdf = Prawn::Document.new(:left_margin => 0,
14
21
  :right_margin => 0,
@@ -26,4 +33,8 @@ end
26
33
  def parse_pdf_object(obj)
27
34
  PDF::Reader::Parser.new(
28
35
  PDF::Reader::Buffer.new(sio = StringIO.new(obj)), nil).parse_token
36
+ end
37
+
38
+ def rb_flag
39
+ ruby_18 { "rb" } || ruby_19 { "rb:ASCII-8BIT" }
29
40
  end
@@ -72,7 +72,7 @@ describe "A table's width" do
72
72
 
73
73
  font_height = pdf.font_metrics.font_height(12)
74
74
 
75
- table_height.should be_close(num_rows*font_height + 2*vpad*num_rows + vpad, 0.001)
75
+ table_height.should.be.close(num_rows*font_height + 2*vpad*num_rows + vpad, 0.001)
76
76
  end
77
77
 
78
78
  end
@@ -139,7 +139,7 @@ describe "A table's content" do
139
139
  data = [["foo","bar"],["baz",""]]
140
140
  @pdf = Prawn::Document.new
141
141
  @pdf.table(data)
142
- }.should_not raise_error
142
+ }.should.not.raise
143
143
  end
144
144
 
145
145
  end
@@ -71,12 +71,12 @@ describe "when drawing text" do
71
71
  position = @pdf.y
72
72
  @pdf.text "Foo"
73
73
 
74
- @pdf.y.should be_close(position - @pdf.font_metrics.font_height(12),
74
+ @pdf.y.should.be.close(position - @pdf.font_metrics.font_height(12),
75
75
  0.0001)
76
76
 
77
77
  position = @pdf.y
78
78
  @pdf.text "Foo\nBar\nBaz"
79
- @pdf.y.should be_close(position - 3*@pdf.font_metrics.font_height(12),
79
+ @pdf.y.should.be.close(position - 3*@pdf.font_metrics.font_height(12),
80
80
  0.0001)
81
81
  end
82
82
 
@@ -154,13 +154,22 @@ describe "when drawing text" do
154
154
  @pdf.text "Blaz", :at => [150,150]
155
155
  text = observer(FontObserver)
156
156
 
157
- text.page_fonts.size.should eql(2)
158
- text.page_fonts[0][0].should eql(:Helvetica)
159
- text.page_fonts[1][0].should eql(:Helvetica)
157
+ text.page_fonts.size.should == 2
158
+ text.page_fonts[0][0].should == :Helvetica
159
+ text.page_fonts[1][0].should == :Helvetica
160
160
  end
161
161
 
162
162
  it "should raise an exception when an unknown font is used" do
163
- lambda { @pdf.font "Pao bu" }.should raise_error(Prawn::Errors::UnknownFont)
163
+ lambda { @pdf.font "Pao bu" }.should.raise(Prawn::Errors::UnknownFont)
164
+ end
165
+
166
+ it "should correctly render a utf-8 string when using a built-in font" do
167
+ str = "©" # copyright symbol
168
+ @pdf.text str
169
+
170
+ # grab the text from the rendered PDF and ensure it matches
171
+ text = observer(TextObserver)
172
+ text.string.should == str
164
173
  end
165
174
 
166
175
  if "spec".respond_to?(:encode!)
@@ -168,22 +177,23 @@ describe "when drawing text" do
168
177
  it "should raise an exception when a utf-8 incompatible string is rendered" do
169
178
  str = "Blah \xDD"
170
179
  str.force_encoding("ASCII-8BIT")
171
- lambda { @pdf.text str }.should raise_error(Prawn::Errors::IncompatibleStringEncoding)
180
+ lambda { @pdf.text str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
172
181
  end
173
182
  it "should not raise an exception when a shift-jis string is rendered" do
174
183
  datafile = "#{Prawn::BASEDIR}/data/shift_jis_text.txt"
175
184
  sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets }
176
- lambda { @pdf.text sjis_str }.should_not raise_error(Prawn::Errors::IncompatibleStringEncoding)
185
+ @pdf.font("#{Prawn::BASEDIR}/data/fonts/gkai00mp.ttf")
186
+ lambda { @pdf.text sjis_str }.should.not.raise(Prawn::Errors::IncompatibleStringEncoding)
177
187
  end
178
188
  else
179
189
  # Handle non utf-8 string encodings in a sane way on non-M17N aware VMs
180
190
  it "should raise an exception when a corrupt utf-8 string is rendered" do
181
191
  str = "Blah \xDD"
182
- lambda { @pdf.text str }.should raise_error(Prawn::Errors::IncompatibleStringEncoding)
192
+ lambda { @pdf.text str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
183
193
  end
184
194
  it "should raise an exception when a shift-jis string is rendered" do
185
195
  sjis_str = File.read("#{Prawn::BASEDIR}/data/shift_jis_text.txt")
186
- lambda { @pdf.text sjis_str }.should raise_error(Prawn::Errors::IncompatibleStringEncoding)
196
+ lambda { @pdf.text sjis_str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
187
197
  end
188
198
  end
189
199
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: prawn
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.1
7
- date: 2008-08-08 00:00:00 -04:00
6
+ version: 0.1.2
7
+ date: 2008-08-11 00:00:00 -04:00
8
8
  summary: A fast and nimble PDF generator for Ruby
9
9
  require_paths:
10
10
  - lib
@@ -47,6 +47,7 @@ files:
47
47
  - examples/multi_page_layout.rb
48
48
  - examples/on_page_start.rb
49
49
  - examples/page_geometry.rb
50
+ - examples/png_types.rb
50
51
  - examples/polygons.rb
51
52
  - examples/ruport_formatter.rb
52
53
  - examples/ruport_helpers.rb
@@ -144,12 +145,21 @@ files:
144
145
  - data/fonts/Times-Roman.afm
145
146
  - data/fonts/ZapfDingbats.afm
146
147
  - data/images
148
+ - data/images/dice.alpha
149
+ - data/images/dice.dat
147
150
  - data/images/dice.png
151
+ - data/images/page_white_text.alpha
152
+ - data/images/page_white_text.dat
153
+ - data/images/page_white_text.png
148
154
  - data/images/pigs.jpg
155
+ - data/images/rails.dat
156
+ - data/images/rails.png
149
157
  - data/images/ruport.png
150
158
  - data/images/ruport_data.dat
151
159
  - data/images/ruport_transparent.png
152
160
  - data/images/stef.jpg
161
+ - data/images/web-links.dat
162
+ - data/images/web-links.png
153
163
  - data/shift_jis_text.txt
154
164
  - Rakefile
155
165
  - README