Class: HexaPDF::Layout::ColumnBox
- Defined in:
- lib/hexapdf/layout/column_box.rb
Overview
A ColumnBox arranges boxes in one or more columns.
The number and width of the columns as well as the size of the gap between the columns can be modified. Additionally, the contents can either fill the columns one after the other or the columns can be made equally high.
If the column box has padding and/or borders specified, they are handled like with any other box. This means they are around all columns and their contents and are not used separately for each column.
The following style properties are used (additionally to those used by the parent class):
- Style#position
-
If this is set to :flow, the frames created for the columns will take the shape of the frame into account. This also means that the
available_width
andavailable_height
arguments are ignored.
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
The child boxes of this ColumnBox.
-
#columns ⇒ Object
readonly
The columns definition.
-
#equal_height ⇒ Object
readonly
Determines whether the columns should all be equally high or not.
-
#gaps ⇒ Object
readonly
The size of the gaps between the columns.
Attributes inherited from Box
#height, #properties, #style, #width
Instance Method Summary collapse
-
#empty? ⇒ Boolean
Returns
true
if no box was fitted into the columns. -
#fit(available_width, available_height, frame) ⇒ Object
Fits the column box into the current region of the frame.
-
#initialize(children: [], columns: 2, gaps: 36, equal_height: true, **kwargs) ⇒ ColumnBox
constructor
Creates a new ColumnBox object for the given child boxes in
children
. -
#supports_position_flow? ⇒ Boolean
Returns
true
as the ‘position’ style property value :flow is supported.
Methods inherited from Box
#content_height, #content_width, create, #draw, #split, #split_box?
Constructor Details
#initialize(children: [], columns: 2, gaps: 36, equal_height: true, **kwargs) ⇒ ColumnBox
Creates a new ColumnBox object for the given child boxes in children
.
columns
-
Can either simply integer specify the number of columns or be a full column definition (see #columns for details).
gaps
-
Can either be a simply integer specifying the width between two columns or a full gap definition (see #gap for details).
equal_height
-
If
true
, the #fit method tries to balance the columns in terms of their height. Otherwise the columns are filled from the left.
123 124 125 126 127 128 129 |
# File 'lib/hexapdf/layout/column_box.rb', line 123 def initialize(children: [], columns: 2, gaps: 36, equal_height: true, **kwargs) super(**kwargs) @children = children @columns = (columns.kind_of?(Array) ? columns : [-1] * columns) @gaps = (gaps.kind_of?(Array) ? gaps : [gaps]) @equal_height = equal_height end |
Instance Attribute Details
#children ⇒ Object (readonly)
The child boxes of this ColumnBox. They need to be finalized before #fit is called.
61 62 63 |
# File 'lib/hexapdf/layout/column_box.rb', line 61 def children @children end |
#columns ⇒ Object (readonly)
The columns definition.
If the value is an array, it needs to contain the widths of the columns. The size of the array determines the number of columns. Otherwise, if the value is an integer, the value defines the number of equally sized columns, i.e. a value of N
is equal to [-1]*N.
If a negative integer is used for the width, the column is auto-sized. Such columns split the remaining width (after substracting the widths of the fixed columns) proportionally among them. For example, if the definition is [-1, -2, -2], the first column is a fifth of the width and the other columns are each two fifth of the width.
Examples:
#>pdf-composer
composer.box(:column, columns: 2, gaps: 10,
children: [composer.document.layout.lorem_ipsum_box])
#>pdf-composer
composer.box(:column, columns: [50, -2, -1], gaps: [10, 5],
children: [composer.document.layout.lorem_ipsum_box])
85 86 87 |
# File 'lib/hexapdf/layout/column_box.rb', line 85 def columns @columns end |
#equal_height ⇒ Object (readonly)
Determines whether the columns should all be equally high or not.
Examples:
#>pdf-composer
composer.box(:column, children: [composer.document.layout.lorem_ipsum_box])
#>pdf-composer
composer.box(:column, equal_height: false,
children: [composer.document.layout.lorem_ipsum_box])
107 108 109 |
# File 'lib/hexapdf/layout/column_box.rb', line 107 def equal_height @equal_height end |
#gaps ⇒ Object (readonly)
The size of the gaps between the columns.
This is an array containing the width of the gaps. If there are more gaps than numbers in the array, the array is cycled.
Examples: see #columns
93 94 95 |
# File 'lib/hexapdf/layout/column_box.rb', line 93 def gaps @gaps end |
Instance Method Details
#empty? ⇒ Boolean
Returns true
if no box was fitted into the columns.
137 138 139 |
# File 'lib/hexapdf/layout/column_box.rb', line 137 def empty? super && (!@box_fitter || @box_fitter.fit_results.empty?) end |
#fit(available_width, available_height, frame) ⇒ Object
Fits the column box into the current region of the frame.
If the style property ‘position’ is set to :flow, the columns might not be rectangles but arbitrary (sets of) polygons since the frames shape is taken into account.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/hexapdf/layout/column_box.rb', line 145 def fit(available_width, available_height, frame) return false if @initial_height > available_height || @initial_width > available_width initial_fit_successful = (@equal_height && @columns.size > 1 ? nil : false) tries = 0 @width = if style.position == :flow (@initial_width > 0 ? @initial_width : frame.width) - reserved_width else (@initial_width > 0 ? @initial_width : available_width) - reserved_width end height = if style.position == :flow (@initial_height > 0 ? @initial_height : frame.height) - reserved_height else (@initial_height > 0 ? @initial_height : available_height) - reserved_height end columns = calculate_columns(@width) return false if columns.empty? left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left top = (style.position == :flow ? frame.bottom + frame.height : frame.y) - reserved_height_top successful_height = height unsuccessful_height = 0 while true @box_fitter = BoxFitter.new columns.each do |col_x, column_width| column_left = left + col_x column_bottom = top - height if style.position == :flow rect = Geom2D::Polygon([column_left, column_bottom], [column_left + column_width, column_bottom], [column_left + column_width, column_bottom + height], [column_left, column_bottom + height]) shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, rect, :intersection) end column_frame = Frame.new(column_left, column_bottom, column_width, height, shape: shape, context: frame.context) @box_fitter << column_frame end children.each {|box| @box_fitter.fit(box) } fit_successful = @box_fitter.fit_successful? initial_fit_successful = fit_successful if initial_fit_successful.nil? if fit_successful successful_height = height if successful_height > height elsif unsuccessful_height < height unsuccessful_height = height end break if !initial_fit_successful || tries > 40 || (fit_successful && successful_height - unsuccessful_height < 10) height = if successful_height - unsuccessful_height <= 5 successful_height else (successful_height + unsuccessful_height) / 2.0 end tries += 1 end @width = columns[-1].sum + reserved_width @height = (@initial_height > 0 ? @initial_height : @box_fitter.content_heights.max + reserved_height) @draw_pos_x = frame.x + reserved_width_left @draw_pos_y = frame.y - @height + reserved_height_bottom @box_fitter.fit_successful? end |
#supports_position_flow? ⇒ Boolean
Returns true
as the ‘position’ style property value :flow is supported.
132 133 134 |
# File 'lib/hexapdf/layout/column_box.rb', line 132 def supports_position_flow? true end |