Shojiku template reference
This page renders docs/engine/README.md.Generated here: the tables and the page index below.
The complete authorable surface of the Shojiku engine, one page per feature (MDN-style): what you can write in templates.yml, what each key means, its defaults, and the diagnostics it can produce. This is the human- and machine-readable source for "what syntax exists"; keep it accurate against the code (see the curation rules in ../README.md).
These pages are on the MCP wire. An agent talking to shojiku-mcp calls list_reference for this index and get_reference — or resources/read on shojiku://reference/<page> — for a page, which answers the markdown you are reading beside that page's keys as a JSON Schema fragment. Eleven pages, this index among them, document keys the catalog names no shape for; their schema half is an empty $defs, which is itself the answer — the prose half carries those keys. Append #<key> for that key on every shape of the page carrying it. So an agent with no checkout reads the reference itself, not only the bundled examples and validate's diagnostics.
Feature availability per engine build is machine-checkable: shojiku capabilities prints the key list, and each page notes its capability keys. Template authors targeting one engine build can ignore them — they exist so GUIs/SDKs can gate features across engine versions.
The development-facing companion features.md carries the implemented-capability inventory and the decision log — that a feature exists and why it is shaped that way; the pages here carry only how to author it. It lives in the repository only and is not part of this rendered reference; the reader-facing tour of what the engine does is the site's own Features page.
A minimal template
page: { size: A4, margin: 25 }
sections:
body:
type: flow
items:
- type: text
text: "Hello {customer.name}"Full file structure (bands, bodies, styles registry): template.md.
Rendering a template
# PDF (the core command)
shojiku render --templates templates.yml --params params.json \
--definitions definitions.yml --output out.pdf # --output - writes to stdout
# Static + data checks, diagnostics as JSON (definitions/params optional)
shojiku validate --templates templates.yml --params params.json
# Per-page preview PNGs ({page} is replaced by the 1-based page number)
shojiku preview --templates templates.yml --params params.json \
--output "page-{page}.png" --scale 2.0 # 2.0 px/pt ≈ 144 dpi
# The resolved layout tree + box index as JSON (GUI/AI surface)
shojiku inspect --templates templates.yml --params params.json
# Which display variants each field type can take, and what each RENDERS
shojiku formats --templates templates.yml --lang ja-JP
# --probe date:'yyyy年M月d日' previews a pattern before you author it
# This build's feature keys (no inputs needed)
shojiku capabilitiesUseful defaults on render / preview / inspect:
--lang <id>selects the locale (default: the templatedefaults.locale, thenja-JP);--locale-dir/--font-dir(repeatable) locate the packs, adding to$SHOJIKU_LOCALE_DIR/$SHOJIKU_FONT_DIRthen./packs/{locale,fonts}. See fonts.md.--font-pack <id>(repeatable) loads a font pack in addition to the locale's ownfonts.uses— how a pack made byshojiku font addis used without rewriting the locale. See fonts.md.- A font pack whose faces are pinned (
sha256+url:) but absent is downloaded into$SHOJIKU_CACHE_DIRbefore rendering;--offlinerefuses instead, and--font-fetch-allow <host>trusts an extra source. Rendering itself never uses the network. See fonts.md. --assets-diris what imagesrc:paths resolve against (default: the template file's directory). Asset policy:--asset-mode open|bundled-only,--allow/deny-dynamic-image <id>.- Diagnostics print to stderr;
validateexits non-zero on errors.
Try the bundled example:
shojiku render --templates examples/business/receipt-ja/templates.yml \
--params examples/business/receipt-ja/params.json \
--definitions examples/business/receipt-ja/definitions.yml --output receipt.pdf
# or via the Docker image (`make docker:build` builds the local
# `shojiku-ci:local` tag; its default command renders exactly this example):
docker run --rm shojiku-ci:local > receipt.pdf
# your own files: mount them and pass normal CLI arguments
docker run --rm -v "$PWD:/work" shojiku-ci:local render \
--templates /work/templates.yml --params /work/params.json \
--output /work/out.pdfGetting the binaries in the first place (source build or Docker, plus MCP-server registration for AI agents) is covered in the quickstart.
Item types
Every items: entry is a map with a type:. Where an item may appear depends on which part of the page holds it. A page (template.md) is made of optional bands — header and footer, repeated at the top/bottom of every page — and one body between them, which is either type: flow (items stack top-to-bottom and paginate onto new pages as they run out of room — the usual choice, flow.md) or type: absolute (every item pinned at its own box.x/box.y, single page). The placement column below uses:
- F — in a flow body
- A — in an absolute body
- B — in a band (header/footer)
- C — inside a
containeritem, wherever that container sits - cell — inside a
repeatcell /repeat_flowcard
type: | What it draws | Allowed placement | Page |
|---|---|---|---|
text | static / interpolated / bound text; rich spans: | F A B C cell | text.md |
rect | rectangle (border/fill) | F A B C cell | rect.md |
line | stroked segment; Length endpoints (to: { x: "100%" }) or an anchor to another item (to: { item: total }) | F A B C cell | line.md |
image | PNG/JPEG/GIF/WebP/SVG asset | F A B C cell | image.md |
qr_code | layout-time vector QR | F A B C cell | qr_code.md |
list | one line per array entry + overflow clamp | F A B C cell | list.md |
container | nestable box: origin, size, style cascade | F A B C cell | container.md |
table | paginating data-driven rows; a column binds a value or hosts a cell: sub-template; box: narrows it in flow or places it as one bounded block elsewhere | F A B C (not cell) | table.md |
repeat | imposition / n-up grid of data-scoped cells | F only | repeat.md |
repeat_flow | flowing card list, one card per element | F only | repeat_flow.md |
page_break | start a fresh page | F only | page_break.md |
char_grid | manuscript-paper / workbook character cells (+ruby) | F A B C cell | char_grid.md |
ellipse | box-inscribed oval, or anchor: to circle another item's glyph band; circled-option mark or decoration | F A B C cell | form_marks.md |
checkbox | always-drawn frame + params-driven check | F A B C cell | form_marks.md |
page_number | {page} / {pages} | B only | page_number.md |
Every type in this table also takes visible:, which binds whether the item is shown to a params field — reserving its box by default, or removing it from layout with collapse: true. See visible.md.
Disallowed placements warn and skip (never a hard failure) — codes in diagnostics.md.
Concepts
| Page | Covers |
|---|---|
| template.md | file structure: page / styles / defaults / formats / sections, bands, bodies, common item keys |
| defaults.md | document presentation defaults: root style (rem root), per-type format defaults, the formats: registry |
| page.md | page size, orientation, margin; the margin box as coordinate origin |
| document.md | document: metadata: title / description / keywords / language / authors → the PDF's document properties (PDF only) |
| length.md | units: pt, %, mm/cm/in, em/rem; round-trip; guards |
| box.md | box: geometry — x/y/w/h, margin/padding, minWidth/maxWidth/minHeight/maxHeight |
| flex.md | default child placement: direction, gap, alignItems, justifyContent, auto margins |
| grid.md | box.type: grid — column/row tracks, fill order |
| flow.md | stacking, gap, pagination, what splits and what moves whole |
| style.md | every style property, the cascade, named styles, box decoration |
| text.md | wrapping, kinsoku, textOverflow, long-text pagination |
| vertical_text.md | writingMode: vertical_rl / textOrientation — vertical text (plain, spans, list, table cells, page_number) |
| link.md | link: { url } hyperlinks on text/image/spans → PDF annotations |
| data-binding.md | data: bindings, {key:format} interpolation, bindings: named declarations, params, format types |
| visible.md | visible: — show an item only for some data; the reserve-box default and the collapse: true opt-in |
| definitions.md | definitions.yml: the OpenAPI-shaped schema (properties/items, format hints, constraints, display variants, params validation) |
| fonts.md | locales, lang packs, valid fontFamily face ids |
| layout-model.md | the resolve invariant, box tree, caps, box index |
| diagnostics.md | every diagnostic code by stage |
The box: keys at a glance
| Key | Meaning | Page |
|---|---|---|
x y | offset from the parent origin (authoring either opts a container child out of flex) | box.md |
w h | border-box size; omitted = fill width / auto height | box.md |
minWidth maxWidth minHeight maxHeight | CSS-order size clamps | box.md |
margin | outer spacing; per-side map; auto sides | box.md |
padding | inner spacing (non-negative) | box.md |
type | child layout mode: flex (default) | grid | flex.md / grid.md |
direction gap alignItems justifyContent | flex keys (grid reuses some) | flex.md |
flexGrow | child's weighted share of leftover row width | flex.md |
columns rows columnGap rowGap | grid tracks & gaps | grid.md |
columnSpan rowSpan | grid child's track span (≥ 1) | grid.md |
Style properties at a glance
Inherited: fontSize fontFamily fontWeight fontStyleletterSpacing lineHeight color textAlign lineBreaktextSpacingTrim hangingPunctuation writingMode textOrientationtextCombineUpright. Not inherited: verticalAlign backgroundColor borderWidthborderColor borderStyle borderRadius textOverflow overflowtextDecorationopacity. Full table with defaults and value sets: style.md.
Not supported yet
The cross-cutting list, gathered from the per-feature pages. Where a diagnostic reports the restriction, the entry names its code, so the claim is checkable against diagnostics.md; a structural limit that nothing reports carries a dash instead. A restriction stated here is stated on its own page's Limitations section too.
| Not supported | Reported as |
|---|---|
A table inside a repeat cell, a repeat_flow card, or a table column's cell: | table_in_cell |
Body-cell spanning (colspan). headerGroups spans the header row only | header_group_span_clamped |
repeat / repeat_flow / page_break outside a flow body | repeat_in_band, repeat_flow_in_container, page_break_in_absolute_body, … |
page_number outside a band | page_number_in_body, page_number_in_container |
A text mark: in vertical writing | vertical_text_unsupported |
textOverflow: shrink / ellipsis on a rich spans block | span_overflow_unsupported |
Per-corner border radii; any radius on a per-side or double border, a table, or a form mark | border_radius_ignored |
| SVG constructs outside the subset parser | svg_unsupported |
| Remote image sources — the render path has no network I/O, by design | remote_asset_unsupported |
| Flex wrapping: a row is one line | flex_row_overflow |
Justified text and hyphenation (textAlign is left/center/right) | — |
| Barcode symbologies other than QR | — |
| Arithmetic in bindings: totals and tax are computed by the host | — |
Per-section page geometry: one page block per document | — |
Reading order for new authors
- template.md — the file skeleton
- box.md + length.md — placing things
- style.md + fonts.md — making them look right
- flow.md + table.md — variable-length content
- data-binding.md — wiring in params
AI agents authoring a template end-to-end (three files → validate → preview loop) should also load the step-by-step playbook in skills/shojiku-template-author/ (AI-only — written as instructions to the agent).
Runnable examples live in the repository source (examples/ at the repo root); the snippets on each feature page are the self-contained fallback for a reader who has only these pages. An agent on the MCP wire is not such a reader — list_examples / get_example serve the full entries, the same way list_reference / get_reference serve this reference. Each example directory commits its rendered output (output.pdf + preview-<n>.png) next to the sources, so you can see what a template produces without rendering anything; make examples:render regenerates them all. The set (gallery order and one-line pitches: README.md § Gallery): examples/business/invoice-ja (a multi-page A4 invoice: paginating table with repeating header, pre-computed totals, QR + link; params-short.json renders the single-page variant), examples/business/estimate-ja (the invoice's sibling: single-rate one-pager, estimate-terms box, a negative discount row), examples/business/delivery-note-ja (a delivery note: headerGroups spanning band, data-driven row.conditionalStyles, a receipt-stamp field — partial ↔ complete delivery as two params files), examples/business/pickup-slip-ja (the Thinreports-migration worked example — the migration walkthrough's result, with the legacy .tlf + Ruby host beside it), examples/forms/application-form-ja (an A4 application form: form marks, 〒 entry cells, wareki placeholder — a blank ↔ filled-sample params pair), examples/business/event-tickets-ja (2×4 n-up event tickets with per-element QR; params-few.json = one sheet), examples/business/catalog-ja (a product catalog: repeat_flow variable-height cards, dynamic images, fit: cover), examples/business/shipping-labels-ja (2×3 n-up shipping labels: 〒 cells, list + an overflow-count line, per-order QR), examples/forms/certificate-ja (an A4 landscape certificate: double border, mincho + letter spacing, seal/medal SVGs, wareki), examples/typography/kokugo-print-ja (a kokugo worksheet: kanji practice cells + ruby, a vertical copying grid, answer cells), examples/typography/novel-ja (a B5 vertical paperback booklet: ruby-paginating vertical body, strict kinsoku + hanging punctuation, a tate-chu-yoko colophon, vertical page numbers), examples/business/restaurant-menu-us (a US Japanese-restaurant menu: English menu + USD prices with vertical Japanese accents — writingMode: vertical_rl brand column + per-dish dish names, double border, mincho), examples/business/invoice-en (Letter US-style invoice: USD cents, plural-aware quantities, Net-30 terms block), examples/forms/certificate-en (Letter-landscape certificate: real italics, en-US dates), examples/business/receipt-ja (an A4 receipt: containers, %, named styles), examples/business/receipt-us (80mm thermal, custom page size), examples/business/receipt-zh-tw / examples/business/receipt-zh-cn (the same receipt geometry under zh locale packs), examples/business/receipt-hi-in (Devanagari conjuncts + lakh/crore digit grouping) / examples/business/receipt-fil-ph (Latin face + the Philippine peso) / examples/business/receipt-th-th (Thai wrapped at word boundaries, dated in the Buddhist era), examples/typography/genkoyoshi-ja (a B5 vertical 200-cell genkoyoshi: char_grid + aozora ruby) and its horizontal twin examples/typography/genkoyoshi-yoko-ja, examples/forms/rirekisho-ja (an A3 landscape JIS-style rirekisho: custom page size, 2-column header, full-width tables), and examples/dev/layout-showcase — the component-showcase document (rich spans, hyperlinks, flex/grid, overflow policies, zebra table, list, QR, SVG gradient, repeat_flow, repeat imposition starting in place with trim guides and a document-scoped cell value (breakBefore: auto, cutMarks, scope: document), page_break), one labeled section per feature family, each demo followed by a code panel showing the YAML that produces it. The showcase is the visual index of the engine, Bootstrap-docs style: its rendered pages show the look and the syntax side by side; open the feature's reference page for the full key table. It grows with the engine — every new authorable feature adds a showcase section (demo + code panel) in the cycle that ships it.
Every page
| Page | Covers |
|---|---|
box — position, size, spacing, bounds | Position, size, margin, padding and min/max bounds — the border-box every item is placed by. |
char_grid — manuscript-paper / workbook / form character cells | One character per cell: manuscript paper, practice sheets, and form entry boxes. |
type: container | An origin and a resolved size: children position, resolve %, and inherit against it. |
| Data binding & formatting | How templates bind runtime params and how the locale pack formats them for display. |
| Template defaults & the format registry | Document-wide presentation defaults and the named format registry — the CSS :root analog. |
definitions.yml — the data dictionary | The data dictionary: the engineer-to-author seam that enriches validation and formatting. |
| Diagnostics reference | The complete registry of every code the engine can emit, with severity and meaning. |
document: — document metadata | Document metadata written into the PDF's properties: title, description, keywords, language, authors. |
Flex layout (box.type: flex, the default) | The default container mode: CSS-flex semantics down to the defaults, keyed on box.type. |
| Flow — stacking & pagination | A flow body stacks items top-down and paginates when content passes the region bottom. |
| Locales & fonts (packs) | Locale packs and font packs: where formatting data and typefaces come from. |
type: ellipse / type: checkbox — form marks | Choice marks drawn as vector paths — circling a printed option, or a checkbox. |
Static grid (box.type: grid) | Explicit column tracks — fr weights and auto sizing — instead of a flex stack. |
type: image | A raster or SVG image from a template-time source or a params-bound value. |
| The layout model | How a template plus params becomes the resolved layout tree every backend draws. |
| Lengths & units | Every geometry value: absolute units, %, em/rem, and where each has no basis. |
type: line | A stroked segment between two points — no box, its own style shape. |
link: — hyperlinks | A clickable URL emitted as a PDF link annotation over the item's drawn geometry. |
type: list | A bounded per-element list: one entry per line, clamped with an overflow line. |
page — size, orientation, margin | Sheet geometry: paper size, orientation, and the margin box every coordinate resolves against. |
type: page_break | An explicit break: the next flow item starts on a fresh page. |
type: page_number | The current page number — band-only, because the count is known at assembly. |
type: qr_code | A QR code encoded at layout time into vector modules — static text or a bound value. |
type: rect | A rectangle: pure decoration painted by the unified style properties. |
type: repeat — imposition / n-up | Imposition / n-up: N data-scoped copies of one cell laid onto each page. |
type: repeat_flow — flow repeat (card list) | One auto-height card per array element, in normal flow — a vertical card list. |
style — appearance properties & the cascade | Every appearance property and the three-surface cascade that resolves them per item. |
type: table | A data-driven table: bound or container columns, spanning headers, conditional rows, row-by-row pagination. |
| Template file structure | The file's own shape: top-level keys, the header/body/footer sections, and what every item shares. |
type: text | Static, interpolated, or bound text — inline spans, ruby, wrapping, and overflow. |
| Vertical writing | Vertical writing: characters fill a column top-to-bottom, columns lay out right-to-left. |
visible: — show an item only for some data | Show an item only for some data: the form-mark presence predicate on any item, reserving its box or removing it from layout. |