jerryvos-prawn-layout 0.2.0.3 → 0.2.0.4
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 +1 -1
- data/lib/prawn/layout/grid.rb +238 -0
- data/lib/prawn/layout/page.rb +116 -0
- data/lib/prawn/layout.rb +21 -0
- data/lib/prawn/table/cell.rb +287 -0
- data/lib/prawn/table.rb +515 -0
- data/spec/grid_spec.rb +61 -0
- data/spec/page_layout_spec.rb +41 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/table_spec.rb +400 -0
- metadata +10 -1
data/Rakefile
CHANGED
@@ -0,0 +1,238 @@
|
|
1
|
+
module Prawn
|
2
|
+
class Document
|
3
|
+
# Defines the grid system for a particular document. Takes the number of rows and columns and the
|
4
|
+
# width to use for the gutter as the keys :rows, :columns, :gutter
|
5
|
+
#
|
6
|
+
def define_grid(options = {})
|
7
|
+
@grid = Grid.new(self, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
# A method that can either be used to access a particular grid on the page or interogate the grid
|
11
|
+
# system directly.
|
12
|
+
#
|
13
|
+
# @pdf.grid # Get the Grid directly
|
14
|
+
# @pdf.grid([0,1]) # Get the box at [0,1]
|
15
|
+
# @pdf.grid([0,1], [1,2]) # Get a multi-box spanning from [0,1] to [1,2]
|
16
|
+
def grid(*args)
|
17
|
+
@boxes ||= {}
|
18
|
+
@boxes[args] ||= if args.empty?
|
19
|
+
@grid
|
20
|
+
else
|
21
|
+
g1, g2 = args
|
22
|
+
if(g1.class == Array && g2.class == Array &&
|
23
|
+
g1.length == 2 && g2.length == 2)
|
24
|
+
multi_box(single_box(*g1), single_box(*g2))
|
25
|
+
else
|
26
|
+
single_box(g1, g2)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# A Grid represents the entire grid system of a Page and calculates the column width and row height
|
32
|
+
# of the base box.
|
33
|
+
class Grid
|
34
|
+
attr_reader :pdf, :columns, :rows, :gutter
|
35
|
+
# :nodoc
|
36
|
+
def initialize(pdf, options = {})
|
37
|
+
Prawn.verify_options([:columns, :rows, :gutter], options)
|
38
|
+
|
39
|
+
@pdf = pdf
|
40
|
+
@columns = options[:columns]
|
41
|
+
@rows = options[:rows]
|
42
|
+
@gutter = options[:gutter].to_f
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the base width of boxes.
|
46
|
+
def column_width
|
47
|
+
@column_width ||= subdivide(pdf.bounds.width, columns)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Calculates the base height of boxes.
|
51
|
+
def row_height
|
52
|
+
@row_height ||= subdivide(pdf.bounds.height, rows)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Diagnostic tool to show all of the grids. Defaults to gray.
|
56
|
+
def show_all(color = "CCCCCC")
|
57
|
+
self.rows.times do |i|
|
58
|
+
self.columns.times do |j|
|
59
|
+
pdf.grid(i,j).show(color)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def subdivide(total, num)
|
66
|
+
(total.to_f - (gutter * (num - 1).to_f)) / num.to_f
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# A Box is a class that represents a bounded area of a page. A Grid object has methods that allow
|
71
|
+
# easy access to the coordinates of its corners, which can be plugged into most existing prawn
|
72
|
+
# methods.
|
73
|
+
#
|
74
|
+
class Box
|
75
|
+
attr_reader :pdf
|
76
|
+
|
77
|
+
def initialize(pdf, i, j)
|
78
|
+
@pdf = pdf
|
79
|
+
@i = i
|
80
|
+
@j = j
|
81
|
+
end
|
82
|
+
|
83
|
+
# Mostly diagnostic method that outputs the name of a box as col_num, row_num
|
84
|
+
def name
|
85
|
+
"#{@i.to_s},#{@j.to_s}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# :nodoc
|
89
|
+
def total_height
|
90
|
+
pdf.bounds.height.to_f
|
91
|
+
end
|
92
|
+
|
93
|
+
# Width of a box
|
94
|
+
def width
|
95
|
+
grid.column_width.to_f
|
96
|
+
end
|
97
|
+
|
98
|
+
# Height of a box
|
99
|
+
def height
|
100
|
+
grid.row_height.to_f
|
101
|
+
end
|
102
|
+
|
103
|
+
# Width of the gutter
|
104
|
+
def gutter
|
105
|
+
grid.gutter.to_f
|
106
|
+
end
|
107
|
+
|
108
|
+
# x-coordinate of left side
|
109
|
+
def left
|
110
|
+
@left ||= (width + gutter) * @j.to_f
|
111
|
+
end
|
112
|
+
|
113
|
+
# x-coordinate of right side
|
114
|
+
def right
|
115
|
+
@right ||= left + width
|
116
|
+
end
|
117
|
+
|
118
|
+
# y-coordinate of the top
|
119
|
+
def top
|
120
|
+
@top ||= total_height - ((height + gutter) * @i.to_f)
|
121
|
+
end
|
122
|
+
|
123
|
+
# y-coordinate of the bottom
|
124
|
+
def bottom
|
125
|
+
@bottom ||= top - height
|
126
|
+
end
|
127
|
+
|
128
|
+
# x,y coordinates of top left corner
|
129
|
+
def top_left
|
130
|
+
[left, top]
|
131
|
+
end
|
132
|
+
|
133
|
+
# x,y coordinates of top right corner
|
134
|
+
def top_right
|
135
|
+
[right, top]
|
136
|
+
end
|
137
|
+
|
138
|
+
# x,y coordinates of bottom left corner
|
139
|
+
def bottom_left
|
140
|
+
[left, bottom]
|
141
|
+
end
|
142
|
+
|
143
|
+
# x,y coordinates of bottom right corner
|
144
|
+
def bottom_right
|
145
|
+
[right, bottom]
|
146
|
+
end
|
147
|
+
|
148
|
+
# Creates a standard bounding box based on the grid box.
|
149
|
+
def bounding_box(&blk)
|
150
|
+
pdf.bounding_box(top_left, :width => width, :height => height, &blk)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Diagnostic method
|
154
|
+
def show(grid_color = "CCCCCC")
|
155
|
+
self.bounding_box do
|
156
|
+
pdf.stroke_color = grid_color
|
157
|
+
pdf.text self.name
|
158
|
+
pdf.stroke_bounds
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
def grid
|
164
|
+
pdf.grid
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# A MultiBox is specified by 2 Boxes and spans the areas between.
|
169
|
+
class MultiBox < Box
|
170
|
+
def initialize(pdf, b1, b2)
|
171
|
+
@pdf = pdf
|
172
|
+
@bs = [b1, b2]
|
173
|
+
end
|
174
|
+
|
175
|
+
def name
|
176
|
+
@bs.map {|b| b.name}.join(":")
|
177
|
+
end
|
178
|
+
|
179
|
+
def total_height
|
180
|
+
@bs[0].total_height
|
181
|
+
end
|
182
|
+
|
183
|
+
def width
|
184
|
+
right_box.right - left_box.left
|
185
|
+
end
|
186
|
+
|
187
|
+
def height
|
188
|
+
top_box.top - bottom_box.bottom
|
189
|
+
end
|
190
|
+
|
191
|
+
def gutter
|
192
|
+
@bs[0].gutter
|
193
|
+
end
|
194
|
+
|
195
|
+
def left
|
196
|
+
left_box.left
|
197
|
+
end
|
198
|
+
|
199
|
+
def right
|
200
|
+
right_box.right
|
201
|
+
end
|
202
|
+
|
203
|
+
def top
|
204
|
+
top_box.top
|
205
|
+
end
|
206
|
+
|
207
|
+
def bottom
|
208
|
+
bottom_box.bottom
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
def left_box
|
213
|
+
@left_box ||= @bs.min {|a,b| a.left <=> b.left}
|
214
|
+
end
|
215
|
+
|
216
|
+
def right_box
|
217
|
+
@right_box ||= @bs.max {|a,b| a.right <=> b.right}
|
218
|
+
end
|
219
|
+
|
220
|
+
def top_box
|
221
|
+
@top_box ||= @bs.max {|a,b| a.top <=> b.top}
|
222
|
+
end
|
223
|
+
|
224
|
+
def bottom_box
|
225
|
+
@bottom_box ||= @bs.min {|a,b| a.bottom <=> b.bottom}
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
def single_box(i, j)
|
231
|
+
Box.new(self, i, j)
|
232
|
+
end
|
233
|
+
|
234
|
+
def multi_box(b1, b2)
|
235
|
+
MultiBox.new(self, b1, b2)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# layout/page.rb : Provides helpers for page layout
|
4
|
+
#
|
5
|
+
# Copyright January 2009, Gregory Brown. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
module Prawn
|
10
|
+
class Document
|
11
|
+
# A LazyBoundingBox is simply a BoundingBox with an action tied to it to be
|
12
|
+
# executed later. The lazy_bounding_box method takes the same arguments as
|
13
|
+
# bounding_box, but returns a LazyBoundingBox object instead of executing
|
14
|
+
# the code block directly.
|
15
|
+
#
|
16
|
+
# You can then call LazyBoundingBox#draw at any time (or multiple times if
|
17
|
+
# you wish), and the contents of the block will then be run. This can be
|
18
|
+
# useful for assembling repeating page elements or reusable components.
|
19
|
+
#
|
20
|
+
# file = "lazy_bounding_boxes.pdf"
|
21
|
+
# Prawn::Document.generate(file, :skip_page_creation => true) do
|
22
|
+
# point = [bounds.right-50, bounds.bottom + 25]
|
23
|
+
# page_counter = lazy_bounding_box(point, :width => 50) do
|
24
|
+
# text "Page: #{page_count}"
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# 10.times do
|
28
|
+
# start_new_page
|
29
|
+
# text "Some text"
|
30
|
+
# page_counter.draw
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
def lazy_bounding_box(*args,&block)
|
35
|
+
translate!(args[0])
|
36
|
+
box = LazyBoundingBox.new(self,*args)
|
37
|
+
box.action(&block)
|
38
|
+
return box
|
39
|
+
end
|
40
|
+
|
41
|
+
# A bounding box with the same dimensions of its parents, minus a margin
|
42
|
+
# on all sides
|
43
|
+
#
|
44
|
+
def padded_box(margin, &block)
|
45
|
+
bounding_box [bounds.left + margin, bounds.top - margin],
|
46
|
+
:width => bounds.width - (margin * 2),
|
47
|
+
:height => bounds.height - (margin * 2), &block
|
48
|
+
end
|
49
|
+
|
50
|
+
# A header is a LazyBoundingBox drawn relative to the margins that can be
|
51
|
+
# repeated on every page of the document.
|
52
|
+
#
|
53
|
+
# Unless <tt>:width</tt> or <tt>:height</tt> are specified, the margin_box
|
54
|
+
# width and height are used.
|
55
|
+
#
|
56
|
+
# header margin_box.top_left do
|
57
|
+
# text "Here's My Fancy Header", :size => 25, :align => :center
|
58
|
+
# stroke_horizontal_rule
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
def header(top_left,options={},&block)
|
62
|
+
@header = repeating_page_element(top_left,options,&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
# A footer is a LazyBoundingBox drawn relative to the margins that can be
|
66
|
+
# repeated on every page of the document.
|
67
|
+
#
|
68
|
+
# Unless <tt>:width</tt> or <tt>:height</tt> are specified, the margin_box
|
69
|
+
# width and height are used.
|
70
|
+
#
|
71
|
+
# footer [margin_box.left, margin_box.bottom + 25] do
|
72
|
+
# stroke_horizontal_rule
|
73
|
+
# text "And here's a sexy footer", :size => 16
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
def footer(top_left,options={},&block)
|
77
|
+
@footer = repeating_page_element(top_left,options,&block)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def repeating_page_element(top_left,options={},&block)
|
83
|
+
r = LazyBoundingBox.new(self, translate(top_left),
|
84
|
+
:width => options[:width] || margin_box.width,
|
85
|
+
:height => options[:height] || margin_box.height )
|
86
|
+
r.action(&block)
|
87
|
+
return r
|
88
|
+
end
|
89
|
+
|
90
|
+
class LazyBoundingBox < BoundingBox
|
91
|
+
|
92
|
+
# Defines the block to be executed by LazyBoundingBox#draw.
|
93
|
+
# Usually, this will be used via a higher level interface.
|
94
|
+
# See the documentation for Document#lazy_bounding_box, Document#header,
|
95
|
+
# and Document#footer
|
96
|
+
#
|
97
|
+
def action(&block)
|
98
|
+
@action = block
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sets Document#bounds to use the LazyBoundingBox for its bounds,
|
102
|
+
# runs the block specified by LazyBoundingBox#action,
|
103
|
+
# and then restores the original bounds of the document.
|
104
|
+
#
|
105
|
+
def draw
|
106
|
+
@parent.mask(:y) do
|
107
|
+
parent_box = @parent.bounds
|
108
|
+
@parent.bounds = self
|
109
|
+
@parent.y = absolute_top
|
110
|
+
@action.call
|
111
|
+
@parent.bounds = parent_box
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/prawn/layout.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "prawn/table"
|
2
|
+
require "prawn/layout/page"
|
3
|
+
require 'prawn/layout/grid'
|
4
|
+
|
5
|
+
module Prawn
|
6
|
+
|
7
|
+
module Errors
|
8
|
+
|
9
|
+
# This error is raised when table data is malformed
|
10
|
+
#
|
11
|
+
InvalidTableData = Class.new(StandardError)
|
12
|
+
|
13
|
+
# This error is raised when an empty or nil table is rendered
|
14
|
+
#
|
15
|
+
EmptyTable = Class.new(StandardError)
|
16
|
+
end
|
17
|
+
|
18
|
+
module Layout
|
19
|
+
VERSION = "0.2.0.4"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# cell.rb : Table support functions
|
4
|
+
#
|
5
|
+
# Copyright June 2008, Gregory Brown. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
module Prawn
|
9
|
+
|
10
|
+
class Document
|
11
|
+
# Builds and renders a Table::Cell. A cell is essentially a
|
12
|
+
# special-purpose bounding box designed for flowing text within a bordered
|
13
|
+
# area. For available options, see Table::Cell#new.
|
14
|
+
#
|
15
|
+
# Prawn::Document.generate("cell.pdf") do
|
16
|
+
# cell [100,500],
|
17
|
+
# :width => 200,
|
18
|
+
# :text => "The rain in Spain falls mainly on the plains"
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
def cell(point, options={})
|
22
|
+
Prawn::Table::Cell.new(
|
23
|
+
options.merge(:document => self, :point => point)).draw
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Table
|
28
|
+
# A cell is a special-purpose bounding box designed to flow text within a
|
29
|
+
# bordered area. This is used by Prawn's Document::Table implementation but
|
30
|
+
# can also be used standalone for drawing text boxes via Document#cell
|
31
|
+
#
|
32
|
+
class Cell
|
33
|
+
|
34
|
+
# Creates a new cell object. Generally used indirectly via Document#cell
|
35
|
+
#
|
36
|
+
# Of the available options listed below, <tt>:point</tt>, <tt>:width</tt>,
|
37
|
+
# and <tt>:text</tt> must be provided. If you are not using the
|
38
|
+
# Document#cell shortcut, the <tt>:document</tt> must also be provided.
|
39
|
+
#
|
40
|
+
# <tt>:point</tt>:: Absolute [x,y] coordinate of the top-left corner of the cell.
|
41
|
+
# <tt>:document</tt>:: The Prawn::Document object to render on.
|
42
|
+
# <tt>:text</tt>:: The text to be flowed within the cell
|
43
|
+
# <tt>:text_color</tt>:: The color of the text to be displayed
|
44
|
+
# <tt>:width</tt>:: The width in PDF points of the cell.
|
45
|
+
# <tt>:height</tt>:: The height in PDF points of the cell.
|
46
|
+
# <tt>:horizontal_padding</tt>:: The horizontal padding in PDF points
|
47
|
+
# <tt>:vertical_padding</tt>:: The vertical padding in PDF points
|
48
|
+
# <tt>:padding</tt>:: Overrides both horizontal and vertical padding
|
49
|
+
# <tt>:align</tt>:: One of <tt>:left</tt>, <tt>:right</tt>, <tt>:center</tt>
|
50
|
+
# <tt>:borders</tt>:: An array of sides which should have a border. Any of <tt>:top</tt>, <tt>:left</tt>, <tt>:right</tt>, <tt>:bottom</tt>
|
51
|
+
# <tt>:border_width</tt>:: The border line width. Defaults to 1.
|
52
|
+
# <tt>:border_style</tt>:: One of <tt>:all</tt>, <tt>:no_top</tt>, <tt>:no_bottom</tt>, <tt>:sides</tt>, <tt>:none</tt>, <tt>:bottom_only</tt>. Defaults to :all.
|
53
|
+
# <tt>:border_color</tt>:: The color of the cell border.
|
54
|
+
# <tt>:font_size</tt>:: The font size for the cell text.
|
55
|
+
# <tt>:font_style</tt>:: The font style for the cell text.
|
56
|
+
#
|
57
|
+
def initialize(options={})
|
58
|
+
@point = options[:point]
|
59
|
+
@document = options[:document]
|
60
|
+
@text = options[:text].to_s
|
61
|
+
@text_color = options[:text_color]
|
62
|
+
@width = options[:width]
|
63
|
+
@height = options[:height]
|
64
|
+
@borders = options[:borders]
|
65
|
+
@border_width = options[:border_width] || 1
|
66
|
+
@border_style = options[:border_style] || :all
|
67
|
+
@border_color = options[:border_color]
|
68
|
+
@background_color = options[:background_color]
|
69
|
+
@align = options[:align] || :left
|
70
|
+
@font_size = options[:font_size]
|
71
|
+
@font_style = options[:font_style]
|
72
|
+
|
73
|
+
@horizontal_padding = options[:horizontal_padding] || 0
|
74
|
+
@vertical_padding = options[:vertical_padding] || 0
|
75
|
+
|
76
|
+
if options[:padding]
|
77
|
+
@horizontal_padding = @vertical_padding = options[:padding]
|
78
|
+
end
|
79
|
+
|
80
|
+
@rowspan = options[:rowspan] || 1
|
81
|
+
@colspan = options[:colspan] || 1
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_accessor :point, :border_style, :border_width, :background_color,
|
85
|
+
:document, :horizontal_padding, :vertical_padding, :align,
|
86
|
+
:borders, :text_color, :border_color, :font_size, :font_style,
|
87
|
+
:rowspan, :colspan
|
88
|
+
|
89
|
+
attr_writer :height, :width #:nodoc:
|
90
|
+
|
91
|
+
# Returns the cell's text as a string.
|
92
|
+
#
|
93
|
+
def to_s
|
94
|
+
@text
|
95
|
+
end
|
96
|
+
|
97
|
+
# The width of the text area excluding the horizonal padding
|
98
|
+
#
|
99
|
+
def text_area_width
|
100
|
+
width - 2*@horizontal_padding
|
101
|
+
end
|
102
|
+
|
103
|
+
# The width of the cell in PDF points
|
104
|
+
#
|
105
|
+
def width
|
106
|
+
@width || (@document.width_of(@text, :size => @font_size)) + 2*@horizontal_padding
|
107
|
+
end
|
108
|
+
|
109
|
+
# The height of the cell in PDF points
|
110
|
+
#
|
111
|
+
def height
|
112
|
+
@height || text_area_height + 2*@vertical_padding
|
113
|
+
end
|
114
|
+
|
115
|
+
# The height of the text area excluding the vertical padding
|
116
|
+
#
|
117
|
+
def text_area_height
|
118
|
+
text_height = 0
|
119
|
+
if @font_size
|
120
|
+
@document.font_size(@font_size) do
|
121
|
+
text_height = @document.height_of(@text, text_area_width)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
text_height = @document.height_of(@text, text_area_width)
|
125
|
+
end
|
126
|
+
text_height
|
127
|
+
end
|
128
|
+
|
129
|
+
# Draws the cell onto the PDF document
|
130
|
+
#
|
131
|
+
def draw
|
132
|
+
rel_point = @point
|
133
|
+
|
134
|
+
if @background_color
|
135
|
+
@document.mask(:fill_color) do
|
136
|
+
@document.fill_color @background_color
|
137
|
+
h = borders.include?(:bottom) ?
|
138
|
+
height - border_width : height + border_width / 2.0
|
139
|
+
@document.fill_rectangle [rel_point[0] + border_width / 2.0,
|
140
|
+
rel_point[1] - border_width / 2.0 ],
|
141
|
+
width - border_width, h
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if @border_width > 0
|
146
|
+
@document.mask(:line_width) do
|
147
|
+
@document.line_width = @border_width
|
148
|
+
|
149
|
+
@document.mask(:stroke_color) do
|
150
|
+
@document.stroke_color @border_color if @border_color
|
151
|
+
|
152
|
+
if borders.include?(:left)
|
153
|
+
@document.stroke_line [rel_point[0], rel_point[1] + (@border_width / 2.0)],
|
154
|
+
[rel_point[0], rel_point[1] - height - @border_width / 2.0 ]
|
155
|
+
end
|
156
|
+
|
157
|
+
if borders.include?(:right)
|
158
|
+
@document.stroke_line(
|
159
|
+
[rel_point[0] + width, rel_point[1] + (@border_width / 2.0)],
|
160
|
+
[rel_point[0] + width, rel_point[1] - height - @border_width / 2.0] )
|
161
|
+
end
|
162
|
+
|
163
|
+
if borders.include?(:top)
|
164
|
+
@document.stroke_line(
|
165
|
+
[ rel_point[0] + @border_width / 2.0, rel_point[1] ],
|
166
|
+
[ rel_point[0] - @border_width / 2.0 + width, rel_point[1] ])
|
167
|
+
end
|
168
|
+
|
169
|
+
if borders.include?(:bottom)
|
170
|
+
@document.stroke_line [rel_point[0], rel_point[1] - height ],
|
171
|
+
[rel_point[0] + width, rel_point[1] - height]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
borders
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
@document.bounding_box( [@point[0] + @horizontal_padding,
|
182
|
+
@point[1] - @vertical_padding],
|
183
|
+
:width => text_area_width,
|
184
|
+
:height => height - @vertical_padding) do
|
185
|
+
@document.move_down((@document.font.line_gap - @document.font.descender)/2)
|
186
|
+
|
187
|
+
options = {:align => @align, :final_gap => false}
|
188
|
+
|
189
|
+
options[:size] = @font_size if @font_size
|
190
|
+
options[:style] = @font_style if @font_style
|
191
|
+
|
192
|
+
@document.mask(:fill_color) do
|
193
|
+
@document.fill_color @text_color if @text_color
|
194
|
+
@document.text @text, options
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def borders
|
202
|
+
@borders ||= case @border_style
|
203
|
+
when :all
|
204
|
+
[:top,:left,:right,:bottom]
|
205
|
+
when :sides
|
206
|
+
[:left,:right]
|
207
|
+
when :no_top
|
208
|
+
[:left,:right,:bottom]
|
209
|
+
when :no_bottom
|
210
|
+
[:left,:right,:top]
|
211
|
+
when :bottom_only
|
212
|
+
[:bottom]
|
213
|
+
when :none
|
214
|
+
[]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
class CellFake < Cell #:nodoc:
|
221
|
+
def height
|
222
|
+
0
|
223
|
+
end
|
224
|
+
|
225
|
+
def draw
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class CellBlock #:nodoc:
|
230
|
+
|
231
|
+
# Not sure if this class is something I want to expose in the public API.
|
232
|
+
|
233
|
+
def initialize(document)
|
234
|
+
@document = document
|
235
|
+
@cells = []
|
236
|
+
@width = 0
|
237
|
+
@height = 0
|
238
|
+
end
|
239
|
+
|
240
|
+
attr_reader :width, :height, :cells
|
241
|
+
attr_accessor :background_color, :text_color, :border_color
|
242
|
+
|
243
|
+
def <<(cell)
|
244
|
+
@cells << cell
|
245
|
+
@height = cell.height if cell.height > @height
|
246
|
+
@width += cell.width
|
247
|
+
self
|
248
|
+
end
|
249
|
+
|
250
|
+
def draw
|
251
|
+
y = @document.y
|
252
|
+
x = @document.bounds.absolute_left
|
253
|
+
|
254
|
+
@cells.each do |e|
|
255
|
+
e.point = [x - @document.bounds.absolute_left,
|
256
|
+
y - @document.bounds.absolute_bottom]
|
257
|
+
e.height ||= @height
|
258
|
+
e.background_color ||= @background_color
|
259
|
+
e.text_color ||= @text_color
|
260
|
+
e.border_color ||= @border_color
|
261
|
+
e.draw
|
262
|
+
x += e.width
|
263
|
+
end
|
264
|
+
|
265
|
+
@document.y = y - @height
|
266
|
+
end
|
267
|
+
|
268
|
+
def border_width
|
269
|
+
@cells[0].border_width
|
270
|
+
end
|
271
|
+
|
272
|
+
def border_style=(s)
|
273
|
+
@cells.each { |e| e.border_style = s }
|
274
|
+
end
|
275
|
+
|
276
|
+
def align=(align)
|
277
|
+
@cells.each { |e| e.align = align }
|
278
|
+
end
|
279
|
+
|
280
|
+
def border_style
|
281
|
+
@cells[0].border_style
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|