Skip to main content

Layout Tags

Control spacing and horizontal positioning within any text element (p, th, td, h1h5).

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" }
ParameterDescription
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.).

ParameterDescription
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. In Foo [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 borderless table with "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. :::