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,182 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
class Table
|
5
|
+
# @private
|
6
|
+
class ColumnWidthCalculator
|
7
|
+
def initialize(cells)
|
8
|
+
@cells = cells
|
9
|
+
|
10
|
+
@widths_by_column = Hash.new(0)
|
11
|
+
@rows_with_a_span_dummy = Hash.new(false)
|
12
|
+
|
13
|
+
#calculate for each row if it includes a Cell:SpanDummy
|
14
|
+
@cells.each do |cell|
|
15
|
+
@rows_with_a_span_dummy[cell.row] = true if cell.is_a?(Cell::SpanDummy)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# does this row include a Cell:SpanDummy?
|
20
|
+
#
|
21
|
+
# @param row - the row that should be checked for Cell:SpanDummy elements
|
22
|
+
#
|
23
|
+
def has_a_span_dummy?(row)
|
24
|
+
@rows_with_a_span_dummy[row]
|
25
|
+
end
|
26
|
+
|
27
|
+
# helper method
|
28
|
+
# column widths are stored in the values array
|
29
|
+
# a cell may span cells whose value is only partly given
|
30
|
+
# this function handles this special case
|
31
|
+
#
|
32
|
+
# @param values - The columns widths calculated up until now
|
33
|
+
# @param cell - The current cell
|
34
|
+
# @param index - The current column
|
35
|
+
# @param meth - Meth (min/max); used to calculate values to be filled
|
36
|
+
#
|
37
|
+
def fill_values_if_needed(values, cell, index, meth)
|
38
|
+
#have all spanned indices been filled with a value?
|
39
|
+
#e.g. values[0], values[1] and values[2] don't return nil given a index of 0 and a colspan of 3
|
40
|
+
number_of_nil_values = 0
|
41
|
+
cell.colspan.times do |i|
|
42
|
+
number_of_nil_values += 1 if values[index+i].nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
#nothing to do? because
|
46
|
+
#a) all values are filled
|
47
|
+
return values if number_of_nil_values == 0
|
48
|
+
#b) no values are filled
|
49
|
+
return values if number_of_nil_values == cell.colspan
|
50
|
+
#c) I am not sure why this line is needed FIXXME
|
51
|
+
#some test cases manage to this line even though there is no dummy cell in the row
|
52
|
+
#I'm not sure if this is a sign for a further underlying bug.
|
53
|
+
return values unless has_a_span_dummy?(cell.row)
|
54
|
+
#fill up the values array
|
55
|
+
|
56
|
+
#calculate the new sum
|
57
|
+
new_sum = cell.send(meth) * cell.colspan
|
58
|
+
#substract any calculated values
|
59
|
+
cell.colspan.times do |i|
|
60
|
+
new_sum -= values[index+i] unless values[index+i].nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
#calculate value for the remaining - not yet filled - cells.
|
64
|
+
new_value = new_sum.to_f / number_of_nil_values
|
65
|
+
#fill the not yet filled cells
|
66
|
+
cell.colspan.times do |i|
|
67
|
+
values[index+i] = new_value if values[index+i].nil?
|
68
|
+
end
|
69
|
+
return values
|
70
|
+
end
|
71
|
+
|
72
|
+
def natural_widths
|
73
|
+
#calculate natural column width for all rows that do not include a span dummy
|
74
|
+
@cells.each do |cell|
|
75
|
+
unless has_a_span_dummy?(cell.row)
|
76
|
+
@widths_by_column[cell.column] =
|
77
|
+
[@widths_by_column[cell.column], cell.width.to_f].max
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
#integrate natural column widths for all rows that do include a span dummy
|
82
|
+
@cells.each do |cell|
|
83
|
+
next unless has_a_span_dummy?(cell.row)
|
84
|
+
#the width of a SpanDummy cell will be calculated by the "mother" cell
|
85
|
+
next if cell.is_a?(Cell::SpanDummy)
|
86
|
+
|
87
|
+
if cell.colspan == 1
|
88
|
+
@widths_by_column[cell.column] =
|
89
|
+
[@widths_by_column[cell.column], cell.width.to_f].max
|
90
|
+
else
|
91
|
+
#calculate the current with of all cells that will be spanned by the current cell
|
92
|
+
current_width_of_spanned_cells =
|
93
|
+
@widths_by_column.to_a[cell.column..(cell.column + cell.colspan - 1)]
|
94
|
+
.collect{|key, value| value}.inject(0, :+)
|
95
|
+
|
96
|
+
#update the Hash only if the new with is at least equal to the old one
|
97
|
+
#due to arithmetic errors we need to ignore a small difference in the new and the old sum
|
98
|
+
#the same had to be done in the column_widht_calculator#natural_width
|
99
|
+
update_hash = ((cell.width.to_f - current_width_of_spanned_cells) >
|
100
|
+
Prawn::FLOAT_PRECISION)
|
101
|
+
|
102
|
+
if update_hash
|
103
|
+
# Split the width of colspanned cells evenly by columns
|
104
|
+
width_per_column = cell.width.to_f / cell.colspan
|
105
|
+
# Update the Hash
|
106
|
+
cell.colspan.times do |i|
|
107
|
+
@widths_by_column[cell.column + i] = width_per_column
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
@widths_by_column.sort_by { |col, _| col }.map { |_, w| w }
|
114
|
+
end
|
115
|
+
|
116
|
+
# get column widths (either min or max depending on meth)
|
117
|
+
# used in cells.rb
|
118
|
+
#
|
119
|
+
# @param row_or_column - you may call this on either rows or columns
|
120
|
+
# @param meth - min/max
|
121
|
+
# @param aggregate - functions from cell.rb to be used to aggregate e.g. avg_spanned_min_width
|
122
|
+
#
|
123
|
+
def aggregate_cell_values(row_or_column, meth, aggregate)
|
124
|
+
values = {}
|
125
|
+
|
126
|
+
#calculate values for all cells that do not span accross multiple cells
|
127
|
+
#this ensures that we don't have a problem if the first line includes
|
128
|
+
#a cell that spans across multiple cells
|
129
|
+
@cells.each do |cell|
|
130
|
+
#don't take spanned cells
|
131
|
+
if cell.colspan == 1 and cell.class != Prawn::Table::Cell::SpanDummy
|
132
|
+
index = cell.send(row_or_column)
|
133
|
+
values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# if there are only colspanned or rowspanned cells in a table
|
138
|
+
spanned_width_needs_fixing = true
|
139
|
+
|
140
|
+
@cells.each do |cell|
|
141
|
+
index = cell.send(row_or_column)
|
142
|
+
if cell.colspan > 1
|
143
|
+
#special treatment if some but not all spanned indices in the values array have been calculated
|
144
|
+
#only applies to rows
|
145
|
+
values = fill_values_if_needed(values, cell, index, meth) if row_or_column == :column
|
146
|
+
#calculate current (old) return value before we do anything
|
147
|
+
old_sum = 0
|
148
|
+
cell.colspan.times { |i|
|
149
|
+
old_sum += values[index+i] unless values[index+i].nil?
|
150
|
+
}
|
151
|
+
|
152
|
+
#calculate future return value
|
153
|
+
new_sum = cell.send(meth) * cell.colspan
|
154
|
+
|
155
|
+
#due to float rounding errors we need to ignore a small difference in the new
|
156
|
+
#and the old sum the same had to be done in
|
157
|
+
#the column_width_calculator#natural_width
|
158
|
+
spanned_width_needs_fixing = ((new_sum - old_sum) > Prawn::FLOAT_PRECISION)
|
159
|
+
|
160
|
+
if spanned_width_needs_fixing
|
161
|
+
#not entirely sure why we need this line, but with it the tests pass
|
162
|
+
values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
|
163
|
+
#overwrite the old values with the new ones, but only if all entries existed
|
164
|
+
entries_exist = true
|
165
|
+
cell.colspan.times { |i| entries_exist = false if values[index+i].nil? }
|
166
|
+
cell.colspan.times { |i|
|
167
|
+
values[index+i] = cell.send(meth) if entries_exist
|
168
|
+
}
|
169
|
+
end
|
170
|
+
else
|
171
|
+
if spanned_width_needs_fixing && cell.class == Prawn::Table::Cell::SpanDummy
|
172
|
+
values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
return values.values.inject(0, &:+)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
data/manual/contents.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Generates the Prawn by example manual.
|
4
|
+
|
5
|
+
require_relative "example_helper"
|
6
|
+
|
7
|
+
Encoding.default_external = Encoding::UTF_8
|
8
|
+
|
9
|
+
Prawn::ManualBuilder::Example.generate("manual.pdf",
|
10
|
+
:skip_page_creation => true, :page_size => "FOLIO") do
|
11
|
+
|
12
|
+
load_package "table"
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# All of the previous styling options we've seen deal with all the table cells
|
4
|
+
# at once.
|
5
|
+
#
|
6
|
+
# With initializer blocks we may deal with specific cells.
|
7
|
+
# A block passed to one of the table methods (<code>Prawn::Table.new</code>,
|
8
|
+
# <code>Prawn::Document#table</code>, <code>Prawn::Document#make_table</code>)
|
9
|
+
# will be called after cell setup but before layout. This is a very flexible way
|
10
|
+
# to specify styling and layout constraints.
|
11
|
+
#
|
12
|
+
# Just like the <code>Prawn::Document.generate</code> method, the table
|
13
|
+
# initializer blocks may be used with and without a block argument.
|
14
|
+
#
|
15
|
+
# The table class has three methods that are handy within an initializer block:
|
16
|
+
# <code>cells</code>, <code>rows</code> and <code>columns</code>. All three
|
17
|
+
# return an instance of <code>Prawn::Table::Cells</code> which represents
|
18
|
+
# a selection of cells.
|
19
|
+
#
|
20
|
+
# <code>cells</code> return all the table cells, while <code>rows</code> and
|
21
|
+
# <code>columns</code> accept a number or a range as argument which returns a
|
22
|
+
# single row/column or a range of rows/columns respectively. (<code>rows</code>
|
23
|
+
# and <code>columns</code> are also aliased as <code>row</code> and
|
24
|
+
# <code>column</code>)
|
25
|
+
#
|
26
|
+
# The <code>Prawn::Table::Cells</code> class also defines <code>rows</code> and
|
27
|
+
# <code>columns</code> so they may be chained to narrow the selection of cells.
|
28
|
+
#
|
29
|
+
# All of the cell styling options we've seen on previous examples may be set as
|
30
|
+
# properties of the selection of cells.
|
31
|
+
#
|
32
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
33
|
+
%w[.. example_helper]))
|
34
|
+
|
35
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
36
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
37
|
+
data = [ ["Header", "A " * 5, "B"],
|
38
|
+
["Data row", "C", "D " * 5],
|
39
|
+
["Another data row", "E", "F"]]
|
40
|
+
|
41
|
+
table(data) do
|
42
|
+
cells.padding = 12
|
43
|
+
cells.borders = []
|
44
|
+
|
45
|
+
row(0).borders = [:bottom]
|
46
|
+
row(0).border_width = 2
|
47
|
+
row(0).font_style = :bold
|
48
|
+
|
49
|
+
columns(0..1).borders = [:right]
|
50
|
+
|
51
|
+
row(0).columns(0..1).borders = [:bottom, :right]
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# <code>Prawn::Table#initialize</code> takes a
|
4
|
+
# <code>:before_rendering_page</code> argument, to adjust the way an entire page
|
5
|
+
# of table cells is styled. This allows you to do things like draw a border
|
6
|
+
# around the entire table as displayed on a page.
|
7
|
+
#
|
8
|
+
# The callback is passed a Cells object that is numbered based on the order of
|
9
|
+
# the cells on the page (e.g., the first row on the page is
|
10
|
+
# <code>cells.row(0)</code>).
|
11
|
+
#
|
12
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
13
|
+
%w[.. example_helper]))
|
14
|
+
|
15
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
16
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
17
|
+
table([["foo", "bar", "baz"]] * 40) do |t|
|
18
|
+
t.cells.border_width = 1
|
19
|
+
t.before_rendering_page do |page|
|
20
|
+
page.row(0).border_top_width = 3
|
21
|
+
page.row(-1).border_bottom_width = 3
|
22
|
+
page.column(0).border_left_width = 3
|
23
|
+
page.column(-1).border_right_width = 3
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# The <code>border_lines</code> option accepts an array with the styles of the
|
4
|
+
# border sides. The default is <code>[:solid, :solid, :solid, :solid]</code>.
|
5
|
+
#
|
6
|
+
# <code>border_lines</code> must be set to an array.
|
7
|
+
#
|
8
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
9
|
+
%w[.. example_helper]))
|
10
|
+
|
11
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
12
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
13
|
+
data = [ ["Look at how the cell border lines can be mixed", "", ""],
|
14
|
+
["dotted top border", "", ""],
|
15
|
+
["solid right border", "", ""],
|
16
|
+
["dotted bottom border", "", ""],
|
17
|
+
["dashed left border", "", ""]
|
18
|
+
]
|
19
|
+
|
20
|
+
text "Cell :border_lines => [:dotted, :solid, :dotted, :dashed]"
|
21
|
+
|
22
|
+
table(data, :cell_style =>
|
23
|
+
{ :border_lines => [:dotted, :solid, :dotted, :dashed] })
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# The <code>borders</code> option accepts an array with the border sides that
|
4
|
+
# will be drawn. The default is <code>[:top, :bottom, :left, :right]</code>.
|
5
|
+
#
|
6
|
+
# <code>border_width</code> may be set with a numeric value.
|
7
|
+
#
|
8
|
+
# Both <code>border_color</code> and <code>background_color</code> accept an
|
9
|
+
# HTML like RGB color string ("FF0000")
|
10
|
+
#
|
11
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
12
|
+
%w[.. example_helper]))
|
13
|
+
|
14
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
15
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
16
|
+
data = [ ["Look at how the cells will look when styled", "", ""],
|
17
|
+
["They probably won't look the same", "", ""]
|
18
|
+
]
|
19
|
+
|
20
|
+
{ :borders => [:top, :left],
|
21
|
+
:border_width => 3,
|
22
|
+
:border_color => "FF0000"}.each do |property, value|
|
23
|
+
|
24
|
+
text "Cell #{property}: #{value.inspect}"
|
25
|
+
table(data, :cell_style => {property => value})
|
26
|
+
move_down 20
|
27
|
+
end
|
28
|
+
|
29
|
+
text "Cell background_color: FFFFCC"
|
30
|
+
table(data, :cell_style => {:background_color => "FFFFCC"})
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# To style all the table cells you can use the <code>:cell_style</code> option
|
4
|
+
# with the table methods. It accepts a hash with the cell style options.
|
5
|
+
#
|
6
|
+
# Some straightforward options are <code>width</code>, <code>height</code>,
|
7
|
+
# and <code>padding</code>. All three accept numeric values to set the property.
|
8
|
+
#
|
9
|
+
# <code>padding</code> also accepts a four number array that defines the padding
|
10
|
+
# in a CSS like syntax setting the top, right, bottom, left sequentially. The
|
11
|
+
# default is 5pt for all sides.
|
12
|
+
#
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
14
|
+
%w[.. example_helper]))
|
15
|
+
|
16
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
17
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
18
|
+
data = [ ["Look at how the cells will look when styled", "", ""],
|
19
|
+
["They probably won't look the same", "", ""]
|
20
|
+
]
|
21
|
+
|
22
|
+
{:width => 160, :height => 50, :padding => 12}.each do |property, value|
|
23
|
+
text "Cell's #{property}: #{value}"
|
24
|
+
table(data, :cell_style => {property => value})
|
25
|
+
move_down 20
|
26
|
+
end
|
27
|
+
|
28
|
+
text "Padding can also be set with an array: [0, 0, 0, 30]"
|
29
|
+
table(data, :cell_style => {:padding => [0, 0, 0, 30]})
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Text cells accept the following options: <code>align</code>,
|
4
|
+
# <code>font</code>, <code>font_style</code>, <code>inline_format</code>,
|
5
|
+
# <code>kerning</code>, <code>leading</code>, <code>min_font_size</code>,
|
6
|
+
# <code>overflow</code>, <code>rotate</code>, <code>rotate_around</code>,
|
7
|
+
# <code>single_line</code>, <code>size</code>, <code>text_color</code>,
|
8
|
+
# and <code>valign</code>.
|
9
|
+
#
|
10
|
+
# Most of these style options are direct translations from the text methods
|
11
|
+
# styling options.
|
12
|
+
#
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
14
|
+
%w[.. example_helper]))
|
15
|
+
|
16
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
17
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
18
|
+
data = [ ["Look at how the cells will look when styled", "", ""],
|
19
|
+
["They probably won't look the same", "", ""]
|
20
|
+
]
|
21
|
+
|
22
|
+
table data, :cell_style => { :font => "Times-Roman", :font_style => :italic }
|
23
|
+
move_down 20
|
24
|
+
|
25
|
+
table data, :cell_style => { :size => 18, :text_color => "346842" }
|
26
|
+
move_down 20
|
27
|
+
|
28
|
+
table [["Just <font size='18'>some</font> <b><i>inline</i></b>", "", ""],
|
29
|
+
["<color rgb='FF00FF'>styles</color> being applied here", "", ""]],
|
30
|
+
:cell_style => { :inline_format => true }
|
31
|
+
move_down 20
|
32
|
+
|
33
|
+
table [["1", "2", "3", "rotate"]], :cell_style => { :rotate => 30 }
|
34
|
+
move_down 20
|
35
|
+
|
36
|
+
table data, :cell_style => { :overflow => :shrink_to_fit, :min_font_size => 8,
|
37
|
+
:width => 60, :height => 30 }
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Prawn will make its best attempt to identify the best width for the columns.
|
4
|
+
# If the end result isn't good, we can override it with some styling.
|
5
|
+
#
|
6
|
+
# Individual column widths can be set with the <code>:column_widths</code>
|
7
|
+
# option. Just provide an array with the sequential width values for the columns
|
8
|
+
# or a hash were each key-value pair represents the column 0-based index and its
|
9
|
+
# width.
|
10
|
+
#
|
11
|
+
require File.expand_path(File.join(File.dirname(__FILE__),
|
12
|
+
%w[.. example_helper]))
|
13
|
+
|
14
|
+
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
15
|
+
Prawn::ManualBuilder::Example.generate(filename) do
|
16
|
+
data = [ ["this is not quite as long as the others",
|
17
|
+
"here we have a line that is long but with smaller words",
|
18
|
+
"this is so very looooooooooooooooooooooooooooooong"] ]
|
19
|
+
|
20
|
+
text "Prawn trying to guess the column widths"
|
21
|
+
table(data)
|
22
|
+
move_down 20
|
23
|
+
|
24
|
+
text "Manually setting all the column widths"
|
25
|
+
table(data, :column_widths => [100, 200, 240])
|
26
|
+
move_down 20
|
27
|
+
|
28
|
+
text "Setting only the last column width"
|
29
|
+
table(data, :column_widths => {2 => 240})
|
30
|
+
end
|