Layout Tags
Control spacing and horizontal positioning within any text element (p, th, td, h1–h5).
Line break
Insert a line break within a paragraph without starting a new content element.
{ "p": "Line one[br]Line two[br]Line three" }
Long form: [linebreak]
Multiple breaks
Add a count to insert multiple line breaks at once:
{ "p": "Content above[br, 3]Content below with 3 blank lines between" }
Example: address block
{ "p": "Meridian Financial Group[br]45 Bay Street, Suite 1200[br]Toronto, ON M5J 2X5[br]Canada" }
Tab stop
Position text at a specific horizontal offset from the left margin. The optional third parameter controls how text aligns at the tab stop.
{ "p": "Label[tab, 200]Value" }
{ "p": "Label[tab, 200, 1]Right-aligned value" }
| Parameter | Description |
|---|---|
First (POS) | Horizontal offset in points from the left margin where the tab stop sits. |
Second (ALIGN, optional) | How following text aligns at the tab stop: 0 = left (default), 1 = right, 2 = center. |
Right-alignment is what you want for "page X of Y" footers and right-justified amount columns — the text ends at the tab position rather than starting there.
Example: label-value pairs
{ "p": "Invoice Number[tab, 200]INV-2026-0042" }
{ "p": "Date[tab, 200]April 15, 2026" }
{ "p": "Amount Due[tab, 200]$24,500.00" }
Example: three-column header with right-aligned date
{ "p": "Meridian Financial Group[tab, 250]Annual Compliance Report[tab, 495, 1]April 2026" }
This places the company name on the left, the report title in the middle starting at point 250, and the date right-aligned with its trailing edge at point 495. Right-alignment keeps the date pinned to the right margin even when the date string changes width.
Example: two-column receipt with right-aligned amounts
{
"content": [
{ "p": "[b]Item[/b][tab, 350, 1][b]Price[/b]" },
{ "p": "Consulting (40 hrs)[tab, 350, 1]$6,000.00" },
{ "p": "Development (80 hrs)[tab, 350, 1]$16,000.00" },
{ "p": "Infrastructure setup[tab, 350, 1]$2,500.00" },
{ "separator": { "lineType": "solid", "width": 0.5, "color": "#000000" } },
{ "p": "[b]Total[/b][tab, 350, 1][b]$24,500.00[/b]" }
]
}
:::tip Borderless tables scale better
When the layout matters more than the exact pixel position — e.g. header/footer regions that should adapt to page-size or margin changes — prefer a borderless table or columns block over absolute [tab, POS] stops. Tabs bind to absolute points and break when the page width changes; tables describe proportions and re-flow automatically.
For the most common case — "share this row's width between N pieces" — see [col] below. It does the math for you against the current container width, so authors don't compute points.
:::
Column splits
:::tip Not the same as HTML's colspan
[col, N] splits one paragraph into N weighted horizontal segments — a Bootstrap-like row inside body text. Use it to lay out header lines, label/value pairs, two-column receipts.
colspan (in a table cell) is the inverse — it merges multiple table columns into one cell. See Tables → Spanning cells.
The two compose freely — a colspan: 4 cell can host [col, 3][col, 1] inside it, splitting its interior into label / status segments. The tables edge-case gallery §11 shows this in one render.
:::
Split a paragraph into evenly-distributed (or weighted) segments without computing point positions. [col]…[/col] is the row-sharing ergonomic — most uses of [tab, POS] to lay out header lines, label/value pairs, or two-column receipts collapse to one or two [col] pairs.
{ "p": "[col]Invoice #2026-001[/col][col]Date 2026-05-21[/col]" }
The engine reads the current container's content width (page width minus margins), counts the [col] pairs in the paragraph, and places tab stops automatically. The last segment always right-flushes to the right edge — exactly what header lines and footer "Page X of Y" patterns want.
Equal vs weighted splits
Bare [col] segments split evenly. To allocate width unequally, add an integer weight as a parameter — the same relative-weight model as table.widths. Weights are normalized across all segments, so [col, 6][col, 6] and [col, 1][col, 1] produce identical output.
{ "p": "[col, 3]Narrow[/col][col, 6]Wide middle segment[/col][col, 3]Narrow[/col]" }
This is a 1:2:1 layout — the middle segment gets twice the width of the two side segments. Weight values are unitless; pick whatever totals are intuitive (4:4:4 for thirds, 6:6 for halves, 8:4 for two-thirds + one-third, etc.).
| Parameter | Description |
|---|---|
First (N, optional) | Integer weight ≥ 1 for this segment. Defaults to 1. Non-positive or non-numeric values fall back to 1. |
Example: brand header with right-flushed contact
{ "p": "[col][b]Meridian Financial[/b][/col][col]contact@meridian.example · +1 (416) 555-0140[/col]" }
Two segments → one stop at the right edge → the contact line flushes right while the brand stays left.
Example: three-segment row (left / centered title / right-flushed page)
{ "p": "[col]Acme Inc.[/col][col]Q2 Sales Report[/col][col]Page 1 of 3[/col]" }
Example: asymmetric labels (Bootstrap-style 12-grid)
{ "p": "[col, 8][b]Q2 Revenue Summary[/b][/col][col, 4][i]Fiscal year 2026[/i][/col]" }
Weights summing to 12 read like a Bootstrap row, but the engine doesn't enforce a base — any totals work. [col, 2] and [col, 1] together produce the same 2/3 + 1/3 split as [col, 8] and [col, 4].
Inline tags compose inside segments
Each segment accepts the full inline-tag vocabulary — bold, italic, color, font, links, fields, etc. — just like the surrounding paragraph:
{ "p": "[col][b]Acme Inc.[/b][/col][col][i]Confidential[/i][/col][col][fontcolor, #0066cc]contact@acme.com[/fontcolor][/col]" }
Behavior and trade-offs
A few sharp edges to know about:
- Text outside
[col]…[/col]is absorbed into the adjacent segment. InFoo [col]A[/col][col]B[/col], "Foo " merges into segment 1's left position; in[col]A[/col] middle [col]B[/col], " middle " merges into segment 1. Place all row content inside[col]pairs for predictable layout. - For two-segment rows, weights are visually invisible. The last segment always right-flushes, so
[col, 8][col, 4]and[col, 1][col, 1]produce identical output. If you genuinely need an asymmetric two-column row (e.g. 70% / 30%), reach for a borderlesstablewith"widths": [7, 3]instead. - Empty
[col, N][/col]still reserves its weighted slot. Useful for intentional gaps without breaking adjacent alignment.
:::tip When to use [col] vs [tab, POS] vs a table
[col]— the everyday choice for row-sharing within a single paragraph. Use it for header lines, footer lines, label/value pairs, and any "left / middle / right" pattern.[tab, POS]— only when you need a specific absolute offset (in points) that doesn't depend on container width. Rare in practice; reach for it when migrating legacy fixtures or when content must land at an exact ruler position.- Borderless
table— when each "column" contains multiple stacked items (label + value + sub-label all in column 1, etc.), or when content spans rows.[col]is a single-paragraph tool; tables are for grids. :::