prawn-table 0.0.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 +7 -0
- data/COPYING +2 -0
- data/GPLv2 +340 -0
- data/GPLv3 +674 -0
- data/Gemfile +5 -0
- data/LICENSE +56 -0
- data/lib/prawn/table.rb +641 -0
- data/lib/prawn/table/cell.rb +772 -0
- data/lib/prawn/table/cell/image.rb +69 -0
- data/lib/prawn/table/cell/in_table.rb +33 -0
- data/lib/prawn/table/cell/span_dummy.rb +93 -0
- data/lib/prawn/table/cell/subtable.rb +66 -0
- data/lib/prawn/table/cell/text.rb +154 -0
- data/lib/prawn/table/cells.rb +255 -0
- data/lib/prawn/table/column_width_calculator.rb +182 -0
- data/manual/contents.rb +13 -0
- data/manual/example_helper.rb +8 -0
- data/manual/table/basic_block.rb +53 -0
- data/manual/table/before_rendering_page.rb +26 -0
- data/manual/table/cell_border_lines.rb +24 -0
- data/manual/table/cell_borders_and_bg.rb +31 -0
- data/manual/table/cell_dimensions.rb +30 -0
- data/manual/table/cell_text.rb +38 -0
- data/manual/table/column_widths.rb +30 -0
- data/manual/table/content_and_subtables.rb +39 -0
- data/manual/table/creation.rb +27 -0
- data/manual/table/filtering.rb +36 -0
- data/manual/table/flow_and_header.rb +17 -0
- data/manual/table/image_cells.rb +33 -0
- data/manual/table/position.rb +29 -0
- data/manual/table/row_colors.rb +20 -0
- data/manual/table/span.rb +30 -0
- data/manual/table/style.rb +22 -0
- data/manual/table/table.rb +52 -0
- data/manual/table/width.rb +27 -0
- data/prawn-table.gemspec +48 -0
- data/spec/cell_spec.rb +629 -0
- data/spec/extensions/encoding_helpers.rb +11 -0
- data/spec/extensions/mocha.rb +46 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/table/span_dummy_spec.rb +17 -0
- data/spec/table_spec.rb +1527 -0
- metadata +240 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Allow speccing things when an expectation matcher runs. Similar to #with, but
|
4
|
+
# always succeeds.
|
5
|
+
#
|
6
|
+
# @pdf.expects(:stroke_line).checking do |from, to|
|
7
|
+
# @pdf.map_to_absolute(from).should == [0, 0]
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Note that the outer expectation does *not* fail only because the inner one
|
11
|
+
# does; in the above example, the outer expectation would only fail if
|
12
|
+
# stroke_line were not called.
|
13
|
+
|
14
|
+
class ParameterChecker < Mocha::ParametersMatcher
|
15
|
+
def initialize(&matching_block)
|
16
|
+
@expected_parameters = [Mocha::ParameterMatchers::AnyParameters.new]
|
17
|
+
@matching_block = matching_block
|
18
|
+
end
|
19
|
+
|
20
|
+
def match?(actual_parameters = [])
|
21
|
+
@matching_block.call(*actual_parameters)
|
22
|
+
|
23
|
+
true # always succeed
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Mocha::Expectation
|
28
|
+
def checking(&block)
|
29
|
+
@parameters_matcher = ParameterChecker.new(&block)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# Equivalent to expects(method_name).at_least(0). More useful when combined
|
36
|
+
# with parameter matchers to ignore certain calls for the sake of parameter
|
37
|
+
# matching.
|
38
|
+
#
|
39
|
+
# @pdf.ignores(:stroke_color=).with("000000")
|
40
|
+
# @pdf.expects(:stroke_color=).with("ff0000")
|
41
|
+
#
|
42
|
+
module Mocha::ObjectMethods
|
43
|
+
def ignores(method_name)
|
44
|
+
expects(method_name).at_least(0)
|
45
|
+
end
|
46
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}"
|
4
|
+
|
5
|
+
require "bundler"
|
6
|
+
Bundler.setup
|
7
|
+
|
8
|
+
if ENV["COVERAGE"]
|
9
|
+
require "simplecov"
|
10
|
+
SimpleCov.start do
|
11
|
+
add_filter "/spec/"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require "prawn"
|
16
|
+
require_relative "../lib/prawn/table"
|
17
|
+
|
18
|
+
Prawn.debug = true
|
19
|
+
|
20
|
+
require "rspec"
|
21
|
+
require "mocha/api"
|
22
|
+
require "pdf/reader"
|
23
|
+
require "pdf/inspector"
|
24
|
+
|
25
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
26
|
+
# in spec/extensions/ and its subdirectories.
|
27
|
+
Dir[File.dirname(__FILE__) + "/extensions/**/*.rb"].each {|f| require f }
|
28
|
+
|
29
|
+
RSpec.configure do |config|
|
30
|
+
config.mock_framework = :mocha
|
31
|
+
config.include EncodingHelpers
|
32
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_pdf(klass=Prawn::Document)
|
36
|
+
@pdf = klass.new(:margin => 0)
|
37
|
+
end
|
38
|
+
|
39
|
+
RSpec::Matchers.define :have_parseable_xobjects do
|
40
|
+
match do |actual|
|
41
|
+
expect { PDF::Inspector::XObject.analyze(actual.render) }.not_to raise_error
|
42
|
+
true
|
43
|
+
end
|
44
|
+
failure_message_for_should do |actual|
|
45
|
+
"expected that #{actual}'s XObjects could be successfully parsed"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Make some methods public to assist in testing
|
50
|
+
module Prawn::Graphics
|
51
|
+
public :map_to_absolute
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "..", "spec_helper")
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
describe "Prawn::Table::Cell::SpanDummy" do
|
7
|
+
before(:each) do
|
8
|
+
@pdf = Prawn::Document.new
|
9
|
+
@table = @pdf.table([[{:content => "Row", :colspan => 2}]])
|
10
|
+
@master_cell = @table.cells[0,0]
|
11
|
+
@span_dummy = @master_cell.dummy_cells.first
|
12
|
+
end
|
13
|
+
|
14
|
+
it "delegates background_color to the master cell" do
|
15
|
+
@span_dummy.background_color.should == @master_cell.background_color
|
16
|
+
end
|
17
|
+
end
|
data/spec/table_spec.rb
ADDED
@@ -0,0 +1,1527 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# run rspec -t issue:XYZ to run tests for a specific github issue
|
4
|
+
# or rspec -t unresolved to run tests for all unresolved issues
|
5
|
+
|
6
|
+
|
7
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
|
8
|
+
|
9
|
+
require_relative "../lib/prawn/table"
|
10
|
+
require 'set'
|
11
|
+
|
12
|
+
describe "Prawn::Table" do
|
13
|
+
|
14
|
+
describe "converting data to Cell objects" do
|
15
|
+
before(:each) do
|
16
|
+
@pdf = Prawn::Document.new
|
17
|
+
@table = @pdf.table([%w[R0C0 R0C1], %w[R1C0 R1C1]])
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return a Prawn::Table" do
|
21
|
+
@table.should be_a_kind_of Prawn::Table
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should flatten the data into the @cells array in row-major order" do
|
25
|
+
@table.cells.map { |c| c.content }.should == %w[R0C0 R0C1 R1C0 R1C1]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should add row and column numbers to each cell" do
|
29
|
+
c = @table.cells.to_a.first
|
30
|
+
c.row.should == 0
|
31
|
+
c.column.should == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should allow empty fields" do
|
35
|
+
lambda {
|
36
|
+
data = [["foo","bar"],["baz",""]]
|
37
|
+
@pdf.table(data)
|
38
|
+
}.should_not raise_error
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should allow a table with a header but no body" do
|
42
|
+
lambda { @pdf.table([["Header"]], :header => true) }.should_not raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should accurately count columns from data" do
|
46
|
+
# First data row may contain colspan which would hide true column count
|
47
|
+
data = [["Name:", {:content => "Some very long name", :colspan => 5}]]
|
48
|
+
pdf = Prawn::Document.new
|
49
|
+
table = Prawn::Table.new data, pdf
|
50
|
+
table.column_widths.length.should == 6
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "headers should allow for rowspan" do
|
55
|
+
it "should remember rowspans accross multiple pages", :issue => 721 do
|
56
|
+
pdf = Prawn::Document.new({:page_size => "A4", :page_layout => :portrait})
|
57
|
+
rows = [ [{:content=>"The\nNumber", :rowspan=>2}, {:content=>"Prefixed", :colspan=>2} ],
|
58
|
+
["A's", "B's"] ]
|
59
|
+
|
60
|
+
(1..50).each do |n|
|
61
|
+
rows.push( ["#{n}", "A#{n}", "B#{n}"] )
|
62
|
+
end
|
63
|
+
|
64
|
+
pdf.table( rows, :header=>2 ) do
|
65
|
+
row(0..1).style :background_color=>"FFFFCC"
|
66
|
+
end
|
67
|
+
|
68
|
+
#ensure that the header on page 1 is identical to the header on page 0
|
69
|
+
output = PDF::Inspector::Page.analyze(pdf.render)
|
70
|
+
output.pages[0][:strings][0..4].should == output.pages[1][:strings][0..4]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "Text may be longer than the available space in a row on a single page" do
|
75
|
+
it "should not glitch the layout if there is too much text to fit onto a single row on a single page", :unresolved, :issue => 562 do
|
76
|
+
pdf = Prawn::Document.new({:page_size => "A4", :page_layout => :portrait})
|
77
|
+
|
78
|
+
table_data = Array.new
|
79
|
+
text = 'This will be a very long text. ' * 5
|
80
|
+
table_data.push([{:content => text, :rowspan => 2}, 'b', 'c'])
|
81
|
+
table_data.push(['b','c'])
|
82
|
+
|
83
|
+
column_widths = [50, 60, 400]
|
84
|
+
|
85
|
+
table = Prawn::Table.new table_data, pdf,:column_widths => column_widths
|
86
|
+
|
87
|
+
#render the table onto the pdf
|
88
|
+
table.draw
|
89
|
+
|
90
|
+
#expected behavior would be for the long text to be cut off or an exception to be raised
|
91
|
+
#thus we only expect a single page
|
92
|
+
pdf.page_count.should == 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "You can explicitly set the column widths and use a colspan > 1" do
|
97
|
+
|
98
|
+
it "should tolerate floating point rounding errors < 0.000000001" do
|
99
|
+
data=[["a", "b ", "c ", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
|
100
|
+
[{:content=>"Foobar", :colspan=>12}]
|
101
|
+
]
|
102
|
+
#we need values with lots of decimals so that arithmetic errors will occur
|
103
|
+
#the values are not arbitrary but where found converting mm to pdf pt
|
104
|
+
column_widths=[137, 40, 40, 54.69291338582678, 54.69291338582678,
|
105
|
+
54.69291338582678, 54.69291338582678, 54.69291338582678,
|
106
|
+
54.69291338582678, 54.69291338582678, 54.69291338582678,
|
107
|
+
54.69291338582678]
|
108
|
+
|
109
|
+
pdf = Prawn::Document.new({:page_size => 'A4', :page_layout => :landscape})
|
110
|
+
table = Prawn::Table.new data, pdf, :column_widths => column_widths
|
111
|
+
table.column_widths.should == column_widths
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should work with two different given colspans", :issue => 628 do
|
115
|
+
data = [
|
116
|
+
[" ", " ", " "],
|
117
|
+
[{:content=>" ", :colspan=>3}],
|
118
|
+
[" ", {:content=>" ", :colspan=>2}]
|
119
|
+
]
|
120
|
+
column_widths = [60, 240, 60]
|
121
|
+
pdf = Prawn::Document.new
|
122
|
+
#the next line raised an Prawn::Errors::CannotFit exception before issue 628 was fixed
|
123
|
+
table = Prawn::Table.new data, pdf, :column_widths => column_widths
|
124
|
+
table.column_widths.should == column_widths
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should work with a colspan > 1 with given column_widths (issue #407)" do
|
128
|
+
#normal entries in line 1
|
129
|
+
data = [
|
130
|
+
[ '','',''],
|
131
|
+
[ { :content => "", :colspan => 3 } ],
|
132
|
+
[ "", "", "" ],
|
133
|
+
]
|
134
|
+
pdf = Prawn::Document.new
|
135
|
+
table = Prawn::Table.new data, pdf, :column_widths => [100 , 200, 240]
|
136
|
+
|
137
|
+
#colspan entry in line 1
|
138
|
+
data = [
|
139
|
+
[ { :content => "", :colspan => 3 } ],
|
140
|
+
[ "", "", "" ],
|
141
|
+
]
|
142
|
+
pdf = Prawn::Document.new
|
143
|
+
table = Prawn::Table.new data, pdf, :column_widths => [100 , 200, 240]
|
144
|
+
|
145
|
+
#mixed entries in line 1
|
146
|
+
data = [
|
147
|
+
[ { :content => "", :colspan =>2 }, "" ],
|
148
|
+
[ "", "", "" ],
|
149
|
+
]
|
150
|
+
pdf = Prawn::Document.new
|
151
|
+
table = Prawn::Table.new data, pdf, :column_widths => [100 , 200, 240]
|
152
|
+
|
153
|
+
data = [['', '', {:content => '', :colspan => 2}, '',''],
|
154
|
+
['',{:content => '', :colspan => 5}]
|
155
|
+
]
|
156
|
+
pdf = Prawn::Document.new
|
157
|
+
table = Prawn::Table.new data, pdf, :column_widths => [50 , 100, 50, 50, 50, 50]
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should not increase column width when rendering a subtable",
|
162
|
+
:unresolved, :issue => 612 do
|
163
|
+
|
164
|
+
pdf = Prawn::Document.new
|
165
|
+
|
166
|
+
first = {:content=>"Foooo fo foooooo",:width=>50,:align=>:center}
|
167
|
+
second = {:content=>"Foooo",:colspan=>2,:width=>70,:align=>:center}
|
168
|
+
third = {:content=>"fooooooooooo, fooooooooooooo, fooo, foooooo fooooo",:width=>50,:align=>:center}
|
169
|
+
fourth = {:content=>"Bar",:width=>20,:align=>:center}
|
170
|
+
|
171
|
+
table_content = [[
|
172
|
+
first,
|
173
|
+
[[second],[third,fourth]]
|
174
|
+
]]
|
175
|
+
|
176
|
+
table = Prawn::Table.new table_content, pdf
|
177
|
+
table.column_widths.should == [50.0, 70.0]
|
178
|
+
end
|
179
|
+
|
180
|
+
it "illustrates issue #710", :issue => 710 do
|
181
|
+
partial_width = 40
|
182
|
+
pdf = Prawn::Document.new({page_size: "LETTER", page_layout: :portrait})
|
183
|
+
col_widths = [
|
184
|
+
50,
|
185
|
+
partial_width, partial_width, partial_width, partial_width
|
186
|
+
]
|
187
|
+
|
188
|
+
day_header = [{
|
189
|
+
content: "Monday, August 5th, A.S. XLIX",
|
190
|
+
colspan: 5,
|
191
|
+
}]
|
192
|
+
|
193
|
+
times = [{
|
194
|
+
content: "Loc",
|
195
|
+
colspan: 1,
|
196
|
+
}, {
|
197
|
+
content: "8:00",
|
198
|
+
colspan: 4,
|
199
|
+
}]
|
200
|
+
|
201
|
+
data = [ day_header ] + [ times ]
|
202
|
+
|
203
|
+
#raised a Prawn::Errors::CannotFit:
|
204
|
+
#Table's width was set larger than its contents' maximum width (max width 210, requested 218.0)
|
205
|
+
table = Prawn::Table.new data, pdf, :column_widths => col_widths
|
206
|
+
end
|
207
|
+
|
208
|
+
it "illustrate issue #533" do
|
209
|
+
data = [['', '', '', '', '',''],
|
210
|
+
['',{:content => '', :colspan => 5}]]
|
211
|
+
pdf = Prawn::Document.new
|
212
|
+
table = Prawn::Table.new data, pdf, :column_widths => [50, 200, 40, 40, 50, 50]
|
213
|
+
end
|
214
|
+
|
215
|
+
it "illustrates issue #502" do
|
216
|
+
pdf = Prawn::Document.new
|
217
|
+
first = {:content=>"Foooo fo foooooo",:width=>50,:align=>:center}
|
218
|
+
second = {:content=>"Foooo",:colspan=>2,:width=>70,:align=>:center}
|
219
|
+
third = {:content=>"fooooooooooo, fooooooooooooo, fooo, foooooo fooooo",:width=>50,:align=>:center}
|
220
|
+
fourth = {:content=>"Bar",:width=>20,:align=>:center}
|
221
|
+
table_content = [[
|
222
|
+
first,
|
223
|
+
[[second],[third,fourth]]
|
224
|
+
]]
|
225
|
+
pdf.move_down(20)
|
226
|
+
table = Prawn::Table.new table_content, pdf
|
227
|
+
pdf.table(table_content)
|
228
|
+
end
|
229
|
+
|
230
|
+
#https://github.com/prawnpdf/prawn/issues/407#issuecomment-28556698
|
231
|
+
it "correctly computes column widths with empty cells + colspan" do
|
232
|
+
data = [['', ''],
|
233
|
+
[{:content => '', :colspan => 2}]
|
234
|
+
]
|
235
|
+
pdf = Prawn::Document.new
|
236
|
+
|
237
|
+
table = Prawn::Table.new data, pdf, :column_widths => [50, 200]
|
238
|
+
table.column_widths.should == [50.0, 200.0]
|
239
|
+
end
|
240
|
+
|
241
|
+
it "illustrates a variant of problem in issue #407 - comment 28556698" do
|
242
|
+
pdf = Prawn::Document.new
|
243
|
+
table_data = [["a", "b", "c"], [{:content=>"d", :colspan=>3}]]
|
244
|
+
column_widths = [50, 60, 400]
|
245
|
+
|
246
|
+
# Before we fixed #407, this line incorrectly raise a CannotFit error
|
247
|
+
pdf.table(table_data, :column_widths => column_widths)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should not allow oversized subtables when parent column width is constrained" do
|
251
|
+
pdf = Prawn::Document.new
|
252
|
+
child_1 = pdf.make_table([['foo'*100]])
|
253
|
+
child_2 = pdf.make_table([['foo']])
|
254
|
+
lambda do
|
255
|
+
pdf.table([[child_1], [child_2]], column_widths: [pdf.bounds.width/2] * 2)
|
256
|
+
end.should raise_error(Prawn::Errors::CannotFit)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "#initialize" do
|
261
|
+
before(:each) do
|
262
|
+
@pdf = Prawn::Document.new
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should instance_eval a 0-arg block" do
|
266
|
+
initializer = mock()
|
267
|
+
initializer.expects(:kick).once
|
268
|
+
|
269
|
+
@pdf.table([["a"]]){
|
270
|
+
initializer.kick
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should call a 1-arg block with the document as the argument" do
|
275
|
+
initializer = mock()
|
276
|
+
initializer.expects(:kick).once
|
277
|
+
|
278
|
+
@pdf.table([["a"]]){ |doc|
|
279
|
+
doc.should be_a_kind_of(Prawn::Table); initializer.kick }
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should proxy cell methods to #cells" do
|
283
|
+
table = @pdf.table([["a"]], :cell_style => { :padding => 11 })
|
284
|
+
table.cells[0, 0].padding.should == [11, 11, 11, 11]
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should set row and column length" do
|
288
|
+
table = @pdf.table([["a", "b", "c"], ["d", "e", "f"]])
|
289
|
+
table.row_length.should == 2
|
290
|
+
table.column_length.should == 3
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should generate a text cell based on a String" do
|
294
|
+
t = @pdf.table([["foo"]])
|
295
|
+
t.cells[0,0].should be_a_kind_of(Prawn::Table::Cell::Text)
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should pass through a text cell" do
|
299
|
+
c = Prawn::Table::Cell::Text.new(@pdf, [0,0], :content => "foo")
|
300
|
+
t = @pdf.table([[c]])
|
301
|
+
t.cells[0,0].should == c
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "cell accessors" do
|
306
|
+
before(:each) do
|
307
|
+
@pdf = Prawn::Document.new
|
308
|
+
@table = @pdf.table([%w[R0C0 R0C1], %w[R1C0 R1C1]])
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should select rows by number or range" do
|
312
|
+
Set.new(@table.row(0).map { |c| c.content }).should ==
|
313
|
+
Set.new(%w[R0C0 R0C1])
|
314
|
+
Set.new(@table.rows(0..1).map { |c| c.content }).should ==
|
315
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should select rows by array" do
|
319
|
+
Set.new(@table.rows([0, 1]).map { |c| c.content }).should ==
|
320
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
321
|
+
end
|
322
|
+
|
323
|
+
it "should allow negative row selectors" do
|
324
|
+
Set.new(@table.row(-1).map { |c| c.content }).should ==
|
325
|
+
Set.new(%w[R1C0 R1C1])
|
326
|
+
Set.new(@table.rows(-2..-1).map { |c| c.content }).should ==
|
327
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
328
|
+
Set.new(@table.rows(0..-1).map { |c| c.content }).should ==
|
329
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should select columns by number or range" do
|
333
|
+
Set.new(@table.column(0).map { |c| c.content }).should ==
|
334
|
+
Set.new(%w[R0C0 R1C0])
|
335
|
+
Set.new(@table.columns(0..1).map { |c| c.content }).should ==
|
336
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should select columns by array" do
|
340
|
+
Set.new(@table.columns([0, 1]).map { |c| c.content }).should ==
|
341
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should allow negative column selectors" do
|
345
|
+
Set.new(@table.column(-1).map { |c| c.content }).should ==
|
346
|
+
Set.new(%w[R0C1 R1C1])
|
347
|
+
Set.new(@table.columns(-2..-1).map { |c| c.content }).should ==
|
348
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
349
|
+
Set.new(@table.columns(0..-1).map { |c| c.content }).should ==
|
350
|
+
Set.new(%w[R0C0 R0C1 R1C0 R1C1])
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should allow rows and columns to be combined" do
|
354
|
+
@table.row(0).column(1).map { |c| c.content }.should == ["R0C1"]
|
355
|
+
end
|
356
|
+
|
357
|
+
it "should accept a filter block, returning a cell proxy" do
|
358
|
+
@table.cells.filter { |c| c.content =~ /R0/ }.column(1).map{ |c|
|
359
|
+
c.content }.should == ["R0C1"]
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should accept the [] method, returning a Cell or nil" do
|
363
|
+
@table.cells[0, 0].content.should == "R0C0"
|
364
|
+
@table.cells[12, 12].should be_nil
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should proxy unknown methods to the cells" do
|
368
|
+
@table.cells.height = 200
|
369
|
+
@table.row(1).height = 100
|
370
|
+
|
371
|
+
@table.cells[0, 0].height.should == 200
|
372
|
+
@table.cells[1, 0].height.should == 100
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should ignore non-setter methods" do
|
376
|
+
lambda {
|
377
|
+
@table.cells.content_width
|
378
|
+
}.should raise_error(NoMethodError)
|
379
|
+
end
|
380
|
+
|
381
|
+
it "skips cells that don't respond to the given method" do
|
382
|
+
table = @pdf.make_table([[{:content => "R0", :colspan => 2}],
|
383
|
+
%w[R1C0 R1C1]])
|
384
|
+
lambda {
|
385
|
+
table.row(0).font_style = :bold
|
386
|
+
}.should_not raise_error
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should accept the style method, proxying its calls to the cells" do
|
390
|
+
@table.cells.style(:height => 200, :width => 200)
|
391
|
+
@table.column(0).style(:width => 100)
|
392
|
+
|
393
|
+
@table.cells[0, 1].width.should == 200
|
394
|
+
@table.cells[1, 0].height.should == 200
|
395
|
+
@table.cells[1, 0].width.should == 100
|
396
|
+
end
|
397
|
+
|
398
|
+
it "style method should accept a block, passing each cell to be styled" do
|
399
|
+
@table.cells.style { |c| c.height = 200 }
|
400
|
+
@table.cells[0, 1].height.should == 200
|
401
|
+
end
|
402
|
+
|
403
|
+
it "should return the width of selected columns for #width" do
|
404
|
+
c0_width = @table.column(0).map{ |c| c.width }.max
|
405
|
+
c1_width = @table.column(1).map{ |c| c.width }.max
|
406
|
+
|
407
|
+
@table.column(0).width.should == c0_width
|
408
|
+
@table.column(1).width.should == c1_width
|
409
|
+
|
410
|
+
@table.columns(0..1).width.should == c0_width + c1_width
|
411
|
+
@table.cells.width.should == c0_width + c1_width
|
412
|
+
end
|
413
|
+
|
414
|
+
it "should return the height of selected rows for #height" do
|
415
|
+
r0_height = @table.row(0).map{ |c| c.height }.max
|
416
|
+
r1_height = @table.row(1).map{ |c| c.height }.max
|
417
|
+
|
418
|
+
@table.row(0).height.should == r0_height
|
419
|
+
@table.row(1).height.should == r1_height
|
420
|
+
|
421
|
+
@table.rows(0..1).height.should == r0_height + r1_height
|
422
|
+
@table.cells.height.should == r0_height + r1_height
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
describe "layout" do
|
427
|
+
before(:each) do
|
428
|
+
@pdf = Prawn::Document.new
|
429
|
+
@long_text = "The quick brown fox jumped over the lazy dogs. " * 5
|
430
|
+
end
|
431
|
+
|
432
|
+
describe "width" do
|
433
|
+
it "should raise_error an error if the given width is outside of range" do
|
434
|
+
lambda do
|
435
|
+
@pdf.table([["foo"]], :width => 1)
|
436
|
+
end.should raise_error(Prawn::Errors::CannotFit)
|
437
|
+
|
438
|
+
lambda do
|
439
|
+
@pdf.table([[@long_text]], :width => @pdf.bounds.width + 100)
|
440
|
+
end.should raise_error(Prawn::Errors::CannotFit)
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should accept the natural width for small tables" do
|
444
|
+
pad = 10 # default padding
|
445
|
+
@table = @pdf.table([["a"]])
|
446
|
+
@table.width.should == @table.cells[0, 0].natural_content_width + pad
|
447
|
+
end
|
448
|
+
|
449
|
+
it "width should == sum(column_widths)" do
|
450
|
+
table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do
|
451
|
+
column(0).width = 50
|
452
|
+
column(1).width = 100
|
453
|
+
column(2).width = 150
|
454
|
+
end
|
455
|
+
table.width.should == 300
|
456
|
+
end
|
457
|
+
|
458
|
+
it "should accept Numeric for column_widths" do
|
459
|
+
table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do |t|
|
460
|
+
t.column_widths = 50
|
461
|
+
end
|
462
|
+
table.width.should == 150
|
463
|
+
end
|
464
|
+
|
465
|
+
it "should calculate unspecified column widths as "+
|
466
|
+
"(max(string_width) + 2*horizontal_padding)" do
|
467
|
+
hpad, fs = 3, 12
|
468
|
+
columns = 2
|
469
|
+
table = Prawn::Table.new( [%w[ foo b ], %w[d foobar]], @pdf,
|
470
|
+
:cell_style => { :padding => hpad, :size => fs } )
|
471
|
+
|
472
|
+
col0_width = @pdf.width_of("foo", :size => fs)
|
473
|
+
col1_width = @pdf.width_of("foobar", :size => fs)
|
474
|
+
|
475
|
+
table.width.should == col0_width + col1_width + 2*columns*hpad
|
476
|
+
end
|
477
|
+
|
478
|
+
it "should allow mixing autocalculated and preset"+
|
479
|
+
"column widths within a single table" do
|
480
|
+
hpad, fs = 10, 6
|
481
|
+
stretchy_columns = 2
|
482
|
+
|
483
|
+
col0_width = 50
|
484
|
+
col1_width = @pdf.width_of("foo", :size => fs)
|
485
|
+
col2_width = @pdf.width_of("foobar", :size => fs)
|
486
|
+
col3_width = 150
|
487
|
+
|
488
|
+
table = Prawn::Table.new( [%w[snake foo b apple],
|
489
|
+
%w[kitten d foobar banana]], @pdf,
|
490
|
+
:cell_style => { :padding => hpad, :size => fs }) do
|
491
|
+
|
492
|
+
column(0).width = col0_width
|
493
|
+
column(3).width = col3_width
|
494
|
+
end
|
495
|
+
|
496
|
+
table.width.should == col1_width + col2_width +
|
497
|
+
2*stretchy_columns*hpad +
|
498
|
+
col0_width + col3_width
|
499
|
+
end
|
500
|
+
|
501
|
+
it "should preserve all manually requested column widths" do
|
502
|
+
col0_width = 50
|
503
|
+
col1_width = 20
|
504
|
+
col3_width = 60
|
505
|
+
|
506
|
+
table = Prawn::Table.new( [["snake", "foo", "b",
|
507
|
+
"some long, long text that will wrap"],
|
508
|
+
%w[kitten d foobar banana]], @pdf,
|
509
|
+
:width => 150) do
|
510
|
+
|
511
|
+
column(0).width = col0_width
|
512
|
+
column(1).width = col1_width
|
513
|
+
column(3).width = col3_width
|
514
|
+
end
|
515
|
+
|
516
|
+
table.draw
|
517
|
+
|
518
|
+
table.column(0).width.should == col0_width
|
519
|
+
table.column(1).width.should == col1_width
|
520
|
+
table.column(3).width.should == col3_width
|
521
|
+
end
|
522
|
+
|
523
|
+
it "should_not exceed the maximum width of the margin_box" do
|
524
|
+
expected_width = @pdf.margin_box.width
|
525
|
+
data = [
|
526
|
+
['This is a column with a lot of text that should comfortably exceed '+
|
527
|
+
'the width of a normal document margin_box width', 'Some more text',
|
528
|
+
'and then some more', 'Just a bit more to be extra sure']
|
529
|
+
]
|
530
|
+
table = Prawn::Table.new(data, @pdf)
|
531
|
+
|
532
|
+
table.width.should == expected_width
|
533
|
+
end
|
534
|
+
|
535
|
+
it "should_not exceed the maximum width of the margin_box even with" +
|
536
|
+
"manual widths specified" do
|
537
|
+
expected_width = @pdf.margin_box.width
|
538
|
+
data = [
|
539
|
+
['This is a column with a lot of text that should comfortably exceed '+
|
540
|
+
'the width of a normal document margin_box width', 'Some more text',
|
541
|
+
'and then some more', 'Just a bit more to be extra sure']
|
542
|
+
]
|
543
|
+
table = Prawn::Table.new(data, @pdf) { column(1).width = 100 }
|
544
|
+
|
545
|
+
table.width.should == expected_width
|
546
|
+
end
|
547
|
+
|
548
|
+
it "scales down only the non-preset column widths when the natural width" +
|
549
|
+
"exceeds the maximum width of the margin_box" do
|
550
|
+
expected_width = @pdf.margin_box.width
|
551
|
+
data = [
|
552
|
+
['This is a column with a lot of text that should comfortably exceed '+
|
553
|
+
'the width of a normal document margin_box width', 'Some more text',
|
554
|
+
'and then some more', 'Just a bit more to be extra sure']
|
555
|
+
]
|
556
|
+
table = Prawn::Table.new(data, @pdf) { column(1).width = 100; column(3).width = 50 }
|
557
|
+
|
558
|
+
table.width.should == expected_width
|
559
|
+
table.column_widths[1].should == 100
|
560
|
+
table.column_widths[3].should == 50
|
561
|
+
end
|
562
|
+
|
563
|
+
it "should allow width to be reset even after it has been calculated" do
|
564
|
+
@table = @pdf.table([[@long_text]])
|
565
|
+
@table.width
|
566
|
+
@table.width = 100
|
567
|
+
@table.width.should == 100
|
568
|
+
end
|
569
|
+
|
570
|
+
it "should shrink columns evenly when two equal columns compete" do
|
571
|
+
@table = @pdf.table([["foo", @long_text], [@long_text, "foo"]])
|
572
|
+
@table.cells[0, 0].width.should == @table.cells[0, 1].width
|
573
|
+
end
|
574
|
+
|
575
|
+
it "should grow columns evenly when equal deficient columns compete" do
|
576
|
+
@table = @pdf.table([["foo", "foobar"], ["foobar", "foo"]], :width => 500)
|
577
|
+
@table.cells[0, 0].width.should == @table.cells[0, 1].width
|
578
|
+
end
|
579
|
+
|
580
|
+
it "should respect manual widths" do
|
581
|
+
@table = @pdf.table([%w[foo bar baz], %w[baz bar foo]], :width => 500) do
|
582
|
+
column(1).width = 60
|
583
|
+
end
|
584
|
+
@table.column(1).width.should == 60
|
585
|
+
@table.column(0).width.should == @table.column(2).width
|
586
|
+
end
|
587
|
+
|
588
|
+
it "should allow table cells to be resized in block" do
|
589
|
+
# if anything goes wrong, a CannotFit error will be raised
|
590
|
+
|
591
|
+
@pdf.table([%w[1 2 3 4 5]]) do |t|
|
592
|
+
t.width = 40
|
593
|
+
t.cells.size = 8
|
594
|
+
t.cells.padding = 0
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
it "should be the width of the :width parameter" do
|
599
|
+
expected_width = 300
|
600
|
+
table = Prawn::Table.new( [%w[snake foo b apple],
|
601
|
+
%w[kitten d foobar banana]], @pdf,
|
602
|
+
:width => expected_width)
|
603
|
+
|
604
|
+
table.width.should == expected_width
|
605
|
+
end
|
606
|
+
|
607
|
+
it "should_not exceed the :width option" do
|
608
|
+
expected_width = 400
|
609
|
+
data = [
|
610
|
+
['This is a column with a lot of text that should comfortably exceed '+
|
611
|
+
'the width of a normal document margin_box width', 'Some more text',
|
612
|
+
'and then some more', 'Just a bit more to be extra sure']
|
613
|
+
]
|
614
|
+
table = Prawn::Table.new(data, @pdf, :width => expected_width)
|
615
|
+
|
616
|
+
table.width.should == expected_width
|
617
|
+
end
|
618
|
+
|
619
|
+
it "should_not exceed the :width option even with manual widths specified" do
|
620
|
+
expected_width = 400
|
621
|
+
data = [
|
622
|
+
['This is a column with a lot of text that should comfortably exceed '+
|
623
|
+
'the width of a normal document margin_box width', 'Some more text',
|
624
|
+
'and then some more', 'Just a bit more to be extra sure']
|
625
|
+
]
|
626
|
+
table = Prawn::Table.new(data, @pdf, :width => expected_width) do
|
627
|
+
column(1).width = 100
|
628
|
+
end
|
629
|
+
|
630
|
+
table.width.should == expected_width
|
631
|
+
end
|
632
|
+
|
633
|
+
it "should calculate unspecified column widths even " +
|
634
|
+
"with colspan cells declared" do
|
635
|
+
pdf = Prawn::Document.new
|
636
|
+
hpad, fs = 3, 5
|
637
|
+
columns = 3
|
638
|
+
|
639
|
+
data = [ [ { :content => 'foo', :colspan => 2 }, "foobar" ],
|
640
|
+
[ "foo", "foo", "foo" ] ]
|
641
|
+
table = Prawn::Table.new( data, pdf,
|
642
|
+
:cell_style => {
|
643
|
+
:padding_left => hpad, :padding_right => hpad,
|
644
|
+
:size => fs
|
645
|
+
})
|
646
|
+
|
647
|
+
col0_width = pdf.width_of("foo", :size => fs) # cell 1, 0
|
648
|
+
col1_width = pdf.width_of("foo", :size => fs) # cell 1, 1
|
649
|
+
col2_width = pdf.width_of("foobar", :size => fs) # cell 0, 1 (at col 2)
|
650
|
+
|
651
|
+
table.width.should == col0_width + col1_width +
|
652
|
+
col2_width + 2*columns*hpad
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
describe "height" do
|
657
|
+
it "should set all cells in a row to the same height" do
|
658
|
+
@table = @pdf.table([["foo", @long_text]])
|
659
|
+
@table.cells[0, 0].height.should == @table.cells[0, 1].height
|
660
|
+
end
|
661
|
+
|
662
|
+
it "should move y-position to the bottom of the table after drawing" do
|
663
|
+
old_y = @pdf.y
|
664
|
+
table = @pdf.table([["foo"]])
|
665
|
+
@pdf.y.should == old_y - table.height
|
666
|
+
end
|
667
|
+
|
668
|
+
it "should_not wrap unnecessarily" do
|
669
|
+
# Test for FP errors and glitches
|
670
|
+
t = @pdf.table([["Bender Bending Rodriguez"]])
|
671
|
+
h = @pdf.height_of("one line")
|
672
|
+
(t.height - 10).should be < h*1.5
|
673
|
+
end
|
674
|
+
|
675
|
+
it "should have a height of n rows" do
|
676
|
+
data = [["foo"],["bar"],["baaaz"]]
|
677
|
+
|
678
|
+
vpad = 4
|
679
|
+
origin = @pdf.y
|
680
|
+
@pdf.table data, :cell_style => { :padding => vpad }
|
681
|
+
|
682
|
+
table_height = origin - @pdf.y
|
683
|
+
font_height = @pdf.font.height
|
684
|
+
line_gap = @pdf.font.line_gap
|
685
|
+
|
686
|
+
num_rows = data.length
|
687
|
+
table_height.should be_within(0.001).of(
|
688
|
+
num_rows * font_height + 2*vpad*num_rows )
|
689
|
+
end
|
690
|
+
|
691
|
+
end
|
692
|
+
|
693
|
+
describe "position" do
|
694
|
+
it "should center tables with :position => :center" do
|
695
|
+
@pdf.expects(:bounding_box).with do |(x, y), opts|
|
696
|
+
expected = (@pdf.bounds.width - 500) / 2.0
|
697
|
+
(x - expected).abs < 0.001
|
698
|
+
end
|
699
|
+
|
700
|
+
@pdf.table([["foo"]], :column_widths => 500, :position => :center)
|
701
|
+
end
|
702
|
+
|
703
|
+
it "should right-align tables with :position => :right" do
|
704
|
+
@pdf.expects(:bounding_box).with do |(x, y), opts|
|
705
|
+
expected = @pdf.bounds.width - 500
|
706
|
+
(x - expected).abs < 0.001
|
707
|
+
end
|
708
|
+
|
709
|
+
@pdf.table([["foo"]], :column_widths => 500, :position => :right)
|
710
|
+
end
|
711
|
+
|
712
|
+
it "should accept a Numeric" do
|
713
|
+
@pdf.expects(:bounding_box).with do |(x, y), opts|
|
714
|
+
expected = 123
|
715
|
+
(x - expected).abs < 0.001
|
716
|
+
end
|
717
|
+
|
718
|
+
@pdf.table([["foo"]], :column_widths => 500, :position => 123)
|
719
|
+
end
|
720
|
+
|
721
|
+
it "should raise_error an ArgumentError on unknown :position" do
|
722
|
+
lambda do
|
723
|
+
@pdf.table([["foo"]], :position => :bratwurst)
|
724
|
+
end.should raise_error(ArgumentError)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
end
|
729
|
+
|
730
|
+
describe "Multi-page tables" do
|
731
|
+
it "should flow to the next page when hitting the bottom of the bounds" do
|
732
|
+
Prawn::Document.new { table([["foo"]] * 30) }.page_count.should == 1
|
733
|
+
Prawn::Document.new { table([["foo"]] * 31) }.page_count.should == 2
|
734
|
+
Prawn::Document.new { table([["foo"]] * 31); table([["foo"]] * 35) }.
|
735
|
+
page_count.should == 3
|
736
|
+
end
|
737
|
+
|
738
|
+
it "should respect the containing bounds" do
|
739
|
+
Prawn::Document.new do
|
740
|
+
bounding_box([0, cursor], :width => bounds.width, :height => 72) do
|
741
|
+
table([["foo"]] * 4)
|
742
|
+
end
|
743
|
+
end.page_count.should == 2
|
744
|
+
end
|
745
|
+
|
746
|
+
it "should_not start a new page before finishing out a row" do
|
747
|
+
Prawn::Document.new do
|
748
|
+
table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
|
749
|
+
end.page_count.should == 1
|
750
|
+
end
|
751
|
+
|
752
|
+
it "should only start new page on long cells if it would gain us height" do
|
753
|
+
Prawn::Document.new do
|
754
|
+
text "Hello"
|
755
|
+
table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
|
756
|
+
end.page_count.should == 2
|
757
|
+
end
|
758
|
+
|
759
|
+
it "should_not start a new page to gain height when at the top of " +
|
760
|
+
"a bounding box, even if stretchy" do
|
761
|
+
Prawn::Document.new do
|
762
|
+
bounding_box([bounds.left, bounds.top - 20], :width => 400) do
|
763
|
+
table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
|
764
|
+
end
|
765
|
+
end.page_count.should == 1
|
766
|
+
end
|
767
|
+
|
768
|
+
it "should still break to the next page if in a stretchy bounding box " +
|
769
|
+
"but not at the top" do
|
770
|
+
Prawn::Document.new do
|
771
|
+
bounding_box([bounds.left, bounds.top - 20], :width => 400) do
|
772
|
+
text "Hello"
|
773
|
+
table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
|
774
|
+
end
|
775
|
+
end.page_count.should == 2
|
776
|
+
end
|
777
|
+
|
778
|
+
it "should only draw first-page header if the first body row fits" do
|
779
|
+
pdf = Prawn::Document.new
|
780
|
+
|
781
|
+
pdf.y = 60 # not enough room for a table row
|
782
|
+
pdf.table [["Header"], ["Body"]], :header => true
|
783
|
+
|
784
|
+
output = PDF::Inspector::Page.analyze(pdf.render)
|
785
|
+
# Ensure we only drew the header once, on the second page
|
786
|
+
output.pages[0][:strings].should be_empty
|
787
|
+
output.pages[1][:strings].should == ["Header", "Body"]
|
788
|
+
end
|
789
|
+
|
790
|
+
it 'should only draw first-page header if the first multi-row fits',
|
791
|
+
:issue => 707 do
|
792
|
+
pdf = Prawn::Document.new
|
793
|
+
|
794
|
+
pdf.y = 100 # not enough room for the header and multirow cell
|
795
|
+
pdf.table [
|
796
|
+
[{content: 'Header', colspan: 2}],
|
797
|
+
[{content: 'Multirow cell', rowspan: 3}, 'Line 1'],
|
798
|
+
] + (2..3).map { |i| ["Line #{i}"] }, :header => true
|
799
|
+
|
800
|
+
output = PDF::Inspector::Page.analyze(pdf.render)
|
801
|
+
# Ensure we only drew the header once, on the second page
|
802
|
+
output.pages[0][:strings].should == []
|
803
|
+
output.pages[1][:strings].should == ['Header', 'Multirow cell', 'Line 1',
|
804
|
+
'Line 2', 'Line 3']
|
805
|
+
end
|
806
|
+
|
807
|
+
it "should draw background before borders, but only within pages" do
|
808
|
+
seq = sequence("drawing_order")
|
809
|
+
|
810
|
+
@pdf = Prawn::Document.new
|
811
|
+
|
812
|
+
# give enough room for only the first row
|
813
|
+
@pdf.y = @pdf.bounds.absolute_bottom + 30
|
814
|
+
t = @pdf.make_table([["A", "B"],
|
815
|
+
["C", "D"]],
|
816
|
+
:cell_style => {:background_color => 'ff0000'})
|
817
|
+
|
818
|
+
ca = t.cells[0, 0]
|
819
|
+
cb = t.cells[0, 1]
|
820
|
+
cc = t.cells[1, 0]
|
821
|
+
cd = t.cells[1, 1]
|
822
|
+
|
823
|
+
# All backgrounds should draw before any borders on page 1...
|
824
|
+
ca.expects(:draw_background).in_sequence(seq)
|
825
|
+
cb.expects(:draw_background).in_sequence(seq)
|
826
|
+
ca.expects(:draw_borders).in_sequence(seq)
|
827
|
+
cb.expects(:draw_borders).in_sequence(seq)
|
828
|
+
# ...and page 2
|
829
|
+
@pdf.expects(:start_new_page).in_sequence(seq)
|
830
|
+
cc.expects(:draw_background).in_sequence(seq)
|
831
|
+
cd.expects(:draw_background).in_sequence(seq)
|
832
|
+
cc.expects(:draw_borders).in_sequence(seq)
|
833
|
+
cd.expects(:draw_borders).in_sequence(seq)
|
834
|
+
|
835
|
+
t.draw
|
836
|
+
end
|
837
|
+
|
838
|
+
describe "before_rendering_page callback" do
|
839
|
+
before(:each) { @pdf = Prawn::Document.new }
|
840
|
+
|
841
|
+
it "is passed all cells to be rendered on that page" do
|
842
|
+
kicked = 0
|
843
|
+
|
844
|
+
@pdf.table([["foo"]] * 100) do |t|
|
845
|
+
t.before_rendering_page do |page|
|
846
|
+
page.row_count.should == ((kicked < 3) ? 30 : 10)
|
847
|
+
page.column_count.should == 1
|
848
|
+
page.row(0).first.content.should == "foo"
|
849
|
+
page.row(-1).first.content.should == "foo"
|
850
|
+
kicked += 1
|
851
|
+
end
|
852
|
+
end
|
853
|
+
|
854
|
+
kicked.should == 4
|
855
|
+
end
|
856
|
+
|
857
|
+
it "numbers cells relative to their position on page" do
|
858
|
+
@pdf.table([["foo"]] * 100) do |t|
|
859
|
+
t.before_rendering_page do |page|
|
860
|
+
page[0, 0].content.should == "foo"
|
861
|
+
end
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
it "changing cells in the callback affects their rendering" do
|
866
|
+
seq = sequence("render order")
|
867
|
+
|
868
|
+
t = @pdf.make_table([["foo"]] * 40) do |table|
|
869
|
+
table.before_rendering_page do |page|
|
870
|
+
page[0, 0].background_color = "ff0000"
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
874
|
+
t.cells[30, 0].stubs(:draw_background).checking do |xy|
|
875
|
+
t.cells[30, 0].background_color.should == 'ff0000'
|
876
|
+
end
|
877
|
+
t.cells[31, 0].stubs(:draw_background).checking do |xy|
|
878
|
+
t.cells[31, 0].background_color.should == nil
|
879
|
+
end
|
880
|
+
|
881
|
+
t.draw
|
882
|
+
end
|
883
|
+
|
884
|
+
it "passes headers on page 2+" do
|
885
|
+
@pdf.table([["header"]] + [["foo"]] * 100, :header => true) do |t|
|
886
|
+
t.before_rendering_page do |page|
|
887
|
+
page[0, 0].content.should == "header"
|
888
|
+
end
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
it "updates dummy cell header rows" do
|
893
|
+
header = [[{:content => "header", :colspan => 2}]]
|
894
|
+
data = [["foo", "bar"]] * 31
|
895
|
+
@pdf.table(header + data, :header => true) do |t|
|
896
|
+
t.before_rendering_page do |page|
|
897
|
+
cell = page[0, 0]
|
898
|
+
cell.dummy_cells.each {|dc| dc.row.should == cell.row }
|
899
|
+
end
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
it "allows headers to be changed" do
|
904
|
+
seq = sequence("render order")
|
905
|
+
@pdf.expects(:draw_text!).with { |t, _| t == "hdr1"}.in_sequence(seq)
|
906
|
+
@pdf.expects(:draw_text!).with { |t, _| t == "foo"}.times(29).in_sequence(seq)
|
907
|
+
# Verify that the changed cell doesn't mutate subsequent pages
|
908
|
+
@pdf.expects(:draw_text!).with { |t, _| t == "header"}.in_sequence(seq)
|
909
|
+
@pdf.expects(:draw_text!).with { |t, _| t == "foo"}.times(11).in_sequence(seq)
|
910
|
+
|
911
|
+
set_first_page_headers = false
|
912
|
+
@pdf.table([["header"]] + [["foo"]] * 40, :header => true) do |t|
|
913
|
+
t.before_rendering_page do |page|
|
914
|
+
# only change first page header
|
915
|
+
page[0, 0].content = "hdr1" unless set_first_page_headers
|
916
|
+
set_first_page_headers = true
|
917
|
+
end
|
918
|
+
end
|
919
|
+
end
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
describe "#style" do
|
924
|
+
it "should send #style to its first argument, passing the style hash and" +
|
925
|
+
" block" do
|
926
|
+
|
927
|
+
stylable = stub()
|
928
|
+
stylable.expects(:style).with(:foo => :bar).once.yields
|
929
|
+
|
930
|
+
block = stub()
|
931
|
+
block.expects(:kick).once
|
932
|
+
|
933
|
+
Prawn::Document.new do
|
934
|
+
table([["x"]]) { style(stylable, :foo => :bar) { block.kick } }
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
it "should default to {} for the hash argument" do
|
939
|
+
stylable = stub()
|
940
|
+
stylable.expects(:style).with({}).once
|
941
|
+
|
942
|
+
Prawn::Document.new do
|
943
|
+
table([["x"]]) { style(stylable) }
|
944
|
+
end
|
945
|
+
end
|
946
|
+
|
947
|
+
it "ignores unknown values on a cell-by-cell basis" do
|
948
|
+
Prawn::Document.new do
|
949
|
+
table([["x", [["y"]]]], :cell_style => {:overflow => :shrink_to_fit})
|
950
|
+
end
|
951
|
+
end
|
952
|
+
end
|
953
|
+
|
954
|
+
describe "row_colors" do
|
955
|
+
it "should allow array syntax for :row_colors" do
|
956
|
+
data = [["foo"], ["bar"], ["baz"]]
|
957
|
+
pdf = Prawn::Document.new
|
958
|
+
t = pdf.table(data, :row_colors => ['cccccc', 'ffffff'])
|
959
|
+
t.cells.map{|x| x.background_color}.should == %w[cccccc ffffff cccccc]
|
960
|
+
end
|
961
|
+
|
962
|
+
it "should ignore headers" do
|
963
|
+
data = [["header"], ["foo"], ["bar"], ["baz"]]
|
964
|
+
pdf = Prawn::Document.new
|
965
|
+
t = pdf.table(data, :header => true,
|
966
|
+
:row_colors => ['cccccc', 'ffffff']) do
|
967
|
+
row(0).background_color = '333333'
|
968
|
+
end
|
969
|
+
|
970
|
+
t.cells.map{|x| x.background_color}.should ==
|
971
|
+
%w[333333 cccccc ffffff cccccc]
|
972
|
+
end
|
973
|
+
|
974
|
+
it "stripes rows consistently from page to page, skipping header rows" do
|
975
|
+
data = [["header"]] + [["foo"]] * 70
|
976
|
+
pdf = Prawn::Document.new
|
977
|
+
t = pdf.make_table(data, :header => true,
|
978
|
+
:row_colors => ['cccccc', 'ffffff']) do
|
979
|
+
cells.padding = 0
|
980
|
+
cells.size = 9
|
981
|
+
row(0).size = 11
|
982
|
+
end
|
983
|
+
|
984
|
+
# page 1: header + 67 cells (odd number -- verifies that the next
|
985
|
+
# page disrupts the even/odd coloring, since both the last data cell
|
986
|
+
# on this page and the first one on the next are colored cccccc)
|
987
|
+
Prawn::Table::Cell.expects(:draw_cells).with do |cells|
|
988
|
+
cells.map { |c, (x, y)| c.background_color } ==
|
989
|
+
[nil] + (%w[cccccc ffffff] * 33) + %w[cccccc]
|
990
|
+
end
|
991
|
+
# page 2: header and 3 data cells
|
992
|
+
Prawn::Table::Cell.expects(:draw_cells).with do |cells|
|
993
|
+
cells.map { |c, (x, y)| c.background_color } ==
|
994
|
+
[nil] + %w[cccccc ffffff cccccc]
|
995
|
+
end
|
996
|
+
t.draw
|
997
|
+
end
|
998
|
+
|
999
|
+
it "should_not override an explicit background_color" do
|
1000
|
+
data = [["foo"], ["bar"], ["baz"]]
|
1001
|
+
pdf = Prawn::Document.new
|
1002
|
+
table = pdf.table(data, :row_colors => ['cccccc', 'ffffff']) { |t|
|
1003
|
+
t.cells[0, 0].background_color = 'dddddd'
|
1004
|
+
}
|
1005
|
+
table.cells.map{|x| x.background_color}.should == %w[dddddd ffffff cccccc]
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
describe "inking" do
|
1010
|
+
before(:each) do
|
1011
|
+
@pdf = Prawn::Document.new
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
it "should set the x-position of each cell based on widths" do
|
1015
|
+
@table = @pdf.table([["foo", "bar", "baz"]])
|
1016
|
+
|
1017
|
+
x = 0
|
1018
|
+
(0..2).each do |col|
|
1019
|
+
cell = @table.cells[0, col]
|
1020
|
+
cell.x.should == x
|
1021
|
+
x += cell.width
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
it "should set the y-position of each cell based on heights" do
|
1026
|
+
y = 0
|
1027
|
+
@table = @pdf.make_table([["foo"], ["bar"], ["baz"]])
|
1028
|
+
|
1029
|
+
(0..2).each do |row|
|
1030
|
+
cell = @table.cells[row, 0]
|
1031
|
+
cell.y.should be_within(0.01).of(y)
|
1032
|
+
y -= cell.height
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
it "should output content cell by cell, row by row" do
|
1037
|
+
data = [["foo","bar"],["baz","bang"]]
|
1038
|
+
@pdf = Prawn::Document.new
|
1039
|
+
@pdf.table(data)
|
1040
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1041
|
+
output.strings.should == data.flatten
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
it "should_not cause an error if rendering the very first row causes a " +
|
1045
|
+
"page break" do
|
1046
|
+
Prawn::Document.new do |pdf|
|
1047
|
+
arr = Array(1..5).collect{|i| ["cell #{i}"] }
|
1048
|
+
|
1049
|
+
pdf.move_down( pdf.y - (pdf.bounds.absolute_bottom + 3) )
|
1050
|
+
|
1051
|
+
lambda {
|
1052
|
+
pdf.table(arr)
|
1053
|
+
}.should_not raise_error
|
1054
|
+
end
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
it "should draw all backgrounds before any borders" do
|
1058
|
+
# lest backgrounds overlap borders:
|
1059
|
+
# https://github.com/sandal/prawn/pull/226
|
1060
|
+
|
1061
|
+
seq = sequence("drawing_order")
|
1062
|
+
|
1063
|
+
t = @pdf.make_table([["A", "B"]],
|
1064
|
+
:cell_style => {:background_color => 'ff0000'})
|
1065
|
+
ca = t.cells[0, 0]
|
1066
|
+
cb = t.cells[0, 1]
|
1067
|
+
|
1068
|
+
# XXX Not a perfectly general test, because it would still be acceptable
|
1069
|
+
# if we drew B then A
|
1070
|
+
ca.expects(:draw_background).in_sequence(seq)
|
1071
|
+
cb.expects(:draw_background).in_sequence(seq)
|
1072
|
+
ca.expects(:draw_borders).in_sequence(seq)
|
1073
|
+
cb.expects(:draw_borders).in_sequence(seq)
|
1074
|
+
|
1075
|
+
t.draw
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
it "should allow multiple inkings of the same table" do
|
1079
|
+
pdf = Prawn::Document.new
|
1080
|
+
t = Prawn::Table.new([["foo"]], pdf)
|
1081
|
+
|
1082
|
+
pdf.expects(:bounding_box).with{|(x, y), options| y.to_i == 495}.yields
|
1083
|
+
pdf.expects(:bounding_box).with{|(x, y), options| y.to_i == 395}.yields
|
1084
|
+
pdf.expects(:draw_text!).with{ |text, options| text == 'foo' }.twice
|
1085
|
+
|
1086
|
+
pdf.move_cursor_to(500)
|
1087
|
+
t.draw
|
1088
|
+
|
1089
|
+
pdf.move_cursor_to(400)
|
1090
|
+
t.draw
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
describe "in stretchy bounding boxes" do
|
1094
|
+
it "should draw all cells on a row at the same y-position" do
|
1095
|
+
pdf = Prawn::Document.new
|
1096
|
+
|
1097
|
+
text_y = pdf.y.to_i - 5 # text starts 5pt below current y pos (padding)
|
1098
|
+
|
1099
|
+
pdf.bounding_box([0, pdf.cursor], :width => pdf.bounds.width) do
|
1100
|
+
pdf.expects(:draw_text!).checking { |text, options|
|
1101
|
+
pdf.bounds.absolute_top.should == text_y
|
1102
|
+
}.times(3)
|
1103
|
+
|
1104
|
+
pdf.table([%w[a b c]])
|
1105
|
+
end
|
1106
|
+
end
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
describe "headers" do
|
1111
|
+
context "single row header" do
|
1112
|
+
it "should add headers to output when specified" do
|
1113
|
+
data = [["a", "b"], ["foo","bar"],["baz","bang"]]
|
1114
|
+
@pdf = Prawn::Document.new
|
1115
|
+
@pdf.table(data, :header => true)
|
1116
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1117
|
+
output.strings.should == data.flatten
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
it "should repeat headers across pages" do
|
1121
|
+
data = [["foo","bar"]] * 30
|
1122
|
+
headers = ["baz","foobar"]
|
1123
|
+
@pdf = Prawn::Document.new
|
1124
|
+
@pdf.table([headers] + data, :header => true)
|
1125
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1126
|
+
output.strings.should == headers + data.flatten[0..-3] + headers +
|
1127
|
+
data.flatten[-2..-1]
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
it "draws headers at the correct position" do
|
1131
|
+
data = [["header"]] + [["foo"]] * 40
|
1132
|
+
|
1133
|
+
Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
|
1134
|
+
cells.each do |cell, pt|
|
1135
|
+
if cell.content == "header"
|
1136
|
+
# Assert that header text is drawn at the same location on each page
|
1137
|
+
if @header_location
|
1138
|
+
pt.should == @header_location
|
1139
|
+
else
|
1140
|
+
@header_location = pt
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
@pdf = Prawn::Document.new
|
1146
|
+
@pdf.table(data, :header => true)
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
it "draws headers at the correct position with column box" do
|
1150
|
+
data = [["header"]] + [["foo"]] * 40
|
1151
|
+
|
1152
|
+
Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
|
1153
|
+
cells.each do |cell, pt|
|
1154
|
+
if cell.content == "header"
|
1155
|
+
pt[0].should == @pdf.bounds.left
|
1156
|
+
end
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
@pdf = Prawn::Document.new
|
1160
|
+
@pdf.column_box [0, @pdf.cursor], :width => @pdf.bounds.width, :columns => 2 do
|
1161
|
+
@pdf.table(data, :header => true)
|
1162
|
+
end
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
it "should_not draw header twice when starting new page" do
|
1166
|
+
@pdf = Prawn::Document.new
|
1167
|
+
@pdf.y = 0
|
1168
|
+
@pdf.table([["Header"], ["Body"]], :header => true)
|
1169
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1170
|
+
output.strings.should == ["Header", "Body"]
|
1171
|
+
end
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
context "multiple row header" do
|
1175
|
+
it "should add headers to output when specified" do
|
1176
|
+
data = [["a", "b"], ["c", "d"], ["foo","bar"],["baz","bang"]]
|
1177
|
+
@pdf = Prawn::Document.new
|
1178
|
+
@pdf.table(data, :header => 2)
|
1179
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1180
|
+
output.strings.should == data.flatten
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
it "should repeat headers across pages" do
|
1184
|
+
data = [["foo","bar"]] * 30
|
1185
|
+
headers = ["baz","foobar"] + ["bas", "foobaz"]
|
1186
|
+
@pdf = Prawn::Document.new
|
1187
|
+
@pdf.table([headers] + data, :header => 2)
|
1188
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1189
|
+
output.strings.should == headers + data.flatten[0..-3] + headers +
|
1190
|
+
data.flatten[-4..-1]
|
1191
|
+
end
|
1192
|
+
|
1193
|
+
it "draws headers at the correct position" do
|
1194
|
+
data = [["header"]] + [["header2"]] + [["foo"]] * 40
|
1195
|
+
|
1196
|
+
Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
|
1197
|
+
cells.each do |cell, pt|
|
1198
|
+
if cell.content == "header"
|
1199
|
+
# Assert that header text is drawn at the same location on each page
|
1200
|
+
if @header_location
|
1201
|
+
pt.should == @header_location
|
1202
|
+
else
|
1203
|
+
@header_location = pt
|
1204
|
+
end
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
if cell.content == "header2"
|
1208
|
+
# Assert that header text is drawn at the same location on each page
|
1209
|
+
if @header2_location
|
1210
|
+
pt.should == @header2_location
|
1211
|
+
else
|
1212
|
+
@header2_location = pt
|
1213
|
+
end
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
@pdf = Prawn::Document.new
|
1218
|
+
@pdf.table(data, :header => 2)
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
it "should_not draw header twice when starting new page" do
|
1222
|
+
@pdf = Prawn::Document.new
|
1223
|
+
@pdf.y = 0
|
1224
|
+
@pdf.table([["Header"], ["Header2"], ["Body"]], :header => 2)
|
1225
|
+
output = PDF::Inspector::Text.analyze(@pdf.render)
|
1226
|
+
output.strings.should == ["Header", "Header2", "Body"]
|
1227
|
+
end
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
describe "nested tables" do
|
1232
|
+
before(:each) do
|
1233
|
+
@pdf = Prawn::Document.new
|
1234
|
+
@subtable = Prawn::Table.new([["foo"]], @pdf)
|
1235
|
+
@table = @pdf.table([[@subtable, "bar"]])
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
it "can be created from an Array" do
|
1239
|
+
cell = Prawn::Table::Cell.make(@pdf, [["foo"]])
|
1240
|
+
cell.should be_a_kind_of(Prawn::Table::Cell::Subtable)
|
1241
|
+
cell.subtable.should be_a_kind_of(Prawn::Table)
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
it "defaults its padding to zero" do
|
1245
|
+
@table.cells[0, 0].padding.should == [0, 0, 0, 0]
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
it "has a subtable accessor" do
|
1249
|
+
@table.cells[0, 0].subtable.should == @subtable
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
it "determines its dimensions from the subtable" do
|
1253
|
+
@table.cells[0, 0].width.should == @subtable.width
|
1254
|
+
@table.cells[0, 0].height.should == @subtable.height
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
describe "An invalid table" do
|
1260
|
+
|
1261
|
+
before(:each) do
|
1262
|
+
@pdf = Prawn::Document.new
|
1263
|
+
@bad_data = ["Single Nested Array"]
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
it "should raise_error error when invalid table data is given" do
|
1267
|
+
lambda {
|
1268
|
+
@pdf.table(@bad_data)
|
1269
|
+
}.should raise_error(Prawn::Errors::InvalidTableData)
|
1270
|
+
end
|
1271
|
+
|
1272
|
+
it "should raise_error an EmptyTableError with empty table data" do
|
1273
|
+
lambda {
|
1274
|
+
data = []
|
1275
|
+
@pdf = Prawn::Document.new
|
1276
|
+
@pdf.table(data)
|
1277
|
+
}.should raise_error( Prawn::Errors::EmptyTable )
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
it "should raise_error an EmptyTableError with nil table data" do
|
1281
|
+
lambda {
|
1282
|
+
data = nil
|
1283
|
+
@pdf = Prawn::Document.new
|
1284
|
+
@pdf.table(data)
|
1285
|
+
}.should raise_error( Prawn::Errors::EmptyTable )
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
describe "colspan / rowspan" do
|
1293
|
+
before(:each) { create_pdf }
|
1294
|
+
|
1295
|
+
it "doesn't raise an error" do
|
1296
|
+
lambda {
|
1297
|
+
@pdf.table([[{:content => "foo", :colspan => 2, :rowspan => 2}]])
|
1298
|
+
}.should_not raise_error
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
it "colspan is properly counted" do
|
1302
|
+
t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
|
1303
|
+
t.column_length.should == 2
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
it "rowspan is properly counted" do
|
1307
|
+
t = @pdf.make_table([[{:content => "foo", :rowspan => 2}]])
|
1308
|
+
t.row_length.should == 2
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
it "raises if colspan or rowspan are called after layout" do
|
1312
|
+
lambda {
|
1313
|
+
@pdf.table([["foo"]]) { cells[0, 0].colspan = 2 }
|
1314
|
+
}.should raise_error(Prawn::Errors::InvalidTableSpan)
|
1315
|
+
|
1316
|
+
lambda {
|
1317
|
+
@pdf.table([["foo"]]) { cells[0, 0].rowspan = 2 }
|
1318
|
+
}.should raise_error(Prawn::Errors::InvalidTableSpan)
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
it "raises when spans overlap" do
|
1322
|
+
lambda {
|
1323
|
+
@pdf.table([["foo", {:content => "bar", :rowspan => 2}],
|
1324
|
+
[{:content => "baz", :colspan => 2}]])
|
1325
|
+
}.should raise_error(Prawn::Errors::InvalidTableSpan)
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
it "table and cell width account for colspan" do
|
1329
|
+
t = @pdf.table([["a", {:content => "b", :colspan => 2}]],
|
1330
|
+
:column_widths => [100, 100, 100])
|
1331
|
+
spanned = t.cells[0, 1]
|
1332
|
+
spanned.colspan.should == 2
|
1333
|
+
t.width.should == 300
|
1334
|
+
t.cells.min_width.should == 300
|
1335
|
+
t.cells.max_width.should == 300
|
1336
|
+
spanned.width.should == 200
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
it "table and cell height account for rowspan" do
|
1340
|
+
t = @pdf.table([["a"], [{:content => "b", :rowspan => 2}]]) do
|
1341
|
+
row(0..2).height = 100
|
1342
|
+
end
|
1343
|
+
spanned = t.cells[1, 0]
|
1344
|
+
spanned.rowspan.should == 2
|
1345
|
+
t.height.should == 300
|
1346
|
+
spanned.height.should == 200
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
it "provides the full content_width as drawing space" do
|
1350
|
+
w = @pdf.make_table([["foo"]]).cells[0, 0].content_width
|
1351
|
+
|
1352
|
+
t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
|
1353
|
+
t.cells[0, 0].spanned_content_width.should == w
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
it "dummy cells are not drawn" do
|
1357
|
+
# make a fake master cell for the dummy cell to slave to
|
1358
|
+
t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
|
1359
|
+
|
1360
|
+
# drawing just a dummy cell should_not ink
|
1361
|
+
@pdf.expects(:stroke_line).never
|
1362
|
+
@pdf.expects(:draw_text!).never
|
1363
|
+
Prawn::Table::Cell.draw_cells([t.cells[0, 1]])
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
it "dummy cells do not add any height or width" do
|
1367
|
+
t1 = @pdf.table([["foo"]])
|
1368
|
+
|
1369
|
+
t2 = @pdf.table([[{:content => "foo", :colspan => 2}]])
|
1370
|
+
t2.width.should == t1.width
|
1371
|
+
|
1372
|
+
t3 = @pdf.table([[{:content => "foo", :rowspan => 2}]])
|
1373
|
+
t3.height.should == t1.height
|
1374
|
+
end
|
1375
|
+
|
1376
|
+
it "dummy cells ignored by #style" do
|
1377
|
+
t = @pdf.table([[{:content => "blah", :colspan => 2}]],
|
1378
|
+
:cell_style => { :size => 9 })
|
1379
|
+
t.cells[0, 0].size.should == 9
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
context "inheriting master cell styles from dummy cell" do
|
1383
|
+
# Relatively full coverage for all these attributes that should be
|
1384
|
+
# inherited.
|
1385
|
+
[["border_X_width", 20],
|
1386
|
+
["border_X_color", "123456"],
|
1387
|
+
["padding_X", 20]].each do |attribute, val|
|
1388
|
+
attribute_right = attribute.sub("X", "right")
|
1389
|
+
attribute_left = attribute.sub("X", "left")
|
1390
|
+
attribute_bottom = attribute.sub("X", "bottom")
|
1391
|
+
attribute_top = attribute.sub("X", "top")
|
1392
|
+
|
1393
|
+
specify "#{attribute_right} of right column is inherited" do
|
1394
|
+
t = @pdf.table([[{:content => "blah", :colspan => 2}]]) do |table|
|
1395
|
+
table.column(1).send("#{attribute_right}=", val)
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
t.cells[0, 0].send(attribute_right).should == val
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
specify "#{attribute_bottom} of bottom row is inherited" do
|
1402
|
+
t = @pdf.table([[{:content => "blah", :rowspan => 2}]]) do |table|
|
1403
|
+
table.row(1).send("#{attribute_bottom}=", val)
|
1404
|
+
end
|
1405
|
+
|
1406
|
+
t.cells[0, 0].send(attribute_bottom).should == val
|
1407
|
+
end
|
1408
|
+
|
1409
|
+
specify "#{attribute_left} of right column is not inherited" do
|
1410
|
+
t = @pdf.table([[{:content => "blah", :colspan => 2}]]) do |table|
|
1411
|
+
table.column(1).send("#{attribute_left}=", val)
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
t.cells[0, 0].send(attribute_left).should_not == val
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
specify "#{attribute_right} of interior column is not inherited" do
|
1418
|
+
t = @pdf.table([[{:content => "blah", :colspan => 3}]]) do |table|
|
1419
|
+
table.column(1).send("#{attribute_right}=", val)
|
1420
|
+
end
|
1421
|
+
|
1422
|
+
t.cells[0, 0].send(attribute_right).should_not == val
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
specify "#{attribute_bottom} of interior row is not inherited" do
|
1426
|
+
t = @pdf.table([[{:content => "blah", :rowspan => 3}]]) do |table|
|
1427
|
+
table.row(1).send("#{attribute_bottom}=", val)
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
t.cells[0, 0].send(attribute_bottom).should_not == val
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
specify "#{attribute_top} of bottom row is not inherited" do
|
1434
|
+
t = @pdf.table([[{:content => "blah", :rowspan => 2}]]) do |table|
|
1435
|
+
table.row(1).send("#{attribute_top}=", val)
|
1436
|
+
end
|
1437
|
+
|
1438
|
+
t.cells[0, 0].send(attribute_top).should_not == val
|
1439
|
+
end
|
1440
|
+
end
|
1441
|
+
end
|
1442
|
+
|
1443
|
+
it "splits natural width between cols in the group" do
|
1444
|
+
t = @pdf.table([[{:content => "foo", :colspan => 2}]])
|
1445
|
+
widths = t.column_widths
|
1446
|
+
widths[0].should == widths[1]
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
it "splits natural width between cols when width is increased" do
|
1450
|
+
t = @pdf.table([[{:content => "foo", :colspan => 2}]],
|
1451
|
+
:width => @pdf.bounds.width)
|
1452
|
+
widths = t.column_widths
|
1453
|
+
widths[0].should == widths[1]
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
it "splits min-width between cols in the group" do
|
1457
|
+
# Since column_widths, when reducing column widths, reduces proportional to
|
1458
|
+
# the remaining width after each column's min width, we must ensure that the
|
1459
|
+
# min-width is split proportionally in order to ensure the width is still
|
1460
|
+
# split evenly when the width is reduced. (See "splits natural width between
|
1461
|
+
# cols when width is reduced".)
|
1462
|
+
t = @pdf.table([[{:content => "foo", :colspan => 2}]],
|
1463
|
+
:width => 20)
|
1464
|
+
t.column(0).min_width.should == t.column(1).min_width
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
it "splits natural width between cols when width is reduced" do
|
1468
|
+
t = @pdf.table([[{:content => "foo", :colspan => 2}]],
|
1469
|
+
:width => 20)
|
1470
|
+
widths = t.column_widths
|
1471
|
+
widths[0].should == widths[1]
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
it "honors a large, explicitly set table width" do
|
1475
|
+
t = @pdf.table([[{:content => "AAAAAAAAAA", :colspan => 3}],
|
1476
|
+
["A", "B", "C"]],
|
1477
|
+
:width => 400)
|
1478
|
+
|
1479
|
+
t.column_widths.inject(0) { |sum, w| sum + w }.
|
1480
|
+
should be_within(0.01).of(400)
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
it "honors a small, explicitly set table width" do
|
1484
|
+
t = @pdf.table([[{:content => "Lorem ipsum dolor sit amet " * 20,
|
1485
|
+
:colspan => 3}],
|
1486
|
+
["A", "B", "C"]],
|
1487
|
+
:width => 200)
|
1488
|
+
t.column_widths.inject(0) { |sum, w| sum + w }.
|
1489
|
+
should be_within(0.01).of(200)
|
1490
|
+
end
|
1491
|
+
|
1492
|
+
it "splits natural_content_height between rows in the group" do
|
1493
|
+
t = @pdf.table([[{:content => "foo", :rowspan => 2}]])
|
1494
|
+
heights = t.row_heights
|
1495
|
+
heights[0].should == heights[1]
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
it "skips column numbers that have been col-spanned" do
|
1499
|
+
t = @pdf.table([["a", "b", {:content => "c", :colspan => 3}, "d"]])
|
1500
|
+
t.cells[0, 0].content.should == "a"
|
1501
|
+
t.cells[0, 1].content.should == "b"
|
1502
|
+
t.cells[0, 2].content.should == "c"
|
1503
|
+
t.cells[0, 3].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
|
1504
|
+
t.cells[0, 4].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
|
1505
|
+
t.cells[0, 5].content.should == "d"
|
1506
|
+
end
|
1507
|
+
|
1508
|
+
it "skips row/col positions that have been row-spanned" do
|
1509
|
+
t = @pdf.table([["a", {:content => "b", :colspan => 2, :rowspan => 2}, "c"],
|
1510
|
+
["d", "e"],
|
1511
|
+
["f", "g", "h", "i"]])
|
1512
|
+
t.cells[0, 0].content.should == "a"
|
1513
|
+
t.cells[0, 1].content.should == "b"
|
1514
|
+
t.cells[0, 2].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
|
1515
|
+
t.cells[0, 3].content.should == "c"
|
1516
|
+
|
1517
|
+
t.cells[1, 0].content.should == "d"
|
1518
|
+
t.cells[1, 1].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
|
1519
|
+
t.cells[1, 2].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
|
1520
|
+
t.cells[1, 3].content.should == "e"
|
1521
|
+
|
1522
|
+
t.cells[2, 0].content.should == "f"
|
1523
|
+
t.cells[2, 1].content.should == "g"
|
1524
|
+
t.cells[2, 2].content.should == "h"
|
1525
|
+
t.cells[2, 3].content.should == "i"
|
1526
|
+
end
|
1527
|
+
end
|