HTML Templates
HTML is the most flexible format in Velkin. Templates render through headless Chromium, so anything a modern browser supports works here — flexbox, grid, SVG, web fonts, and CSS print rules.
| Asset extension | .html, .htm |
| Engine | Handlebars.js 4.7.8 (goja VM) |
| Renderer | Chromium (chromedp) → PDF |
| Endpoints | render-pdf, preview, preview-html |
Data binding
The data you POST (or the report's sampleData when you omit it) is the
Handlebars context:
By default Handlebars HTML-escapes values. Use triple-stache {{{rawHtml}}}
only for values you trust — template authors are trusted, but the data may
not be.
Reusable logic with helpers.hbs
The report's helpers.hbs is JavaScript, executed before each template
compiles. Register helpers and partials with the standard Handlebars.js API:
Handlebars.registerHelper('money', (n) =>
new Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR' }).format(n),
);
Handlebars.registerPartial(
'row',
'<tr><td>{{description}}</td><td>{{money amount}}</td></tr>',
);
Inline partials ({{#*inline}}) work too, but they belong inside the
template, not in helpers.hbs (which is JavaScript). See
Helpers & Partials for the full picture, including the
goja sandbox and its limits.
Page size & print options
Page geometry comes from options.json (outputOptions.pdf), not from the
template. The renderer passes these to Chromium's print engine:
| Option | Effect |
|---|---|
format | Paper size: A0–A6, B4, B5, LETTER, LEGAL, TABLOID, LEDGER. |
landscape | Rotate to landscape. |
printBackground | Include background colors and images (off → backgrounds drop out). |
preferCSSPageSize | When true, a CSS @page { size: … } rule wins over format. |
Print CSS
Because rendering is real Chromium print, standard print CSS applies:
/* Force a page break before each section */
.section { break-before: page; }
/* Avoid breaking a row across pages */
tr { break-inside: avoid; }
/* Table headers repeat on every page automatically */
thead { display: table-header-group; }
/* Page size via CSS (needs preferCSSPageSize: true) */
@page { size: A4; margin: 18mm; }
Velkin does not set print margins or inject header/footer templates — it
only passes paper size, orientation, background, and preferCSSPageSize to
Chromium. To control margins, use CSS @page { margin } (with
preferCSSPageSize: true) or simply pad the <body>. Repeating headers/footers
rely on CSS print techniques (e.g. position: fixed elements repeat on each
page, thead repeats in tables) — there is no separate header/footer option.
Embedded and remote assets
Images, fonts, and stylesheets are fetched by Chromium while the page loads. Two ways to include them:
- Inline as data URIs (
data:image/png;base64,…) — always allowed, fully self-contained, and immune to network policy. Best for logos and small assets. - Remote
http(s)URLs — fetched through Velkin's SSRF filter. Public hosts are allowed; private/loopback/link-local addresses are blocked by default (see Security model).file:,chrome:and similar local schemes are always blocked.
JavaScript is off by default
The PDF is captured after the DOM is ready, so to keep untrusted templates from mutating the page or exfiltrating data, JavaScript execution is disabled by default. Static HTML/CSS renders fine without it.
If your templates need a client-side chart library (Chart.js, D3, …) and you
trust their source, an operator can enable scripts with PDF_ALLOW_JAVASCRIPT=true.
See Installation.