Making a value fit the space you left for it
A placeholder may carry a formatting code after :::. It is
always optional and only changes how the value is printed — never the
value in your JSON.
| Write | What it does |
|---|---|
{{ contact.phone:::l:80 }} |
At most 80 characters. Cuts at the last whole word and adds an ellipsis, which counts towards the limit — so the result never exceeds the room you measured. |
{{ address:::one }} |
Collapses line breaks and runs of spaces into single spaces. For a three-line address that has to sit on one line. |
{{ iban:::wrap:20 }} |
Lets a long unbroken string — an IBAN, a URL, a product code — break across lines instead of running out of its column. Invisible: it adds break opportunities, not characters. |
{% for line in invoice.lines:::max:50 %} |
On a loop: take at most 50 items. A table that receives five thousand rows becomes two hundred pages otherwise. |
Codes may be chained:
{{ notes:::one:::l:120 }}. A code we do not recognise is
ignored and the value is printed unchanged, with a warning on the generation —
a misspelled code costs you the formatting, never the document.
There are no bold, italic or underline codes. Make the placeholder itself bold in Word and the value comes out bold — the editor already does this, and a code would only duplicate it.
Number and date formatting: values are printed exactly as
received. If you need 1,234.56 € or 10/09/2026,
format them in the system that builds the JSON and send strings. Dates in
particular: the document speaks your customer's language, and the service
does not presume to know which.
Drawing a chart from the payload
A bar, line or pie chart, drawn from numbers in the payload. Works in Word and HTML templates, not in Excel.
{{ sales:::chart }} <!-- 150 mm wide, the default -->
{{ sales:::chart:120 }} <!-- 120 mm (40 to 180) -->
and in the payload:
{
"sales": {
"type": "bar",
"title": "Revenue 2026",
"labels": ["Q1", "Q2", "Q3", "Q4"],
"series": [
{"name": "Italy", "values": [10, 12.5, 9, 14]},
{"name": "France", "values": [4, 6, 7, 8]}
],
"alt": "Revenue by quarter, Italy against France"
}
}
typeisbar,lineorpie. A pie takes exactly one series of zero-or-more values.- Bounds: at most 6 series and 60 points (12 slices for a pie); labels and names at most 40 characters; values are plain numbers. Past a bound the chart is left out, with a warning, and the document is still produced.
- The chart is drawn on our side. No address is ever fetched, and nothing in the spec can carry a style, a script or a link.
altis the description a screen reader reads. Leave it out and one is built from the title and the data. Two or more series, and every pie, get a legend, so no meaning rests on colour alone.- A chart that cannot be drawn does not stop the document, in strict and lenient templates alike: it is left out and Validate and the generation's warnings say why. A field that is absent is the usual missing-field rule.
Putting a picture from the payload into the document
A photo of a receipt, a signature scan, a product shot — anything the payload carries rather than something already in the template.
{{ receipt:::img }} <!-- 80 mm wide, the default -->
{{ receipt:::img:120 }} <!-- 120 mm -->
and in the payload:
{
"receipt": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
}
It must be a data URI. Bare base64 is refused, and the
reason is worth one sentence: a long run of letters is valid
base64, so an ordinary text field would be mistaken for a picture and
silently replaced by nothing. The data:image/jpeg;base64,
prefix is one line to add in any language and removes the guesswork.
Send it at full size. Each image is checked to be a real PNG, JPEG or WebP and then resized down to fit a printed page — a 4000-pixel phone photo arrives as about a sixth of the bytes. Resizing before you send only costs quality, and the body limit is what you would be trying to avoid: it is 10 MB of JSON, and base64 inflates by about 37%, so roughly 7 MB of photos per call.
One unusable image costs that image, not the document. It is left out, the rest are placed, and the job records a warning saying how many. An expense note with ten receipts does not fail because the seventh was a screenshot of a screenshot.
Lists work the way every other list does:
{%p for picture in pictures %}
{{ picture.label }}
{{ picture.image:::img:150 }}
{%p endfor %}
Word templates only. In an HTML template there is
nothing to place an image into, and you do not need this filter:
put the same data URI straight into <img src="">
and it works. Using :::img in HTML leaves the space empty and
says so in the job's warnings rather than failing quietly.
A URL is never fetched, in either engine. A link supplied by a caller and dereferenced by our server is a way into our own network, and that door is deliberately shut.
A misspelled code is one row in Common mistakes.