Create Multi-Page Tables
Pass records and column definitions to table() and use a synchronous overflow
callback to continue on another page. Bundled Roboto Regular makes this work
without loading or registering a font.
import { createRecipe } from "@muhammara/wasm";
var Recipe = await createRecipe();
var people = [
{ name: "Alex", city: "Berlin" },
{ name: "Sam", city: "Paris" },
];
var pdf = new Recipe().createPage("letter");
pdf.table(50, 52, people, {
fontSize: 11,
columns: [
{ text: "Name", name: "name", width: 180 },
{ text: "City", name: "city", width: 160 },
],
header: true,
border: true,
// Continue on a new page at the same position.
overflow: (recipe) => {
recipe.endPage().createPage("letter");
return { position: [50, 52] };
},
});
var outputBytes = pdf.endPage().endPDF();
pdf.dispose();
Recipe.disposeAssets();
Columns come from order when it is set, otherwise from columns, otherwise
from every field found in any record, in first-seen order. Columns that a record
lacks, and null or undefined values, render as empty cells. A column
renderer runs once per cell, and the options it returns also size the row. A
continuation reserves room for its repeated header and uses the bounds of the
position and page it continues on. Empty contents draw nothing. After a table,
movedown(0, true) returns the table's left edge and bottom.
Cell values come from each record's own properties. Inherited properties,
including built-ins such as constructor and toString, are treated as
missing and passed to renderers as "" unless the record defines them itself.
Array-form order preserves exact keys, including surrounding whitespace and
empty-string keys; comma-separated string entries are trimmed. If no columns
are selected or discovered, the call draws nothing and preserves the cursor.
Header and row measurements include vertical padding, minHeight, fixed
height, and HTML line breaks. Set these through column cell/hcell,
header/row cell, or a renderer's textBox options. A column's cell is its
only body text box, so a column-level textBox is ignored. A row or header
cell replaces that style's textBox, and nested box styles such as style
merge across column, row, and renderer options. A table-level cell is
ignored; style cells through each column's cell or the table's row.cell.
Header text styles are independent of table/body text styles. Recipe resolves them in this order:
- Start with the column's
headerobject, or the default bold, centered header with 2pt padding when that option is omitted or boolean. A column-levelheader: falseselects the default style; it does not hide the header. - Apply table-level
headeroptions, includingheader.cellbox styling. - If
header.alignToDatais true, copy the column'scell.textAlign. - Apply the column's
hcellbox overrides, merging nested styles.
The table-level header option controls whether headers are drawn. Set header
font, size, and color explicitly when they should match the body; body
column styling cannot override an explicit header style. Measurement and every
repeated header use the same resolved options.
An overflow callback receives the Recipe as both this and its first
argument. It is called once for a pending row: return true to stop, or
continue in an area that fits the entire row plus its repeated header. The
destination is bounded by options.height and the page's bottom margin. If it
is too small, table() throws RangeError before drawing that header or row. A
callback that ends the page must start another one before continuing;
otherwise table() throws an Error.
Move the continuation upward, use a taller page/table area, reduce the cell
heights, or split a large record into multiple rows. Tables do not split a row
automatically.
Columns can define widths, cell styles, header styles, and renderers. Table options also support borders, row styling, bounded height, and repeated headers. Keep overflow callbacks synchronous; load every font and asset before starting layout.
A normal overflow function receives the Recipe as this as well as its first
argument. TypeScript callers can use RecipeTableOptions<Row> and
RecipeTableColumnOptions<Row> to check stored options against their record
fields; inline options infer the record type from the table contents.
To use your own face and skip loading Roboto, initialize with
var Recipe = await createRecipe({ defaultFont: fontFile }). The rest of the
example stays the same. You can also use defaultFont: false, register it with
await Recipe.registerFontAsync("table-font", fontFile) before layout, and set
font: "table-font" in the table options. See
Custom Fonts. The browser
example's Tables tab runs with no uploads and accepts an optional custom font.