Skip to content

Combine templates

Build one document from several templates: a cover before the invoice, terms and conditions after it, a letterhead or a “DRAFT” watermark on every page, a table of contents, or a section name on each page. Each part is an ordinary template in the same workspace, so you design it once and reuse it everywhere. A part can also be a PDF file you upload, such as a supplier’s terms or a form your template fills in; see add an uploaded PDF file.

page.json
{
"operations": [
{ "type": "prepend", "template": "Cover" },
{ "type": "append", "template": "Terms" },
{ "type": "merge", "template": "Letterhead" }
]
}

With these steps, a render of the invoice returns the cover, the invoice and the terms in one PDF, with the letterhead drawn beneath the content of every page.

  1. Create the other template, such as Terms, and publish it.

  2. In the template that adds it, click PDF properties in the preview toolbar. Under Other templates and PDF files, click Add template.

  3. Choose the template, then choose what to do with it:

    Studio type What it does
    Add its pages before prepend Adds all its pages before the document’s pages, such as a cover.
    Add its pages after append Adds all its pages after them, such as terms and conditions.
    Draw it onto every page merge Draws it onto the document’s pages. See draw onto every page.
  4. Click Update preview to see the added pages, then save and publish the template.

Studio writes each step to the operations list in the template’s page.json, and you can edit that list there instead.

A merge step draws the other template onto the document’s pages, beneath the content, like printed letterhead paper. Its first page is drawn onto every page, at its own size from the page’s top-left corner, so give it the same paper size as the document.

For a step that draws, tick any of these in Studio:

Studio Field What it does
Page by page mergeWholeDocument Draws its page 1 onto page 1, its page 2 onto page 2, and so on. Pages beyond its last page get nothing.
Render for every page renderForEveryPage Renders it once for each page, so it can show that page’s details. See print something on each page. Not with Page by page.
In front of the content mergeToFront Draws it over the content instead of beneath it.

A watermark is a template drawn in front of the content. Make a template named Watermark with no margins:

page.json
{
"pageSize": { "widthMm": 210, "heightMm": 297 },
"margins": { "topMm": 0, "rightMm": 0, "bottomMm": 0, "leftMm": 0 }
}
template.html
<div style="height: 297mm; display: flex; align-items: center; justify-content: center;">
<div style="transform: rotate(-45deg); font-size: 120pt; color: #e11d48; opacity: 0.15;">
DRAFT
</div>
</div>

Then add it to the document as a step:

page.json
{ "type": "merge", "template": "Watermark", "mergeToFront": true }

Steps run in the order listed, and a merge draws onto the pages the document has at that point. Put it last to draw onto added pages too, or before them to leave the cover and terms plain.

A step can add a PDF file instead of a template, such as terms you received as a PDF. Add the file to the template’s folder with Import into folder… in the folder’s … menu, then in PDF properties under Other templates and PDF files, click Add PDF file and choose it. In page.json, the step names the file’s path with pdf instead of template:

page.json
{
"operations": [{ "type": "append", "pdf": "terms.pdf" }]
}

The path starts at the template’s folder, like the template’s other workspace files. Every step type works with a file: prepend and append add all its pages, and merge draws its first page, or with mergeWholeDocument its page N onto page N. A file has no data to fill in, so renderForEveryPage does not apply.

To fill in a PDF form you received, draw it beneath your document page by page. Make a template with the form’s paper size and no margins, and place each value where the form has its box:

template.html
<div style="position: relative; height: 297mm;">
<div style="position: absolute; top: 52mm; left: 40mm;">{{customer.name}}</div>
<div style="position: absolute; top: 61mm; left: 40mm;">{{orderNumber}}</div>
</div>

Then draw the form beneath it:

page.json
{
"pageSize": { "widthMm": 210, "heightMm": 297 },
"margins": { "topMm": 0, "rightMm": 0, "bottomMm": 0, "leftMm": 0 },
"operations": [{ "type": "merge", "pdf": "forms/order-form.pdf", "mergeWholeDocument": true }]
}

The document keeps its own pages, so give the template one page for each page of the form: a form page with no page of the template to draw onto is left out. The form’s own fillable fields are not filled in; your template draws the values over them.

Use the document’s pages in the other template

Section titled “Use the document’s pages in the other template”

The other template can read $pdf, the document’s pages as they are when its step runs, to print a table of contents or details that change from page to page.

In the document, mark where each section starts with pdfCreatePagesGroup, and add a link target with pdfDest. Print both with triple braces:

template.html
{{#each sections}}
<h2>{{title}}{{{pdfCreatePagesGroup title=title id=id}}}{{{pdfDest id}}}</h2>
<p>{{body}}</p>
{{/each}}

The other template then has:

Field What it holds
$pdf.pages One entry per page of the document so far. Each has group, the value of the last pdfCreatePagesGroup on or before that page, and items, the values of pdfAddPageItem on that page.
$pdf.pageIndex With Render for every page: the page it is drawn onto, from 0.
$pdf.pageNumber With Render for every page: the same page, from 1.
Helper What it does
{{{pdfCreatePagesGroup value}}} Sets the group of the page it is on, and of the pages after it until the next group. The value can be any data, or hash arguments as above.
{{{pdfAddPageItem value}}} Adds the value, or the hash arguments, to the items of the page it is on.
{{{pdfDest "name"}}} Marks a place that a #name link in any part of the document leads to. See link between parts.

Keep these helpers out of elements with display: inline-block, where they are not found.

The other template still gets the document’s data, with $pdf added. Read $pdf in its HTML, its page header and footer, or its helpers.js. Its scripts run before the pages exist, so they cannot read it, and only an HTML template can read it, not a component template.

Make a template named Contents that lists the first page of each group. In its helpers.js:

helpers.js
function sections(pages, offset) {
const result = [];
pages.forEach((page, index) => {
const last = result[result.length - 1];
if (page.group !== undefined && (last === undefined || last.id !== page.group.id)) {
result.push({ ...page.group, page: index + 1 + offset });
}
});
return result;
}
template.html
<h1>Contents</h1>
<ul>
{{#each (sections $pdf.pages 1)}}
<li><a href="#{{id}}">{{title}}</a>, page {{page}}</li>
{{/each}}
</ul>

Add it to the document with Add its pages before (prepend). The page numbers count the document as it is before the contents are added, so offset adds the contents’ own length, here one page.

Add the other template with Draw it onto every page and tick Render for every page. It renders once for each page, with $pdf.pageIndex and $pdf.pageNumber naming the page it is drawn onto. This template prints the section name and a page number that restarts in each section, at the bottom of every page:

helpers.js
function sectionPage(pages, index) {
let start = index;
while (start > 0 && pages[start - 1].group?.id === pages[index].group?.id) {
start -= 1;
}
return index - start + 1;
}
template.html
<div style="height: 297mm; position: relative;">
<div style="position: absolute; bottom: 10mm; left: 18mm; right: 18mm; font-size: 9pt;">
{{#with (lookup $pdf.pages $pdf.pageIndex)}}{{group.title}}{{/with}}, page
{{sectionPage $pdf.pages $pdf.pageIndex}}
</div>
</div>

Give it the document’s paper size and no margins, as for the watermark above. For a plain “Page 1 of 3” footer you need no other template: use the template’s own page footer, see build a template.

Mark a place with {{{pdfDest "terms"}}} in one template, and link to it with <a href="#terms"> from any template in the same document, such as from the invoice to its terms. {{{pdfDest id="terms"}}} works too. A link to an id in the same template works as usual, and a link to a name that no part defines is left as plain text.

  • The other template renders with the same data and language as the document, on its own paper size, margins, page header and footer.
  • Studio previews use the other template’s saved draft. Every other render uses its published version, so publish it first. When you publish a new version of it, every template that adds it uses that version from then on. See publishing.
  • An uploaded PDF file is read when the document renders, so replacing the file changes every render from then on, without publishing again. Publishing checks that the file exists; if it is deleted or moved later, renders fail.
  • An uploaded PDF file can be up to 5 MB, and it cannot be encrypted. It counts toward the render’s input size, and in a batch every document carries it, so a large file leaves room for fewer documents; see limits.
  • Added pages from an uploaded file keep their look and their bookmarks, but not their fillable form fields or their links to named places inside the file.
  • Templates are found by name, ignoring case. If none has that name, Studio warns under Other templates and PDF files and renders fail until one is created and published.
  • A template can have up to 10 steps. A template that another one adds cannot have steps of its own, and a spreadsheet template cannot be added.
  • Steps run only when you render a template: by name with the Reports API, or a template version with the Renders API. A render of raw HTML and a string render cannot have steps.
  • A Reports API request can replace the template’s steps with pdf.operations, and an empty list runs none. See the API reference.
  • The added pages count toward the page limit; see limits.
  • If the other template or PDF file fails, the error message names it.
  • A PDF/A or PDF/UA document keeps its standard with parts from other templates. In a PDF/UA document, pages added before or after keep their headings and bookmarks, and a drawn template is decoration that screen readers skip. See PDF standards.
  • A PDF/A or PDF/UA document cannot add an uploaded PDF file, since nothing checks that the file meets the standard. Rebuild that part as a template instead.