class HexaPDF::Layout::ColumnBox
arguments are ignored.
frame into account. This also means that the available_width
and available_height
If this is set to :flow, the frames created for the columns will take the shape of the
Style#position::
The following style properties are used (additionally to those used by the parent class):
each column.
box. This means they are around all columns and their contents and are not used separately for
If the column box has padding and/or borders specified, they are handled like with any other
columns can be made equally high.
modified. Additionally, the contents can either fill the columns one after the other or the
The number and width of the columns as well as the size of the gap between the columns can be
A ColumnBox arranges boxes in one or more columns.
def calculate_columns(width)
width.
Calculates the x-coordinates and widths of all columns based on the given total available
def calculate_columns(width) number_of_columns = @columns.size gaps = @gaps.cycle.take(number_of_columns - 1) fixed_width, variable_width = @columns.partition(&:positive?).map {|c| c.sum(&:abs) } rest_width = width - fixed_width - gaps.sum return [] if rest_width <= 0 variable_width_unit = rest_width / variable_width.to_f position = 0 @columns.map.with_index do |column, index| result = if column > 0 [position, column] else [position, column.abs * variable_width_unit] end position += result[1] + (gaps[index] || 0) result end end
def draw_content(canvas, x, y)
def draw_content(canvas, x, y) if style.position != :flow && (x != @fit_x || y != @fit_y) canvas.translate(x - @fit_x, y - @fit_y) do @box_fitter.fit_results.each {|result| result.draw(canvas) } end else @box_fitter.fit_results.each {|result| result.draw(canvas) } end end
def empty?
def empty? super && (!@box_fitter || @box_fitter.fit_results.empty?) end
def fit_content(_available_width, _available_height, frame)
Fits the column box into the current region of the frame.
def fit_content(_available_width, _available_height, frame) initial_fit_successful = (@equal_height && @columns.size > 1 ? nil : false) tries = 0 width = @width - reserved_width height = @height - reserved_height columns = calculate_columns(width) return if columns.empty? left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left top = 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.child_frame(column_left, column_bottom, column_width, height, shape: shape, box: self) @box_fitter << column_frame end children.each {|box| @box_fitter.fit(box) } fit_successful = @box_fitter.success? 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 update_content_width { columns[-1].sum } update_content_height { @box_fitter.content_heights.max } if @box_fitter.success? fit_result.success! elsif !@box_fitter.fit_results.empty? fit_result.overflow! end end
def initialize(children: [], columns: 2, gaps: 36, equal_height: true, **kwargs)
If +true+, the #fit method tries to balance the columns in terms of their height.
+equal_height+::
definition (see #gap for details).
Can either be a simply integer specifying the width between two columns or a full gap
+gaps+::
(see #columns for details).
Can either simply integer specify the number of columns or be a full column definition
+columns+::
Creates a new ColumnBox object for the given child boxes in +children+.
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
def split_content
def split_content box = create_split_box box.instance_variable_set(:@children, @box_fitter.remaining_boxes) [self, box] end
def supports_position_flow?
def supports_position_flow? true end