class HexaPDF::Layout::TableBox


A TableBox allows placing boxes in a table.

A table box instance can be fit into a rectangular area. The widths of the columns is determined by the column_widths definition. This means that there is no auto-sizing supported.

If some rows don’t fit into the provided area, the table is split. The style of the original table is also applied to the split box.

Table Cell

Each table cell is a Box instance and can have an associated style, e.g. for creating borders around the cell contents. It is also possible to create cells that span more than one row or column. By default a cell has a solid, black, 1pt border and a padding of 5pt on all sides.

It is important to note that the drawing of cell borders (just the drawing, size calculations are done as usual) are handled differently from standard box borders. While standard box borders are drawn inside the box, cell borders are drawn on the bounds of the box. This means that, visually, the borders of adjoining cells overlap, with the borders of cells to the right and bottom being on top.

To make sure that the cell borders are not outside of the table’s bounds, the left and top border widths of the top-left cell and the right and bottom border widths of the bottom-right cell are taken into account when calculating the available space.


Let’s start with a basic table:

cells = [[layout.text('A'), layout.text('B')],
         [layout.text('C'), layout.text('D')]]

The HexaPDF::Document::Layout#table_box method accepts the cells as positional argument instead of as keyword argument but all other arguments of ::new work the same.

While the table box itself only allows box instances as cell contents, the layout helper method also allows text which it transforms to text boxes. So this is the same as the above:

composer.table([['A', 'B'], ['C', 'D']])

The style of the cells can be customized, e.g. to avoid drawing borders:

cells = [[layout.text('A'), layout.text('B')],
         [layout.text('C'), layout.text('D')]]
composer.table(cells, cell_style: {border: {width: 0}})

If the table doesn’t fit completely, it is automatically split (in this case, the last row gets moved to the second column):

cells = [[layout.text('A'), layout.text('B')],
         [layout.text('C'), layout.text('D')],
         [layout.text('E'), layout.text('F')]]
composer.column(height: 50) {|col| col.table(cells) }

It is also possible to use row and column spans:

cells = [[{content: layout.text('A'), col_span: 2}, {content: layout.text('B'), row_span: 2}],
         [{content: layout.text('C'), col_span: 2, row_span: 2}],

Each table can have header rows and footer rows which are shown for all split parts:

header = lambda {|tb| [[{content: layout.text('Header', text_align: :center), col_span: 2}]] }
footer = lambda {|tb| [[layout.text('left'), layout.text('right', text_align: :right)]] }
cells = [[layout.text('A'), layout.text('B')],
         [layout.text('C'), layout.text('D')],
         [layout.text('E'), layout.text('F')]]
composer.column(height: 90) {|col| col.table(cells, header: header, footer: footer) }

The cells can be styled using a callable object for more complex styling:

cells = [[layout.text('A'), layout.text('B')],
         [layout.text('C'), layout.text('D')]]
block = lambda do |cell| =
    (cell.row == 0 && cell.column == 0 ? 'ffffaa' : 'ffffee')
composer.table(cells, cell_style: block)



The Cells instance containing the data of the table.

If this is an instance that was split from another one, the cells contain all the rows, not just the ones for this split instance.

Also see start_row_index.


The column widths definition.

See ::new for details.


The Cells instance containing the header cells of the table.

If this is a TableBox instance that was split from another one, the header cells are created again through the use of header block supplied to ::new.


This value is -1 if fit was not yet called. Otherwise it contains the row index of the last row that could be fitted.


The row index into the cells from which this instance starts fitting the rows.

This value is 0 if this instance was not split from another one. Otherwise, it contains the correct start index.

Public Class Methods

new(cells:, column_widths: nil, header: nil, footer: nil, cell_style: nil, **kwargs)

Creates a new TableBox instance.


This needs to be an array of arrays containing the data of the table. See Cells for more information on the allowed contents.

Alternatively, a Cells instance can be used. Note that in this case the cell_style argument is not used.


An array defining the width of the columns of the table. If not set, defaults to an empty array.

Each entry in the array may either be a positive or negative number. A positive number sets a fixed width for the respective column.

A negative number specifies that the respective 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 column width definition is [-1, -2, -2], the first column is a fifth of the width and the other two columns are each two fifth of the width.

If the cells definition has more columns than specified by column_widths, the missing entries are assumed to be -1.


A callable object that needs to accept this TableBox instance as argument and that returns an array of arrays containing the header rows.

The header rows are shown for the table instance and all split boxes.


A callable object that needs to accept this TableBox instance as argument and that returns an array of arrays containing the footer rows.

The footer rows are shown for the table instance and all split boxes.


Contains styling information that should be applied to all header, body and footer cells.

This can either be a hash containing style properties or a callable object accepting a cell as argument.

Calls superclass method HexaPDF::Layout::Box::new

Public Instance Methods


Returns true if not a single row could be fit.

Calls superclass method HexaPDF::Layout::Box#empty?
fit(available_width, available_height, frame)

Fits the table into the current region of the frame.