Chapter 4 · Part II · The craft
Configuration: page and layout
The page size, margins and baseline grid, the columns and their gutters, and the running headers and footers
In short
This page covers the settings for the sheet itself. You choose the page size, the margins, the side the book is bound on and the grid the lines of text sit on. You set how many columns a page has and how much space runs between them. You also decide what is printed at the top and the bottom of every page, such as the page number or the chapter title. It is one of the nine pages of the Configuration reference.
#Page
The page property controls the physical dimensions and appearance of the page.
| Property | Type | Default | Description |
|---|---|---|---|
sizePreset | PageSizePreset | '17x24' | Predefined page size. Set to 'custom' to use explicit width/height. |
width | Dimension | 17 cm | Page width. Taken from sizePreset when omitted; an explicit value always wins (use sizePreset: 'custom' for fully custom sizes). |
height | Dimension | 24 cm | Page height. Taken from sizePreset when omitted; an explicit value always wins. |
margins | PageMargins | 2 cm all sides | Space between the page edge and the content area. Each side (top, bottom, left, right) is set independently. With mirror: true the margins are facing-page margins: left is the inner (spine-side) margin and right the outer one; odd pages (page 1 is odd) keep them as written and even pages swap them, so the content area — and with it the columns, float bands, header/footer containers and opener bands — moves across the spread. Default false. See below. |
backgroundColor | ColorValue | transparent | Page background color. |
dpi | number | 300 | The pixels per inch the layout works in: how physical units (cm, mm, in, pt) become its pixels. A bitmap without a resolution of its own takes one layout pixel per image pixel, so it prints at this many ppi: keep 300 for print, or give the pictures a resolution (Resource.bitmap.resolution, layout.bitmapResolution; see Document format › Bitmap size). The preflight checks each picture's effective resolution. |
cutLines | CutLinesConfig | disabled | Show trim marks at page corners for print cutting. When enabled, the canvas expands to include bleed area and crop marks. See below. |
baselineGrid | BaselineGridConfig | disabled | Draw the baseline grid over the pages, to check the vertical rhythm. The layout snaps to the grid whether or not it is drawn. See below. |
binding | 'auto' | 'left' | 'right' | 'auto' | The edge the book is bound on. 'auto' is 'right' when layout.writingMode is 'vertical-rl', when the document runs right to left (direction) or when its comics section reads right to left (a manga, a Japanese or Traditional Chinese edition), else 'left'. A right-bound book opens on a left page and mirrors its margins the other way round. Book-level: a heading style's own layout never changes it. See Binding. |
#Mirrored margins
Books are read as spreads, and the inner margin usually differs from the outer one. margins.mirror turns the four margins into facing-page margins:
{
"page": {
"margins": {
"top": { "value": 2, "unit": "cm" },
"bottom": { "value": 2.5, "unit": "cm" },
"left": { "value": 2.2, "unit": "cm" },
"right": { "value": 1.4, "unit": "cm" },
"mirror": true
}
}
}With this configuration every odd page has a 2.2 cm margin on the left (the spine) and 1.4 cm on the right (the fore-edge); every even page has 1.4 cm on the left (the fore-edge) and 2.2 cm on the right (the spine). Each laid-out page carries its own contentArea on the VDTPage, so everything derived from it — columns, full-width float bands, header and footer containers, and span: 'page' opener bands — follows the mirrored geometry automatically. Page and bleed frames used by design elements anchored to 'page' / 'bleed' are not affected: they describe the physical sheet, not the margins.
#Binding
"Vertically set Chinese documents are bound on the right-hand side, and horizontally set documents are bound on the left-hand side" (clreq §7.1.1.1). page.binding: 'right' lays a book out for the right edge:
- Page 1 is still odd and still the recto, so
breakBefore.parity,:::pagebreak{parity}, design elements with aparityand every page count keep their meaning. What changes is the side the recto sits on: the left page of the spread. A chapter that opens on a new recto opens on a left page (clreq §7.1.3.3). - With
margins.mirror,leftis still the inner margin, but it is the odd pages that swap: page 1 has its inner margin on its right, page 2 on its left. AoneAndHalfside column at'outer'/'inner', a rotated float that sits against the spine, part-page margins and box corner icons at'outer'/'inner'follow the same rule. - The document says so (
VDTDocument.binding: 'right'), so a host never has to read the config: the Sandbox shows its spreads as[3 | 2]with page 1 alone on the left of the spine, and its HTML viewer runs the pages right to left, opening at the right end, the left arrow going to the next page.renderToHtmlin multi mode lays the row out right to left. - The PDF carries
/ViewerPreferences << /Direction /R2L >>and/PageLayout /TwoPageRight(page 1 alone, then pairs), tagged or not. Acrobat and Foxit follow them; Chrome's built-in viewer ignores both.
Folios and running heads do not move by themselves: a template that prints the page number at the outer corner needs its odd and even elements set for the right edge (elements take a parity).
A book written right to left (Arabic, Persian, Hebrew…) is bound on the right as well: 'auto' gives the right edge when the document's direction resolves to 'rtl'. Such a book also mirrors its whole flow, so its first column is the right one and its indents, list markers, floats and notes stand on the right; header and footer slots stay physical. See Arabic layout.
A comic book read right to left is bound on the right too: 'auto' gives the right edge when the config has a comics section whose reading direction resolves to 'rtl'. That is the case of a manga (comics.artDirection: 'rtl') and of a Japanese or Traditional Chinese edition of a Western comic. The section decides for the whole book, not the :::page blocks of one chapter. See Comics.
#Page size presets
| Preset | Width | Height | Common use |
|---|---|---|---|
'11x17' | 11 cm | 17 cm | Pocket books |
'12x19' | 12 cm | 19 cm | Standard paperback |
'17x24' | 17 cm | 24 cm | Technical books, textbooks |
'21x28' | 21 cm | 28 cm | Magazines, reports (near A4) |
'broadsheet' | 375 mm | 597 mm | Broadsheet newspapers |
'berliner' | 315 mm | 470 mm | Berliner newspapers |
'tabloid' | 280 mm | 430 mm | Tabloid newspapers |
'compact' | 297 mm | 420 mm | Compact newspapers (a broadsheet folded in half) |
The four newspaper formats are new in postext 1.18 and are left out of the drawing: a broadsheet page has almost four times the area of a 21 × 28 one. They are usually set with the 'multiple' layout; six columns are common on a broadsheet and five on a tabloid.
#Baseline grid
The baseline grid is the rhythm of the body text: lines one body line height apart, counted from the top of the content area. The layout uses it whether or not it is drawn: headings, list ends, boxes, figures and display formulas bring the text back onto it (unless their own snapToGrid is off), so the lines of adjacent columns stay aligned. enabled only draws the lines, in the canvas, the PDF and the Sandbox views, to check that rhythm; turning it on or off moves nothing. The lines span only the page's actual text — from the first text line to the last — so float bands, blank parity pages, and unused tail space show no grid.
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Whether to draw the grid lines (canvas and PDF). Only the drawing: the layout is the same either way. |
color | ColorValue | #cccccc | Color of the grid lines. |
lineWidth | Dimension | 0.5 pt | Thickness of the grid lines. |
page: {
baselineGrid: { enabled: true, color: { hex: '#e0e0e0', model: 'hex' } }
}#Cut lines
When enabled, the canvas expands to include a bleed area and the engine draws crop marks at each corner for print production. The PDF gives every page a TrimBox and a BleedBox; a PDF/X file has them even without cut lines. In the Sandbox they are set in Export › Print preparation.
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Whether to expand the canvas with bleed and draw crop marks. |
bleed | Dimension | 3 mm | Extra area around the page used for print bleed. |
markLength | Dimension | 5 mm | Length of each crop mark. |
markOffset | Dimension | 3 mm | Gap between the trim edge and the start of each crop mark. A mark never starts inside the bleed: when bleed is wider, the mark starts at the bleed edge. |
markWidth | Dimension | 0.25 pt | Thickness of the crop marks. |
color | ColorValue | #000000 | Color of the crop marks on the canvas (the screen preview). The PDF always paints them in registration colour (see below). |
The sheet grows by bleed + markOffset + markLength on every side, and the trimmed page sits in its middle. Each corner of the trim gets two marks, markLength long, each in line with one of the edges that meet there. A mark starts markOffset outside the trim, or at the bleed edge when bleed is the wider of the two, so no mark lies over art that runs into the bleed. With the defaults (3 mm of bleed, a 3 mm offset) the marks run from 3 to 8 mm outside the trim, and a blank band 3 mm wide runs round the sheet outside them. cropMarkSegments(page, doc.config.page, doc.trimOffset) returns the eight marks of a page in page pixels, the ones the canvas and PDF backends draw. The third argument is where the trim sits inside the sheet, the value the PDF's TrimBox is written from; left out, it is worked out from cutLines the same way.
Nothing but the marks prints outside the bleed. Everything the page paints, the design elements anchored to 'page' or 'bleed' included, is clipped to the bleed box on the canvas, in the PDF and in the HTML output, as a DTP export clips it: a band or a picture set past the bleed on purpose is cut at the bleed edge, where the trimmer would lose it anyway. Up to postext 1.4 such an element ran on over the marks to the edge of the sheet.
In the PDF (postext-pdf), the MediaBox of each page is the whole sheet. The page also carries a TrimBox, the trimmed page, and a BleedBox, the trim plus the bleed, which imposition and preflight tools read. The marks are painted in registration colour, the /All separation, so they print on every plate. This holds whatever colour space the PDF is written in (RGB, grayscale or CMYK): a plain black would reach the printer as rich black or as the black plate alone. color only applies to the canvas.
#Numbering
The page.pageNumbering block controls how page labels are formatted and where the counter starts. It only defines the document-wide default — to restart numbering mid-document (e.g. roman-numeral front matter switching to decimal chapters from 1), use the :::numbering directive (see Document format → Directives).
| Property | Type | Default | Description |
|---|---|---|---|
format | 'decimal' | 'lower-roman' | 'upper-roman' | 'lower-alpha' | 'upper-alpha', or an East Asian style | 'decimal' | Numeric style used to render page labels. The East Asian styles ('trad-chinese-informal' numbers the pages 一, 二, 三) are listed under Numbering format spellings. |
startAt | number | 1 | Numeric value assigned to the first page regardless of format. format: 'lower-roman', startAt: 1 yields i, ii, iii, …; format: 'decimal', startAt: 17 yields 17, 18, 19, …. |
The computed label is stored on every VDTPage as pageLabel and is what the {pageNumber} header/footer placeholder resolves to. PDFs emit a /PageLabels number tree so Preview / Acrobat's page indicator and "Go to page" navigation match the printed labels exactly. A style PDF has no code for (Chinese numerals, circled and fullwidth digits) is written out page by page, so the reader shows 一, 二, 三 too.
Numbering format spellings
Three settings choose a numbering format, and each grew its own spelling: page labels (page.pageNumbering.format and :::numbering{format=…}) say lower-roman, ordered lists (orderedLists.numberFormat) say arabic for decimal, and resource types (counterFormat) say roman-lower. Every one of them accepts all the spellings below, so a format copied from one setting works in the others. Names are case-insensitive; the one-character forms are not (i and I differ).
| Format | Prints | Accepted spellings |
|---|---|---|
| Decimal | 1, 2, 3 | decimal, arabic, 1 |
| Lower roman | i, ii, iii | lower-roman, roman-lower, i |
| Upper roman | I, II, III | upper-roman, roman-upper, I |
| Lower alpha | a, b, c | lower-alpha, alpha-lower, lower-latin, a |
| Upper alpha | A, B, C | upper-alpha, alpha-upper, upper-latin, A |
| Chinese numerals, Simplified | 一, 十二, 一百零一 | simp-chinese-informal, 一 in a Simplified document |
| Chinese numerals, Traditional | 一, 十二, 一萬 | trad-chinese-informal, cjk-ideographic, 一 in a Traditional document |
| Chinese financial numerals, Simplified | 壹, 壹拾贰, 壹佰贰拾 | simp-chinese-formal, 壹 in a Simplified document |
| Chinese financial numerals, Traditional | 壹, 壹拾貳, 壹佰貳拾 | trad-chinese-formal, 壹 in a Traditional document |
| Chinese digits | 一二〇, 二〇二六 | cjk-decimal, 〇 |
| Heavenly stems | 甲, 乙, 丙 … 癸 | cjk-heavenly-stem, 甲 |
| Earthly branches | 子, 丑, 寅 … 亥 | cjk-earthly-branch, 子 |
| Japanese numerals | 一, 十二, 百一, 一万一 | japanese-informal, 一 in a Japanese document |
| Japanese formal numerals | 壱, 壱拾弐, 壱百 | japanese-formal, 壱 |
| Hiragana, gojūon order | あ, い, う … ん, ああ | hiragana, あ |
| Katakana, gojūon order | ア, イ, ウ … ン, アア | katakana, ア |
| Hiragana, iroha order | い, ろ, は … す, いい | hiragana-iroha, い |
| Katakana, iroha order | イ, ロ, ハ … ス, イイ | katakana-iroha, イ |
| Circled | ①, ②, ③ … ㊿ | circled-decimal, ① |
| Fullwidth digits | 1, 2, 3 | fullwidth-decimal, 1 |
| Arabic-Indic digits | ١, ٢, ٣ … ١٠ | arabic-indic, ١ |
| Persian digits | ۱, ۲, ۳ … ۱۰ | persian, urdu, ۱ |
| Arabic letters, abjad order | أ, ب, ج, د, هـ … غ, أأ | abjad, أبجد |
| Arabic letters, alphabetical order | أ, ب, ت, ث … ي, أأ | hijai, arabic-alpha, arabic-alphabetic, أبتث |
| Abjad numerals | ا, ب … يا (11), غتمو (1446) | arabic-abjad |
| Abjad numerals, Maghrebi values | ص (60), ض (90), ش (1000) | arabic-abjad-maghrebi, maghrebi-abjad |
The East Asian styles keep their CSS Counter Styles names in all three settings. The informal Chinese numerals write 十 for 10 to 19 without a leading 一 (十二, but 一百一十), one 零 for a run of zeros inside the number (一百零一, 一千零五十), and 万 or 萬 for ten thousand, 亿 or 億 for a hundred million (一万零一十). Only cjk-decimal uses 〇, digit by digit, the way years are written (二〇二六, GB/T 15835—2011). The stems stop at 10, the branches at 12 and the circled digits at 50; past them the number prints in digits. 一 and 壹 follow the script of the document's locale: Traditional for zh-Hant, zh-TW or zh-HK, Simplified otherwise. Some older editions write 101 as 一百一 with no 零; Postext does not print that form.
The Japanese styles (since postext 1.16) follow CSS Counter Styles too. japanese-informal writes 十 without a leading 一 and no 零 for an empty place (百一, 千十), and japanese-formal the 大字 壱 弐 参 拾 百 阡 with 壱 kept before each unit (壱拾, 壱百). CSS stops both at 9 999; Postext goes on in groups of four digits with 万 億 兆 (formal 萬 億 兆), each group keeping its 一 before a unit: 一万一 is 10 001, 一億一万 100 010 000. In a Japanese document (ja, ja-JP…) 一 means japanese-informal, so 第{1:一}章 prints 第百一章 there and 第一百零一章 in a Chinese one; 壹 stays the Chinese formal numerals. The kana series are the CSS lists: 48 kana in gojūon order (ゐ and ゑ included) and 47 in the order of the iroha poem; past the last they go on with two kana (ああ, いい), and they have no zero, so an item numbered 0 prints 0. Positional kanji digits (二〇二六), the form of years and folios, are cjk-decimal.
The Arabic styles keep the CSS names where CSS has one (arabic-indic, persian; urdu and maghrebi-abjad from the W3C Ready-made Counter Styles are read as persian and arabic-abjad-maghrebi). arabic keeps its old meaning, the European digits. arabic-abjad writes the additive abjad numerals of Classical Arabic and manuscript foliation, highest value first: 11 is يا, 1446 غتمو, and the thousands count goes before غ (2000 بغ, 1002 غب); past 999 999 the number prints in digits. The W3C note uses the name for a 28-letter series where 11 is ك; Postext calls that series abjad, the lettering of list items in abjad order (أ، ب، ج، د، هـ), and hijai the alphabetical order (أ، ب، ت، ث). Both write the first letter with its hamza (أ), and a heh that would stand alone as هـ, with a tatweel, so neither is read as the digits ١ and ٥; past 28 they double, as lower-alpha does (أأ, أب). The Maghrebi values follow the mnemonic صعفض قرست ثخذ ظغش (ص 60, ض 90); the W3C list swaps ص and ض. A single أ cannot tell the two letter series apart, so their tokens are their first four letters: {1:أبجد}, {1:أبتث}. A document's own digits are set with numerals.
The other spellings are for configurations that nothing type-checks: JSON presets and plain JavaScript. The TypeScript types still name only each setting's own spelling — the one the Sandbox writes and resolveAllConfig returns, the East Asian names included — so a typed PostextConfig keeps to it, and another spelling needs a cast.
// JavaScript or a JSON preset (in TypeScript, each setting's own spelling)
orderedLists: { numberFormat: 'decimal' }, // same as 'arabic'
page: { pageNumbering: { format: 'roman-lower' } }, // same as 'lower-roman'
resourceTypes: [{ id: 'plate', counterFormat: 'upper-roman', … }], // same as 'roman-upper'resolveAllConfig turns the list and page formats into their own setting's spelling — 'decimal' resolves to 'arabic' in orderedLists, 'roman-lower' to 'lower-roman' in page.pageNumbering — and stripConfigDefaults drops a spelling of the default. The Sandbox panels show each of the three settings in its own spelling, whichever one the configuration uses. Any other value — roman, 01, a typo — numbers in decimal instead of printing undefined, and is reported as a configuration warning. The heading numbering templates take the same names after the colon ({1:roman-upper} is {1:I}), next to their own zero-padded {1:01}.
#Layout
The layout property controls how columns are arranged within the content area.
| Property | Type | Default | Description |
|---|---|---|---|
layoutType | 'single' | 'double' | 'oneAndHalf' | 'multiple' | 'double' | Column arrangement. See below for details on each type. |
columnCount | number | 3 | How many equal columns a 'multiple' layout cuts the body into: a whole number from 3 to 8. A value outside that range, or not a whole number, is clamped and reported (see Layout types). A heading style's own 'multiple' layout takes the document's count unless it sets its own. Since postext 1.18. |
gutterWidth | Dimension | 0.75 cm | Horizontal space between columns. Only applies to multi-column layouts. |
sideColumnPercent | number | 33 | Width of the side column as a percentage of the content area. Any value that leaves both columns some width is used as written; one that would not is clamped, and the build reports it (see Layout types). Only applies to 'oneAndHalf' layout. |
sideColumnRole | 'text' | 'floats' | 'text' | What the side column carries: body text (it flows there after the main column), or only the resources and callouts placed with span: 'side' — a float-only margin column. 'oneAndHalf' only. With 'text', a paragraph that runs on from one column into the other is broken again for the width of the column it goes on in; up to postext 1.4 it kept the lines of the column it started in, and a line set for the main column ran past the side one, which clipped it. |
sideColumnSide | 'right' | 'left' | 'outer' | 'inner' | 'right' | Edge of the content area the side column sits at. 'outer' / 'inner' follow the page parity when the margins are mirrored (a recto's outer edge is its right edge, a verso's its left). 'oneAndHalf' only. |
columnRule | ColumnRuleConfig | disabled | Optional visual rule drawn between columns. See below. |
fitFiguresToPage | boolean | false | Shrink a figure (bitmap or SVG) whose image, caption and note would stand taller than the content area until they fit it (a picture with a safe area, Resource.safeArea, is first cropped within it at its full width, and only shrunk if it still does not fit), and set an inline figure a little too tall for the room left in its column smaller (down to half its width, the caption keeping the column's measure) so it stays with its text. A shrunk image sits in its slot per placement.align. The HTML viewer turns it on, since its pages are only as tall as the screen; printed pages are sized for their figures. |
bitmapResolution | 'document' | 'file' | number | 'document' | How a bitmap without its own bitmap.resolution takes its natural print size. 'document' reads its pixels at page.dpi; a number is the ppi of every such bitmap (300: a 2400 px picture is 203.2 mm wide whatever the page's dpi); 'file' uses the resolution its file states (bitmap.fileResolution), 72 and 96 counting as unset, else page.dpi. The column still caps the picture, and a smaller one is never enlarged. See Document format › Bitmap size. Since postext 1.24. |
floatShrink | FloatShrinkConfig | mode: 'never' | The document default for a floated picture's placement.shrink (mode: 'never', 'page' or 'slot') and placement.minScale (minScale, 0.7 when unset): scale the picture down, keeping its proportions, to the room of its slot instead of moving it on (see Document format › Placement). A resource's placement, then its type's defaultPlacement, override it. fitFiguresToPage caps every figure at the content area; floatShrink looks at the band a float would really take, under an opener, beside other floats or over footnotes. Since postext 1.24. |
wrap | TextWrapConfig | minTextWidth: 12em, minLinesBeside: 2, defaultWidth: 0.45 | Text running beside a picture or a box narrower than its column (placement.wrap, a box's wrap): gap, the space between the item and the text (one body line when unset); minTextWidth, the narrowest measure the text beside it may take, a length or a share of the column (narrower: the item takes its band whole, with a textWrap warning); minLinesBeside, the fewest lines worth setting beside it; defaultWidth, the share of the column a wrapped item takes when it sets no width. See Document format › Text wrap. Since postext 1.24. |
floatsAtCitingPage | boolean | false | The document default for placement.citingPage: a 'top' or 'auto' float may take the head of the page (a page-span float) or of the column (a column float) where the line that first cites it lands, instead of the first free slot after that line; the text above the reference moves down under it (see Document format › Placement). A resource's placement, then its type's defaultPlacement, override it. Since postext 1.25. |
maxTopFraction | number | 0.7 | The largest share of a column's height, 0 to 1, that a float heading the page or column that cites it may take, with the floats already standing there, so the page keeps room for its text (LaTeX's \topfraction). A float that would take more goes to its usual slot. Since postext 1.25. |
hugClosingFloats | boolean | true | On the closing page of a chapter (and of the document), the page-wide figures and tables set below the last band of text move up to sit one float gap under it, stacked in their order: nothing follows them there. false leaves them where their placement put them, so a position: 'bottom' float ends at the page foot on the closing page as on every other page — a datasheet whose outline ends at the same height on every page, say. Pages with a side column never move them. |
inlineResourceGap | 'around' | 'above' | 'around' | Where an inline resource (placement.position: 'here', embedded with ::resource) keeps the float gap, a line. 'around' keeps it above and below the resource, and the text after it goes back onto the baseline grid under that gap; a heading, a list, a box or another inline resource right after it shares the gap below with its own space above, the larger of the two applying. 'above' keeps it above only: the text after the resource resumes at the next grid line, however close that is — anywhere from nothing to a line — as up to postext 1.4. Configurations stored by earlier versions whose chapters embed a resource are read with 'above', so their pages do not move (see Bundles written by postext 1.4 or earlier). A configuration written in code for 1.4 keeps the old spacing by setting 'above' itself, or through pinLegacyInlineGap from postext/bundle. |
inlineResourceGapInBoxes | boolean | true | Whether an inline resource inside a box (:::callout) keeps the gap inlineResourceGap sets, a line of the box's own text: above the resource, and below it too with 'around', the larger of it and the next block's own space applying. At the top or the foot of the box, or of a fragment of a split box, the padding sets the resource off instead and no gap is added. false sets the resource right under the text before it and the text after it right under the resource, as up to postext 1.4. Configurations stored by earlier versions whose chapters embed a resource inside a box are read with false (see Bundles written by postext 1.4 or earlier); in code, pinLegacyBoxResourceGap from postext/bundle does the same. |
boxChildSplitMinLines | number | 2 | Fewest lines of a paragraph or list item that a cut inside it leaves on each side when a box splits (splitMinLines, under Callout styles, still counts every line of the box on each side of the cut). A whole number, at least 1. With the default a cut never leaves a lone line of a paragraph or item at the foot of a column or at the head of the next; a box style whose splitMinLines is lower sets the limit instead. 1 lets a cut leave one line of the paragraph or item on a side, as up to postext 1.4. Configurations stored by earlier versions whose chapters hold a :::callout are read with 1 (see Bundles written by postext 1.4 or earlier); in code, pinLegacyBoxChildCut from postext/bundle does the same. |
flowColumns | boolean | true | A :::columns fence outside a box sets its blocks in sub-columns of the text column (or across the page with span="page"), and a group, in a box or in the text, is cut between its sub-columns when it does not fit, going on in the next column or page (see Document format › :::columns). false ignores the fence outside a box and never cuts inside a group, as up to postext 1.24; configurations stored by earlier versions whose chapters hold a :::columns fence are read with false; in code, pinLegacyFlowColumns from postext/bundle does the same. A styled section keeps the document's value. Since postext 1.25. |
floatsUnderOpener | boolean | true | Under a page-wide opener (a heading level or style with span: 'page') set over two or more text columns, the head of the opener's own column, right under its band, is a slot for a 'top' or 'auto' float, level with the heads of the other columns: a floated box fenced right after the opener, or a resource embedded there, sits under the opener in the first column (across several columns from it with columns), and the column's text starts under it. A float cited in running text still follows the line that cites it. false offers no slot there, so such a float lands from the second column on, as up to postext 1.24; configurations stored by earlier versions that set a page-wide heading are read with false; in code, pinLegacyOpenerHeadFloats from postext/bundle does the same. A styled section keeps the document's value. Since postext 1.25. |
writingMode | 'horizontal-tb' | 'vertical-rl' | 'horizontal-tb' | How lines run. 'vertical-rl' sets Chinese and Japanese text vertically: characters top to bottom, each line to the left of the one before. A heading style's layout inherits it unless it sets its own, so a horizontal appendix can follow a vertical book. See Vertical writing. |
#Vertical writing
With writingMode: 'vertical-rl' a page is laid out as a horizontal page turned a quarter turn clockwise. The flow is set in a frame as wide as the sheet is tall; its lines are the columns of vertical text, read from the right, and everything the engine does with lines (breaking, justification, floats, footnotes, keep-together rules) works in that frame. So, on the sheet:
- A column of the layout is a tier (栏):
layoutType: 'double'gives two tiers stacked top to bottom, filled from the upper right;gutterWidthis the gap between them and the column rule a horizontal rule between them. Tiers are not balanced at the end of a chapter (clreq §7.1.3.4): column balancing is off in a vertical document unlessheadings.balancing.enabledis set. - What the flow calls "top" is the sheet's right edge, where reading starts: a top float sits at the right of the page, a bottom float at the left, a page-span opener is a band down the right edge, footnotes land at the left end of each tier. A design element anchored to the top of the page (a heading design, a fixed box) is anchored to the right edge.
sideColumnSide'left'is the top tier,'right'the bottom one;'outer'and'inner'read as'right'and'left'. - The flow's margins are the sheet's margins turned: the right margin is the top of the flow, the top margin its left.
page.marginskeep their names on the sheet. - Running heads, folios, crop marks and the page background stay on the sheet, set horizontally, as clreq describes for vertical books.
- Figures and tables stand upright. A figure fills the height of its tier as far as its caption allows, at most as wide as the page; the width it takes is the room it uses in the flow. Its caption is set horizontally under it, and so are the cells of a table: both are measured as horizontal text. A table is set upright across the page, its rows cut to the tier when it is taller.
placement.alignsets a figure at the top ('left'), middle or foot of its tier. Aplacement.rotateis not applied where the figure is first referred to in vertical text: the build reports arotateIgnoredVerticalcontent warning. A horizontal section of the book (a heading style whoselayoutsets'horizontal-tb') turns its figures as asked. - A picture of a design (an opener's illustration, a part page's) stands upright too. Its box in the flow is sized with the picture's width and height swapped, so
size.widthis how far the picture runs down the column and its width on the sheet follows from its proportions. - Characters: Han, kana and fullwidth forms stand upright, one em each; Latin words and numbers are turned sideways with their horizontal widths; punctuation takes the font's vertical form. Pause and stop marks are never turned: a mainland font sets 、。,. in the top-right corner of the cell and !?:; in its right half, a Taiwan or Hong Kong font centres them (
cjk.region). Brackets take their vertical forms, and “ ” ‘ ’ in mainland text read as 『』「」. Dashes, ellipses and the wave dash take the font's vertical form where it has one (Noto CJK keys that of — toverttogether withfwid: a rule down the middle of the cell), else they are turned with their ink centred on the column's axis. A 破折号 (——) in Chinese text is one rule down the column: its vertical forms would leave blank at both ends of each cell, so each dash is turned with the line and stretched as in horizontal text (see Punctuation widths). A number of at most two digits stands in one upright cell, unless it sits in a Latin sentence, whose words it follows (see Numbers in vertical text). The interpunct (·) takes half a cell in mainland text and a whole cell in Taiwan and Hong Kong text. The signs Unicode sets upright (× © ± § ℃ ① and the like) stand in a cell of their own, inside a number too:3×4is 3 and 4 sideways with × upright between them. An apostrophe or an interpunct between two letters of a Latin word (don’t,l·l) stays in the word, sideways. A Latin paragraph in a vertical flow follows the same rules. Inline formulas, chips and swatches are turned sideways with the line. - Every character is measured as it is painted: a cell advances its cell down the line, a sideways run its horizontal width. Text that stays horizontal on the sheet (running heads, folios, captions, table cells) is measured horizontally.
- The punctuation widths, hanging punctuation and the space between Han and Latin apply down the line as they apply across it. The blank before a glyph is above it and the blank after it below: a Kaiming 、 takes half a cell,
」「compress to a cell and a half, an opening bracket trimmed at the head of a line starts half a cell higher, a hung 。 sits under the foot of its line, and the Han–Latin space is a quarter em of the column above and below a sideways word.:;?!keep a whole cell in vertical text in every region. - The character grid counts characters down the line and lines across the page:
charsPerLinesets how long a tier is,linesPerPagehow many lines a page holds, andlayoutType: 'double'gives two tiers of whole characters with a gutter of whole ems between them.
For hosts reading the layout: a vertical page carries VDTPage.flow. Its contentArea, columns, blocks, lines, floats, footnote areas, opener band and block design overlays are in flow coordinates; width, height, header and footer are on the sheet. flowToPage, pageToFlow, flowRectToPage and pageRectToFlow map between the two, and verticalOrientation(char, region) says how a character stands. flow.centralBaselines gives, per font family, the axis the layout centred upright characters on: the centre of the ink of 中, whose long stroke runs the height of the em box (0.38 em above the baseline in Noto Serif and Noto Sans, SC and TC alike), and which every Chinese, Japanese and Korean face has. Each line is centred on that axis: a vertical line's baseline sits half its line height plus the family's central baseline below the top of its line box (a horizontal line's sits 0.8 of its line height down), so a column of characters stands in the middle of its pitch and a rule drawn between two columns at a whole pitch falls halfway between them. A vertical design text is set the same way in its own lines. The canvas paints a vertical page itself; to paint punctuation with the font's own vertical forms a browser host loads, once per family, a twin face with them switched on: loadVerticalAlternates(family, faces), where faces are the family's sources (URLs or bytes) and descriptors. The twin is kept only where the browser applies the feature to canvas text (Chrome 140 and later): it draws 「(《 with the twin and with a copy of the same faces loaded without the feature, at the weight and style of the faces given, and keeps the twin when their ink differs. A later call for other faces of a family whose twin is in use (the bold after the regular) adds them to it. The Sandbox does this for every family of a vertical document. Without a twin, brackets are turned about their em box and mainland pause and stop marks moved within their cell where the font's vertical forms put them: 、。,. to the top-right corner (within 0.07 em of Noto Serif SC's vertical forms), !?:; half an em to the right and a little up (within 0.02 em). The PDF and the HTML set the same page: see Vertical text in the PDF and the table under How the HTML output differs from canvas and PDF. Checked cell by cell against HarfBuzz (vertical layout with vert) in Noto Serif TC and SC, every character of 「賈雨村」云云,宜乎?故曰!;:、。“引”‘單’…… stands within 0.02 em of it on the canvas and in the PDF, and within 0.05 em in the HTML (Chrome centres a turned glyph on the middle of the font's ascent and descent, which in Noto is 0.05 em above its em box's centre). flow.dashAdvances gives, per family, the horizontal advance in ems of each dash the page stretches to fill its cell (— – ― ⸺ ⸻ -), for a renderer with no font metrics of its own: the HTML stretches the dash by it, as the canvas and the PDF do from theirs. Running heads and folios can be set vertically too: see Vertical text elements.
#Column rule
Draws a thin vertical line in the gutter to visually separate columns.
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Whether to draw the column rule. |
color | ColorValue | #cccccc | Color of the rule line. |
lineWidth | Dimension | 0.5 pt | Thickness of the rule line. |
Under a page-span heading (span: 'page') the rule starts where the text of the columns starts, under the heading's band, whether the default opener or a design of its own paints the band. Up to postext 1.4 it ran from the top of the text block, through the band.
A heading style can set a rule of its own in its layout (see Heading styles), and the pages of its section draw that one. A field the style leaves unset takes the document's value, so a section that only changes its columns keeps the document's rule. Up to postext 1.4 a style's rule was never drawn: every page drew the document's.
#Layout types
-
'single'— One column spanning the full content width. Best for narrow pages or text-heavy content with long paragraphs. -
'double'— Two equal-width columns. The classic editorial layout — keeps line measure within the optimal 40–50 character range for comfortable reading. -
'oneAndHalf'— An asymmetric layout with a main column and a narrower side column. The side column (controlled bysideColumnPercent) is ideal for margin notes, small figures, or supporting content. Values between 25–40% work well, and a narrow channel for line numbers or marginal marks takes around 10–15%. The side column issideColumnPercent% of the content width and the main column is what is left after the gutter — so at 50% the side column is one gutter wider than the main one. Any value is laid out as written while both columns keep at least 1% of the content width; one that would leave either narrower — 0 or below, or one so wide that the main column vanishes behind the gutter — is clamped to the nearest value that keeps both, and a value that is not a number takes the default, 33. The document'sconfigWarningsthen carry{ kind: 'sideColumnPercentClamped', path: 'layout.sideColumnPercent', value, used }— the path of a heading style's ownlayoutnames the style, as inheadingStyles[2].layout.sideColumnPercent, and is measured on that style's margins — which the Sandbox lists in its Checks panel;collectConfigWarnings(config)returns the same list without laying anything out. A layout that is not'oneAndHalf'never reads the value, and never reports it. WithsideColumnRole: 'floats'the body text never enters the side column: it becomes a channel for the figures, tables and callouts placed withspan: 'side'. A side figure or table stacks from the head of the channel on the page that first cites it — the marginal figure of a textbook sits at the top of its page even when the text cites it further down; one that cannot fit the rest of the channel waits for the next page's. A side box stacks beside the text it interrupts, and when the rest of the channel cannot hold it there it slides up to the lowest position that still fits (its foot on the channel's foot), or waits for the next page. When the text after a box's fence continues on the next page (the column is full, or the break rules move that text on), the box stays at its fence, beside the text before it; a style withsideAtColumnEnd: 'after'sets it level with the first line of the text after the fence instead, in the next page's channel, as line numbers and marginal headings written before their line need. An element of a heading design that stands in the channel — a chapter numeral anchored in the outer margin — is kept clear of the stack too (see Reserved height).span: 'page'floats and boxes still cross both columns, and a column float withplacement.captionSideputs its caption in the channel, level with the figure. Combined with mirrored margins andsideColumnSide: 'outer', the channel sits at the outer edge of every page — the marginal column of a textbook. -
'multiple'— Three to eight equal columns, as many ascolumnCountsays (default 3): the grid of newspapers and of many magazines (since postext 1.18). Each column is(content width − (n − 1) × gutterWidth) / nwide. The text runs on from one column to the next as in a two-column layout; the column rule is drawn in every gutter, column balancing levels all the columns on a chapter's closing page and before a page-wide box, footnotes work in every column, and page-wide figures, boxes and headings cross the whole page. A figure or a floated box can take only some of the columns:placement.columns(see Resource types) and a callout style'scolumns. AcolumnCountoutside 3–8, or not a whole number, is rounded and clamped to the nearest count that is (a value that is not a number takes 3), and the document'sconfigWarningscarry{ kind: 'columnCountClamped', path: 'layout.columnCount', value, used }(the path of a heading style's ownlayoutnames the style, as inheadingStyles[1].layout.columnCount). A layout of another type never reads the value. A heading style whoselayoutis'multiple'takes the document'scolumnCountunless it sets its own, so a newspaper set in five columns can run its opinion pages in four.
#Headers & footers
The header and footer properties control per-page header and footer slots. Headers and footers render inside the existing page margins — they do not reserve additional space and do not shrink the content area.
Container frame. An element anchored to 'container' is placed in the margin band between the body and the trim edge, spanning the content-area width. The header container runs from the trim top down to the top of the body; the footer container from the bottom of the body down to the trim bottom. So top-* header anchors and bottom-* footer anchors measure from the trim edge, while bottom-* header anchors and top-* footer anchors measure from the body edge. The container never includes the bleed or the crop-marks band, so a header or footer lands at the same position on the trimmed page whether page.cutLines is on or off. Anchor to 'page' (the trim box) or 'bleed' to reach beyond the content-area width or into the bleed.
Slots use the unified design slot model: every element has a placement with an anchor (to the container or to another element by #id), an optional offset, and an optional size. The legacy flat fields align, marginFromBody, marginFromEdge, and width: 'full' are still accepted on input and are migrated to the new shape automatically; the equivalent new-shape description is documented below.
Each slot holds a list of text, rule, and box elements. Array order is paint order (first element paints first, last element paints on top). This holds whatever the anchors say: an element may anchor to one listed after it (anchor.to: '#ttl'), so a background box can come first and still be positioned against the text it sits behind.
Built-in defaults. When header or footer is undefined, postext applies a sensible built-in default rather than an empty slot:
- Default header:
{title}right-aligned on odd pages,{chapterTitle}left-aligned on even pages, and a full-width rule — all in the palette's main color, Open Sans 8pt/600,marginFromBody16pt(text) /13pt(rule). - Default footer:
{pageNumber}centered on every page in the palette's main color, Open Sans 8pt/600,marginFromBody16pt.
To opt out of the built-in defaults, set header: { elements: [] } (or footer: { elements: [] }). An explicit empty elements array is preserved as "no elements" — only undefined triggers the defaults.
| Property | Type | Default | Description |
|---|---|---|---|
elements | HeaderFooterElement[] | built-in defaults when undefined; [] disables | Ordered list of text and rule elements. |
#Text elements
Text elements render a template string with placeholder substitution. Placeholders use {name} syntax; {{ and }} emit literal braces.
The defaults in the table below are those of a text element you add yourself. The built-in header and footer described under Built-in defaults above are ready-made elements with their own values (Open Sans 8pt/600 in the palette's main colour), not the element defaults.
| Property | Type | Default | Description |
|---|---|---|---|
kind | 'text' | — | Discriminator. |
id | string | — | Stable id, unique within the slot. Other elements anchor to it with anchor.to: '#id'. The sandbox assigns one on creation. |
content | string | '' | Template string. Supports placeholders listed below, plus — an attribute written on the current chapter's H1 line (# Title ). A missing attribute resolves to an empty string without a warning. A newline — or the two characters \n, written in the template or in an attribute value — always starts a new line, whatever the overflow. Text a placeholder copies from the document (a title, a frontmatter field) prints as written: there only a real line break, such as a title break, starts a new line. |
align | 'left' | 'center' | 'right' | 'justify' | 'start' | 'end' | 'center' | Horizontal alignment of the lines inside the element's box. 'justify' stretches the word spaces of every wrapped line but the last of each paragraph so the line fills the box; the lines beside a drop cap fill the room beside it. With hyphenate, a word that does not fit the rest of a justified line is also cut at a syllable to fill it. The last line of a paragraph, a line with no space to stretch and a text that does not wrap are set flush left. Justified text is laid out paragraph by paragraph, so blank lines between paragraphs count as one. The canvas and the PDF place every word where the layout put it; the HTML widens the spaces with word-spacing. 'start' and 'end' follow the text's direction: the right and the left of a right-to-left text. 'left' and 'right' are the box's own sides; in a design laid out in the flow of a right-to-left page (an opener band, a heading design) the flow is mirrored, so they are its start and end, as in the body text. |
direction | 'ltr' | 'rtl' | 'auto' | the document's | Base direction of the text: where its neutral characters go, the order of its runs on a line and the sides 'start' and 'end' mean. 'auto' reads the first strong letter of the resolved text and falls back to the document's direction. Arabic and Hebrew runs read right to left whatever the base. A text that holds an Arabic letter is never letter-spaced (letterSpacing is ignored for the whole text) and its words are never cut while wrapping or truncating; a word wider than the box overflows and is reported as unbreakableWordOverflow. |
parity | 'all' | 'odd' | 'even' | 'all' | Which pages the element appears on (page number parity: page 1 is odd). |
pages | 'all' | 'body' | 'opener' | 'part' | 'blank' | 'all' | Which page roles the element appears on, combined with parity. After placement every page is classified as 'blank' (parity / separator padding, or no content), 'part' (a part-divider page), 'opener' (its first block is a heading whose level spans the page or forces a page break before it — the first page of a chapter) or 'body' (everything else). pages: 'body' hides a running head on chapter openers; pages: 'opener' shows a folio only there. |
fontFamily | string | 'EB Garamond' | Font family. |
fontSize | Dimension | 8 pt | Font size. |
fontWeight | number | 400 | Font weight (100–900). |
italic | boolean | false | Whether to render in italic. |
color | ColorValue | #000000 | Text colour. |
overflow | 'wrap' | 'ellipsis-start' | 'ellipsis-middle' | 'ellipsis-end' | 'clip' | 'wrap' in heading and part designs, else 'ellipsis-end' | How the engine handles text that exceeds the element's available width. 'wrap' breaks the line into multiple lines; the ellipsis variants keep each line on one line and truncate it with … at the start, middle, or end; 'clip' hard-clips to the element's bounding box without inserting any character. Line breaks in the content apply in every mode: the ellipsis and clip modes truncate or clip each line on its own. An element that leaves overflow out takes its slot's default: in a heading design (advancedDesign.slot) or on a part page (parts.design, parts.versoDesign) it wraps, in a running head, a folio or a contents part row (toc.parts.design, a row of fixed height) it ends in …. Set 'wrap' on a running head that may run to several lines (an address), or an ellipsis on a kicker that must stay on one line in an opener. A line cut by an ellipsis, or clipped with ink past the box, is reported as a designTextTruncated content warning, once per element and page (a running head or a folio once per element and chapter). A configuration stored before postext 1.24 (configVersion 9 or older) is read with 'ellipsis-end' on every heading and part text that sets none, so its pages do not move. A text with a dropCap wraps whatever this says; with 'clip' its lines are still cut at the edge of a box of fixed height. 'ellipsis-end' and 'ellipsis-start' cut at a word boundary — The history of…, not The history of th… — and no space or joining punctuation (comma, colon, dash, slash, opening bracket) touches the ellipsis. A word is cut where it must only when the boundary, that punctuation dropped, would keep less than half of what fits: one long word, or a URL (http://exampl…, not http…). A no-break space or a no-break hyphen (U+2011, as in MS‑DOS) is not a word boundary; 'ellipsis-middle' cuts anywhere but drops the spaces beside it. Up to postext 1.4 every mode cut at the last character that fitted, spaces included. |
verticalAlign | 'top' | 'middle' | 'bottom' | 'middle' | Where the text sits inside a box taller than its lines — a fixed placement.size.height, or a box stretched by an anchored neighbour. When the lines are taller than the box (a large numeral in a box of fixed height, a tight lineHeight), they run out on the side the alignment leaves free, as CSS flex alignment does: 'bottom' keeps the foot of the last line box on the foot of the box and runs out at the top, 'middle' runs out evenly at both ends, 'top' at the foot. Up to postext 1.4 such lines always hung from the top, whatever the alignment. A dropCap moves with its lines (up to postext 1.4 it stayed at the top of a box that 'middle' or 'bottom' moved them down in). |
lineHeight | number | Dimension | 1.2 | Leading of the element's lines. A number is a multiple of fontSize. A Dimension is accepted too, the way every other leading of the configuration is written: em / rem is the same multiple, and an absolute length (pt, mm, px…) is the distance between baselines — sets 15.5 pt leading whatever the size. Anything else (zero, a negative number, a malformed dimension) takes the default. Up to postext 1.4 a Dimension here made the design's height unmeasurable: an opener then reserved no room at all, not even its minHeight, and the body ran under the title. |
letterSpacing | Dimension | 0 | Tracking: extra space advanced after every character, spaces included, exactly as CSS letter-spacing does. Measured widths grow with it, so an auto-width box stays tight. The tracking after the last character of a line is left out of its alignment and of an auto-width box, so a centred tracked title is centred on its letters, a right-aligned one ends on the edge and the last letter of a justified line reaches the edge (up to postext 1.4 they sat half a tracking unit, or a whole one, to the left, and an element anchored to the right of a tracked one stood a tracking unit further off). A negative value tightens the letters — a display title at 36 pt often takes { value: -0.3, unit: 'pt' } — and the widths shrink the same way; canvas, HTML and PDF paint it alike (up to postext 1.4 a negative value was set as 0, with no warning). |
textTransform | 'none' | 'uppercase' | 'none' | Letter-case transform applied to the resolved text, placeholders included — a part title set in capitals in the contents. |
box | ElementBoxStyle | — | Optional background and border drawn behind the text: backgroundColor, borderColor, borderWidth, borderRadius and a per-side padding that grows the box beyond the text (see Box elements for the fields). |
dropCap | { lines, fontFamily, fontWeight, fontSize, color, gap } | — | Drop cap: the first letter set large beside the first lines lines (default 2), in its own face, weight and colour, gap from the text. The letter stands on the baseline of the last line it spans, and fontSize defaults to the size that brings its top level with the capitals of the first line: the text size plus lines − 1 line spacings, capitals taken as 0.72 of a letter's size (a face whose capitals are much taller or shorter than that wants a fontSize of its own). A text with a drop cap wraps whatever its overflow; with 'clip' its lines are still cut at the edge of a box of fixed height. A palette-linked color follows the part and section palettes like the rest of the design. In a heading design the letter reserves no room below the text: the part of its line box under its baseline does not push the body down. A letter that descends below its baseline (a Q or a J in many faces) can then reach into the space under the design: give the heading a marginBottom for it. Up to postext 1.4 the default size made the letter as tall as all the line boxes it spans, so its top stood above the first line; an overflow other than 'wrap' dropped the letter without a warning; a section or part palette left its colour as it was; and a letter as deep as the text beside it could push the body a grid line lower. Configurations stored earlier keep the 1.4 size, written out as fontSize (see Bundles written by postext 1.4 or earlier). A drop cap in the running text is set from the paragraph and heading styles instead (see Drop caps), its cap heights measured from the faces. |
paragraphIndent | Dimension | 0 | First-line indent of every paragraph after the first. A newline in the content — or the two characters \n, for text that comes from an attribute value — separates paragraphs; consecutive newlines count as one. |
hyphenate | boolean | false | When true and the text wraps (overflow: 'wrap', or a dropCap, which always wraps), long words that would still overflow after a regular line break are split at syllable boundaries (using the document's active hyphenation locale) with a soft-hyphen at the break. In a justified text (align: 'justify') a word that does not fit the rest of a line is also cut at its last syllable break that fits, to fill the line. |
inlineMarks | boolean | false | Read the resolved text — placeholder values included — as inline Markdown: bold, italic, ^superscript^, ~subscript~. Off, the markers print as written. In a heading design, then keeps the heading's own bold, italic, superscript and subscript runs (Pneumocystis, CO~2~), its other characters escaped so they print as written (since postext 1.19). See Inline marks and outlines. |
stroke | { width, color?, hollow? } | — | Outline drawn around the letters: width (a Dimension, centred on the glyph edges), color (default: the text colour; a drop cap takes its own colour) and hollow (true paints the outline only). See Inline marks and outlines. |
writingMode | 'horizontal-tb' | 'vertical-rl' | 'horizontal-tb' | 'vertical-rl' sets the text top to bottom, lines right to left, the characters upright: a running head down the fore-edge, a vertical title beside a horizontal chapter. See Vertical text elements. |
reserve | boolean | true | Heading designs only: whether the element counts toward the height the heading reserves in the text flow. false for decoration that may lie under the text (a seal at the page foot, a frame, a side band). See Reserved height. Header, footer and part designs ignore it. |
marginFromBody | Dimension | 6 pt | Absolute distance between the element's body-facing edge and the body edge. Independent of other elements. Migrated to placement.offset.y. |
marginFromEdge | Dimension | 0 pt | Horizontal inset from the aligned content edge. Only applies when align is 'left' or 'right'. Migrated to placement.offset.x. |
placement | ElementPlacement | derived from align + marginFromBody + marginFromEdge | Advanced placement (see below). When set, takes precedence over the legacy flat fields. |
Available placeholders:
{pageNumber}— 1-based page number of the current page.{totalPages}— total page count for the document. In a book laid out chapter by chapter (the Sandbox,buildBundle) each chapter is a document, so this is the chapter's own count.{bookTotalPages}— total page count for the whole book: every chapter, blank pages included. For a document laid out on its own it equals{totalPages}. See Book page count below.{title},{subtitle},{author},{publishDate}— values read fromcontent.metadata. Unknown or empty metadata renders as an empty string (and raises a warning in the sandbox).{chapterTitle}— text of the most recent H1 on or before the current page. An H1 whose heading style setsrunningChapter: false(a plate, a map) is passed over.{chapterTitleAtTop},{chapterNumberAtTop}— title and number of the chapter in force at the top of the page, which differs from{chapterTitle}and{chapterNumber}on a page where a new chapter starts below other text. See Chapter at the top of the page below.{partTitle},{partNumber}— title and number of the current part (the most recent:::partpage on or before the current page; blank parity pages just before a part page already belong to it). Empty before the first part.{firstMark.<key>},{lastMark.<key>}— the first and last guide word of the page: a heading of a level (h1–h6) or an entry of a paragraph style. See Guide words below.
Book page count
{bookTotalPages} prints the number of pages of the whole book, the count a reader sees in "page 12 of 348". It counts physical pages, blank ones included, like {totalPages}, but across every chapter:
- A document laid out on its own (
buildDocumentwithout acontinuation) is the whole book:{bookTotalPages}equals{totalPages}. buildBundlelays the book out, adds up the pages of every chapter and lays it out once more with that total, so every chapter prints the same number. The count never moves a page break, so one more round settles it; a configuration that does not print{bookTotalPages}costs nothing.- The Sandbox hands every chapter the total once every chapter's pages are known. Until then, a chapter prints the pages up to its own end. The PDF tab's whole-book export counts the pages it lays out: when a chapter printed another count, because its pages were not known yet, it lays the book out once more with the count the chapters came to.
- A host laying chapters out itself passes the total as
continuation.bookPageCount(the first chapter too). Without it,{bookTotalPages}counts the pages up to the end of the document (continuation.pageIndexOffsetplus its own pages), which is right for the last chapter only.
footer: {
elements: [{
kind: 'text', id: 'folio', content: '{pageNumber} / {bookTotalPages}',
fontSize: { value: 8, unit: 'pt' },
placement: { anchor: { to: 'container', edge: 'top' }, size: { width: 'auto', height: 'auto' } },
}],
}configUsesPlaceholder(config, 'bookTotalPages') tells a host whether the count is worth computing.
Guide words: first and last mark
A dictionary prints the first and last headword of each page in its running head ("Aback – Anchor"); a reference book prints the first and last section. {firstMark.<key>} and {lastMark.<key>} print them. The key names what marks a page:
h1toh6: a heading of that level. The mark is its text, without the number.- A paragraph style id (
entry): a paragraph of a:::paragraphs{style="entry"}container. The mark is the paragraph's opening bold run, the headword, without its closing punctuation:**Aback.** Said of…marksAback. A paragraph that does not open with bold text sets no mark.
{firstMark.<key>} is the first mark that starts on the page and {lastMark.<key>} the last. A page on which no mark starts (a long entry running on) prints the mark in effect, the last one before it, for both. Pages before the first mark print nothing; in a book laid out chapter by chapter that is the chapter's first mark, since marks do not carry over from one chapter to the next. A heading or paragraph split across pages marks the page it starts on only. The key is written after the dot as letters, digits, _ and -, starting with a letter or _; an unknown key prints nothing.
:::paragraphs{style="entry"}
**Aback.** Said of the sails when pressed back against the mast.
**Abaft.** Towards the stern, or behind a given point.
:::header: {
elements: [{
kind: 'text', id: 'guide', content: '{firstMark.entry} – {lastMark.entry}',
fontSize: { value: 8, unit: 'pt' },
placement: { anchor: { to: 'container', edge: 'bottom' }, size: { width: 'auto', height: 'auto' } },
}],
}Guide words are running heads: they resolve in header and footer slots (a heading style's header and footer included) and print nothing in heading, part and contents designs; in heading and part designs the Checks panel flags them as unknown placeholders. To show the first headword on versos and the last on rectos, give two elements parity: 'even' and parity: 'odd'. An H1 whose heading style sets runningChapter: false sets no h1 mark.
Chapter at the top of the page
{chapterTitle} and {chapterNumber} name the last chapter begun on or before the page. In a book whose chapters run on, without a page break between them, a page that closes one chapter and starts the next near its foot then carries the new chapter's title over text that still belongs to the old one. {chapterTitleAtTop} and {chapterNumberAtTop} name the chapter in force at the top of the page instead, as novels with run-on chapters and many reference books do:
- a page whose first block is a chapter's H1 names that chapter;
- any other page names the chapter it runs on from, even when a new chapter starts lower down;
- a blank page added for parity goes with the page after it, and the separator of the
always-*modes with the page before it (see Blank-page ownership); when the page after a parity blank opens with the end of a chapter, not with its H1, the blank keeps that chapter too; - an H1 with
runningChapter: falseis passed over.
header: {
elements: [{
kind: 'text', id: 'chapter', content: '{chapterTitleAtTop}', parity: 'odd', pages: 'body',
fontSize: { value: 8, unit: 'pt' },
placement: { anchor: { to: 'container', edge: 'bottom-right' }, size: { width: 'auto', height: 'auto' } },
}],
}Like guide words, both are running heads: they resolve in header and footer slots and print nothing in heading, part and contents designs. {attr.<key>} always reads the last chapter begun.
Edge-implied alignment
When a text element's placement.anchor.to references another element by #id, the anchor edge implies a default text alignment for wrapped lines:
right-ofandalign-leftimply textalign: 'left'— wrapped lines flow rightward from the anchor.left-ofandalign-rightimplyalign: 'right'— wrapped lines hug the side closest to the anchor target.
The sandbox heading editor applies these implied alignments automatically when you change the anchor edge or target. They keep multi-line wrapped text visually anchored to the element it relates to (so e.g. the "P" of a wrapped "Postext" lines up vertically under the "I" of "Introduction").
Inline marks and outlines
A text element sets its text in one face by default: **, ^ and the other Markdown markers print as written. With inlineMarks: true the resolved text is read as inline Markdown, the same marks the body text takes (see Document format → Inline formatting):
**bold**sets weight 700 (or the element's ownfontWeightwhen it is heavier);*italic*flips the element's slant, so emphasis inside an italic element comes out upright;***both***does both. The underscore forms (__bold__,_italic_) work too.^superscript^and~subscript~are set at 58% of the size, a superscript raised a third of it and a subscript lowered 0.15 of it; a subscript and a superscript that touch (T~0~^2^) are stacked, as in the body.- A backslash sets a marker character itself (
\*,\_,\^,\~). A link keeps its text; code backticks are dropped.
Marks are read after the placeholders are filled in, so a value can carry them: an author line in a heading attribute gets its affiliation numbers as superscripts. Wrapping, justification, the ellipsis modes, dropCap and paragraphIndent all work on marked text; every run keeps the element's colour.
{ "kind": "text", "id": "authors", "content": "{attr.authors}", "inlineMarks": true, "overflow": "wrap",
"fontSize": { "value": 11, "unit": "pt" },
"placement": { "anchor": { "to": "#title", "edge": "below" }, "offset": { "y": { "value": 6, "unit": "pt" } } } }# Snow cover and river flow {authors="Ana Ruiz^1^, Luis Gil^2^ and Marta Sanz^1,3^"}stroke draws an outline around the letters: width is the line width, centred on the glyph edges (half of it falls inside the letters, half outside; the measured text width does not change), color defaults to the text colour, and hollow: true leaves the letters unfilled so only the outline shows — an outlined display number, a title that stands off a photograph. The outline is painted over the fill, the same way on canvas, in HTML (-webkit-text-stroke) and in the PDF (text render mode 2, or 1 when hollow).
{ "kind": "text", "id": "year", "content": "1863", "fontFamily": "Bitter", "fontSize": { "value": 120, "unit": "pt" }, "fontWeight": 700,
"color": { "hex": "#1d3557", "model": "hex" },
"stroke": { "width": { "value": 1.5, "unit": "pt" }, "hollow": true },
"placement": { "anchor": { "to": "page", "edge": "bottom-right" }, "offset": { "x": { "value": -15, "unit": "mm" }, "y": { "value": -20, "unit": "mm" } } } }In the PDF, bold and italic runs embed the matching faces of the element's family, so the font provider must supply them.
Text defaults and anchoring traps
A text element you write yourself starts from these values, some of which surprise:
overflowfollows the slot. In a heading design or on a part page a long title wraps onto more lines; in a running head, a folio or a contents part row a text too wide for its room is cut to one line with…. Setoverflow: 'wrap'on a running head that may run long (an address, a byline), and look at thedesignTextTruncatedwarnings for the texts that were cut. A line break in the content (a newline, or\nin the template or in an attribute value) starts a new line in every mode; the ellipsis modes cut each line on its own.alignis'center'andverticalAlignis'middle'. An auto-width element shrink-wraps its longest line, so its lines are centred against one another; setalign: 'left'for a flush-left block (an auto-width element anchored to another element aligns its lines on the side of its anchor by itself, see above).- The face is EB Garamond 8 pt, black,
lineHeight1.2, whatever the body text uses. Unlike the other leadings of the configuration, a design text'slineHeightis usually a plain multiple (1.2); aDimensionworks too (see above).
An element with no placement.size.width (or 'auto') sizes itself to its text, but only within the room between its anchor point and the edge of the container it grows toward — for a top or bottom anchor, twice the distance to the nearer edge. Its offset counts. An offset away from that edge costs nothing: a running head anchored top-left with a negative x (hanging into the left margin) loses no room, because it grows to the right. An offset toward that edge shrinks the room by as much (twice as much for a top or bottom anchor), and one that pushes the anchor past the edge leaves none: a top-right anchor whose x is below minus the container width, or a top anchor moved sideways by more than half that width. With no room left, an ellipsis mode prints nothing and 'wrap' stacks one character per line. Three ways out:
- give the element a fixed
size.width— fixed widths are never clamped; - anchor it to
'page'or'bleed', which makes the page (or bleed) frame its room; - anchor it to the opposite edge of the container.
The container of a header runs from the trim top down to the body, and that of a footer from the body down to the trim bottom: in a header, top-* anchors measure from the trim edge and bottom-* anchors from the body, and the other way round in a footer (see Container frame above). A header or footer never moves the body text, and it paints on top of it, so an element pushed into the body area covers the text. An opener band, by contrast, paints under the body text; how much room it takes in the flow is described under Reserved height.
#Vertical text elements
A text element with writingMode: 'vertical-rl' is set vertically in a slot whose text is horizontal: the running heads and folios of any book, which stay on the sheet, and every design of a horizontal page. It is laid out as a horizontal text in its own frame turned a quarter turn clockwise, and turned back onto the page:
- Its box stays where the placement puts it. Its height is the length of a line:
size.heightsets it (or'auto', the text's length;'fill', down to the container's edge),size.widthsets how many lines fit across, andsize.maxWidthcaps a line's length. alignplaces the lines along the box ('left'at the top),verticalAlignacross it ('top'at the right, where the first line stands), and a box's padding stays on the side it is written for.- The characters are measured and painted as in a vertical page: Han upright one em each, punctuation in its vertical form, Latin words sideways, short numbers in one cell (
cjk.uprightDigits). The canvas, the PDF and the HTML place it at the same rectangle. - With
inlineMarks: truethe orientation marks set a run apart as in the body:第:tcy[3.0]回sets 3.0 in one cell,:upright[GDP]stands the letters upright one under the other,:sideways[…]turns a run; a line never breaks inside one. A horizontal element ignores them. - A drop cap is not set on a vertical element.
- In the flow of a vertical page (an opener, a part page, a box title) text already runs down, and
writingModechanges nothing there.
Vertical Chinese books put their running heads and folios in one of three places (clreq §7.2; JLREQ §2.6 for Japanese):
| Convention | Where | How to set it |
|---|---|---|
| Horizontal head and foot | Above and under the type area, as in horizontal books; the most common. | The header and footer as they are. |
| Fore-edge (中缝 style, Taiwan 邊峰) | Down the outer margin: the chapter or book title from about four characters below the head of the type area, the folio ending about five above its foot, in Chinese numerals, at about 80 % of the body size. | Two vertical elements anchored to 'outer', below. |
| Outer foot corner | The folio at the foot of the page, in the outer corner (Taiwan's rules for 中式 books). | A horizontal element in the footer at 'bottom-left' with parity: 'odd' and one at 'bottom-right' with parity: 'even' in a right-bound book (the other way round in a left-bound one). |
The fore-edge heads, as the Sandbox adds them (Header › Fore-edge heads (vertical)), here for a 10 pt body (the Sandbox sets them at 80 % of the body size):
{
"page": { "pageNumbering": { "format": "trad-chinese-informal" } },
"header": { "elements": [
{ "kind": "text", "id": "head", "content": "{chapterTitle}", "writingMode": "vertical-rl",
"fontSize": { "value": 8, "unit": "pt" }, "overflow": "clip", "align": "left",
"placement": { "anchor": { "to": "outer", "edge": "top" }, "offset": { "y": { "value": 4, "unit": "em" } } } },
{ "kind": "text", "id": "folio", "content": "{pageNumber}", "writingMode": "vertical-rl",
"fontSize": { "value": 8, "unit": "pt" }, "overflow": "clip", "align": "left",
"placement": { "anchor": { "to": "outer", "edge": "bottom" }, "offset": { "y": { "value": -5, "unit": "em" } } } }
] }
}anchor.to: 'outer' (see Element placement) is the outer margin of each page, so the two elements run down the left edge of a recto and the right edge of a verso in a right-bound book (page.binding). {pageNumber} prints in the page numbering format: trad-chinese-informal gives 一百零三 on page 103, cjk-decimal 一〇三. The per-slot rules still apply: pages: 'body' keeps a head off the chapter openers. A short ornament between head and folio (a fish-tail ︻, a rule) is an ordinary element anchored to the same frame.
In the VDT a vertical block carries vertical (VDTDesignTextBlock.vertical: the region, the upright digits and the central axis of each family); its lines are in the block's own turned frame, xOffset down from the box's top, baselineY leftward from its right edge. A run of a marked line carries tcy or orientation, as a segment of the body does.
#Rule elements
Rule elements render a line: horizontal across the slot, or vertical down it.
| Property | Type | Default | Description |
|---|---|---|---|
kind | 'rule' | — | Discriminator. |
id | string | — | Stable id, unique within the slot, for anchor.to: '#id' references. |
direction | 'horizontal' | 'vertical' | 'horizontal' | A horizontal rule runs along placement.size.width ('fill' = to the container edge) and is thickness tall. A vertical rule runs down placement.size.height ('fill' or unset = to the container edge) and is thickness wide — a divider between a running head and a folio. |
color | ColorValue | #000000 | Stroke colour. |
thickness | Dimension | 0.5 pt | Line thickness. A rule that leaves it out is drawn at the default (up to postext 1.4 it painted nothing). |
width | Dimension | 'full' | 'full' | 'full' spans the content area; a Dimension constrains the line to a fixed length positioned by align. |
align | 'left' | 'center' | 'right' | 'center' | Alignment when width is not 'full'. |
marginFromBody | Dimension | 6 pt | Absolute distance between the rule's body-facing edge and the body edge. Independent of other elements. |
marginFromEdge | Dimension | 0 pt | Horizontal inset from the aligned content edge. Only applies when width is a fixed Dimension and align is 'left' or 'right'. |
parity | 'all' | 'odd' | 'even' | 'all' | Which pages the rule appears on. |
pages | 'all' | 'body' | 'opener' | 'part' | 'blank' | 'all' | Page roles the rule appears on (see the text-element pages field). |
reserve | boolean | true | Heading designs only: whether the rule counts toward the height the heading reserves in the text flow (see Reserved height). |
placement | ElementPlacement | derived from align + marginFromBody + marginFromEdge | Advanced placement (see Element placement). size.width / size.height set the rule length; width: 'fill' is the legacy 'full'. |
#Box elements
Box elements paint a rounded rectangle inside the slot — useful as a backdrop behind text in chapter openers, sidebars or footers. Box elements are positioned exclusively through the placement field; they have no legacy flat shorthand. Fill, stroke and corner radius live in the nested style object (ElementBoxStyle), as in the JSON example under "Element placement".
| Property | Type | Default | Description |
|---|---|---|---|
kind | 'box' | — | Discriminator. |
id | string | — | Stable id, unique within the slot. Sibling elements anchor to it with anchor.to: '#id'. The sandbox assigns one on creation. |
style.backgroundColor | ColorValue | transparent | Fill colour. Set to transparent for an outline-only box. |
style.borderColor | ColorValue | transparent | Stroke colour. |
style.borderWidth | Dimension | 0 pt | Stroke width. Strokes are painted on the inside of the box's bounding rectangle so the outer dimensions stay constant: the outer edge of the stroke runs along the box edge, and a rounded box keeps its outer radius. A stroke as wide as the box fills it. Canvas, HTML and PDF draw it alike (up to postext 1.4 the canvas and the PDF centred the stroke on the edge, half of it outside the box). |
style.borderRadius | Dimension | 0 pt | Corner radius. Clamped to half the smaller side at render time. |
placement | ElementPlacement | — | Required. See "Element placement" below. |
parity | 'all' | 'odd' | 'even' | 'all' | Which pages the box appears on. |
pages | 'all' | 'body' | 'opener' | 'part' | 'blank' | 'all' | Page roles the box appears on (see the text-element pages field). |
reserve | boolean | true | Heading designs only: whether the box counts toward the height the heading reserves in the text flow (see Reserved height). |
#Image elements
An image element draws a bitmap or SVG resource of the document — a publisher's logo on a title page, a mark in a running head. It is sized by its placement.size: with one of width / height left 'auto' (the default) the other side follows the image's aspect ratio; with both set the image is fitted inside the box and centred. A missing or non-image resource draws nothing.
{
kind: 'image', id: 'logo', resourceId: 'logo-publisher',
placement: { anchor: { to: 'page', edge: 'top-left' }, offset: { x: { value: 64, unit: 'mm' }, y: { value: 233, unit: 'mm' } }, size: { width: { value: 83, unit: 'mm' }, height: 'auto' } },
}resourceId takes the placeholders a text element's content takes, so one design can draw a different picture for each heading. With resourceId: '{attr.vignette}' in a heading style, # Chapter I {style="opener" vignette="log"} draws the resource log and # Chapter II {style="opener" vignette="wig"} draws wig: the chapters share the style instead of one clone per picture. A header or footer reads the attributes of the page's chapter, as {attr.<key>} does in a running head, and other placeholders work too ('map-{chapterNumber}'). An id that comes out empty, such as a heading without the attribute, draws nothing. Since postext 1.8.
| Property | Type | Default | Description |
|---|---|---|---|
id | string | — | Stable identifier; other elements may anchor to it as #id. |
resourceId | string | — | Id of a bitmap or SVG Resource of the document. It may hold placeholders, {attr.<key>} among them, filled in per heading, part or page. |
decorative | boolean | false | The picture is decoration only (an ornament, a band): it gives no alternative text to the output even when its resource has one (see below). |
placement | ElementPlacement | — | Anchor, offset and size (see Element placement). A 'fill' side runs to the container edge. |
parity, pages | as above | 'all' | Which pages the image appears on. |
reserve | boolean | true | Heading designs only: whether the image counts toward the height the heading reserves in the text flow (see Reserved height). |
The PDF backend embeds the resource like a figure (an SVG with a print master uses it); the HTML viewer resolves it through resourceImageUrl.
A picture a design draws is content when its resource describes it: the resource's altText, else its caption as plain text (a chip read by its label, a :ref by its text when it has one), goes into the VDT (VDTDesignImageBlock.altText), becomes the alt of its <img> in HTML and a Figure with /Alt in a tagged PDF, read right after the text of its design (a chapter's plate after the chapter's heading). A picture whose resource has neither, and one marked decorative, is decoration: alt="" with role="presentation", and an artifact in the PDF. A running head's or footer's picture repeats on every page, so it is furniture whatever its resource says: no altText in the VDT, alt="" with role="presentation" in HTML, an artifact of the page in the PDF.
#Element placement
ElementPlacement is the unified positioning model used by every element type (text, rule, box) inside any design slot — page header, page footer, or heading-level advanced design slot. Three pieces of state describe a placement:
interface ElementPlacement {
/** What this element anchors to and which edge of that target. */
anchor: {
to: 'container' | 'page' | 'bleed' | 'outer' | `#${string}`; // container = the slot; page = trim box; bleed = trim box + bleed; outer = the outer margin (header, footer); #id = another element
edge: AnchorEdge;
};
/** Distance from the anchor point. */
offset?: { x?: Dimension; y?: Dimension };
/** Optional fixed width / height. Width also accepts 'fill' (span the slot).
* `maxWidth` caps an 'auto' width (text): the element still shrink-wraps its
* content, so elements anchored to it stay attached, but a long text wraps or
* ellipsizes there — a running head can reserve room for the label hanging
* off it instead of squeezing that label out. */
size?: { width?: Dimension | 'fill' | 'auto'; height?: Dimension | 'fill' | 'auto'; maxWidth?: Dimension };
}AnchorEdge accepts:
- Container edges (when
anchor.tois'container','page'or'bleed'):top,top-left,top-right,bottom,bottom-left,bottom-right,left,right. - Element-relative edges (when
anchor.to === '#someId'):right-of,left-of,below,above,align-top,align-bottom,align-left,align-right.
Each element-relative edge puts one corner of the element on a corner of the element it anchors to, and offset moves it from there:
right-of: its top-left corner on the target's top-right corner (beside it, tops level);left-of: its top-right corner on the target's top-left corner;below: its top-left corner on the target's bottom-left corner (under it, left edges level);above: its bottom-left corner on the target's top-left corner;align-topandalign-left: its top-left corner on the target's top-left corner. The two names give the same placement: both the top and the left edges line up;align-bottom: its bottom-left corner on the target's bottom-left corner;align-right: its top-right corner on the target's top-right corner.
An element-relative edge used with 'container', 'page' or 'bleed', and a container edge used with '#id', are read as the top-left corner.
anchor.to: 'page' anchors the element to the trim box (the physical page after cutting) and 'bleed' to the trim box grown by cutLines.bleed on every side (identical to the trim box while cut lines are disabled). Both frames also become the reference for size: 'fill' and for the automatic width clamp, so a coloured band can run edge to edge regardless of the page margins:
{ "kind": "box", "id": "band", "placement": { "anchor": { "to": "bleed", "edge": "top-left" }, "size": { "width": "fill", "height": { "value": 6, "unit": "cm" } } }, "style": { "backgroundColor": { "hex": "#1d3557", "model": "hex" } } }With cut lines on, whatever an element paints past the bleed box is clipped (see Cut lines).
anchor.to: 'outer' (header and footer slots) anchors the element to the page's outer margin: from the edge of the type area to the trim edge on the side away from the spine, and from the head of the type area to its foot. It is on the right of a recto and the left of a verso in a left-bound book and the other way round in a right-bound one (page.binding), so one element serves both pages of a spread: a running head down the fore-edge (see Vertical text elements). In any other slot it reads as 'container'. A text element's offset may be written in em, ems of its own fontSize: four characters below the head of the type area is { "y": { "value": 4, "unit": "em" } }.
Inside a heading's advanced-design slot, page- and bleed-anchored elements do not grow the height reserved for the heading unless they extend below the heading's top edge (a band across the top of the page sits behind the opener; a band reaching below the heading pushes the body text down). Use advancedDesign.minHeight to reserve a fixed opener height regardless, and reserve: false on an element that should not push the text at all. The full rules are under Reserved height.
Each element has a stable id (auto-assigned by the sandbox; you can also set it by hand). Elements anchored to other elements form a small dependency graph that the engine resolves before measuring, so an element can chain off another without manual coordinates.
The legacy align + marginFromBody + marginFromEdge shape is parsed on input and rewritten into a placement at config-resolution time, so existing configs keep working unchanged.