/
column_box.rb
142 lines (120 loc) · 4 KB
/
column_box.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# encoding: utf-8
#
# column_box.rb: Extends BoundingBox to allow for columns of text
#
# Author Paul Ostazeski.
#
# This is free software. Please see the LICENSE and COPYING files for details.
require_relative "bounding_box"
module Prawn
class Document
# @group Experimental API
# A column box is a bounding box with the additional property that when
# text flows past the bottom, it will wrap first to another column on the
# same page, and only flow to the next page when all the columns are
# filled.
#
# column_box accepts the same parameters as bounding_box, as well as the
# number of :columns and a :spacer (in points) between columns. If resetting
# the top margin is desired on a new page (e.g. to allow for initial page
# wide column titles) the option :reflow_margins => true can be set.
#
# Defaults are :columns = 3, :spacer = font_size, and
# :reflow_margins => false
#
# Under PDF::Writer, "spacer" was known as "gutter"
#
def column_box(*args, &block)
init_column_box(block) do |parent_box|
map_to_absolute!(args[0])
@bounding_box = ColumnBox.new(self, parent_box, *args)
end
end
private
def init_column_box(user_block, options = {}, &init_block)
parent_box = @bounding_box
init_block.call(parent_box)
self.y = @bounding_box.absolute_top
user_block.call
self.y = @bounding_box.absolute_bottom unless options[:hold_position]
@bounding_box = parent_box
end
# Implements the necessary functionality to allow Document#column_box to
# work.
#
class ColumnBox < BoundingBox
def initialize(document, parent, point, options = {}) #:nodoc:
super
@columns = options[:columns] || 3
@spacer = options[:spacer] || @document.font_size
@current_column = 0
@reflow_margins = options[:reflow_margins]
end
# The column width, not the width of the whole box,
# before left and/or right padding
def bare_column_width
(@width - @spacer * (@columns - 1)) / @columns
end
# The column width after padding.
# Used to calculate how long a line of text can be.
#
def width
bare_column_width - (@total_left_padding + @total_right_padding)
end
# Column width including the spacer.
#
def width_of_column
bare_column_width + @spacer
end
# x coordinate of the left edge of the current column
#
def left_side
absolute_left + (width_of_column * @current_column)
end
# Relative position of the left edge of the current column
#
def left
width_of_column * @current_column
end
# x co-orordinate of the right edge of the current column
#
def right_side
columns_from_right = @columns - (1 + @current_column)
absolute_right - (width_of_column * columns_from_right)
end
# Relative position of the right edge of the current column.
#
def right
left + width
end
# Moves to the next column or starts a new page if currently positioned at
# the rightmost column.
def move_past_bottom
@current_column = (@current_column + 1) % @columns
@document.y = @y
if 0 == @current_column
if @reflow_margins
@y = @parent.absolute_top
end
@document.start_new_page
end
end
# Override the padding functions so as not to split the padding amount
# between all columns on the page.
def add_left_padding(left_padding)
@total_left_padding += left_padding
@x += left_padding
end
def subtract_left_padding(left_padding)
@total_left_padding -= left_padding
@x -= left_padding
end
def add_right_padding(right_padding)
@total_right_padding += right_padding
end
def subtract_right_padding(right_padding)
@total_right_padding -= right_padding
end
end
end
end