DynamicDocumentAPI

Headers and footers

Add running headers and footers with page numbers, dates, logos and data, in simple text mode or with your own HTML.

View as Markdown

Headers and footers repeat on every page of a PDF. They are configured in output.pdf.header and output.pdf.footer (or stored as defaults on the template), and they come in two flavours: a simple three-slot text mode, and full HTML.

OptionTypeDefaultDescription
enabledbooleanfalseDraw this header or footer
htmlstringnoneHTML mode: the markup to render on every page
left, center, rightstringnoneSimple mode: text for the three slots
font_sizestring"9px"Base font size for the header or footer document
heightstring"15mm"Height reserved on every page

Simple mode

Give one or more of left, center and right a line of text. Tokens are replaced per page, and your data is available through the template language:

{
  "output": {
    "format": "pdf",
    "pdf": {
      "margin": { "top": "20mm", "right": "16mm", "bottom": "18mm", "left": "16mm" },
      "footer": {
        "enabled": true,
        "left": "{{ company.name }} · Invoice {{ invoice.number }}",
        "right": "Page {{page}} of {{pages}}",
        "font_size": "8pt",
        "height": "12mm"
      }
    }
  }
}
TokenReplaced with
{{page}}The current page number
{{pages}}The total number of pages
{{date}}The date the document was rendered
{{title}}The document title

The {{date}} token uses the renderer's default date format. When the format matters, pass the date in your data and format it yourself, for example {{ invoice.issue_date | format_date("long", "en-GB") }}.

HTML mode

Set html for full control. The markup is a small standalone document that is drawn on every page; spans with particular class names are filled in by the renderer:

{
  "header": {
    "enabled": true,
    "html": "<div style=\"width:100%;padding:0 16mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;align-items:center;font-size:8pt;color:#555\"><span>{{ company.name }}</span><span>Page <span class=\"page\"></span> of <span class=\"pages\"></span></span></div>",
    "height": "16mm"
  }
}

The same markup, formatted for reading:

<div style="width:100%;padding:0 16mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;align-items:center;font-size:8pt;color:#555">
  <span>{{ company.name }}</span>
  <span>Page <span class="page"></span> of <span class="pages"></span></span>
</div>
ClassFilled with
page (alias pageNumber)The current page number
pages (alias totalPages)The total number of pages
dateThe render date
titleThe document title
urlThe document URL, which is most useful for url renders

Put the class on an empty element: any content inside it is replaced.

How headers and footers are rendered

The header and footer are rendered in an isolated document, separate from your page. That has a few consequences worth knowing:

  • Your document's CSS does not apply. Style the header with inline styles.
  • Network resources cannot be loaded. Images referenced with <img src="…"> and fonts declared with @font-face are downloaded and inlined as data URIs for you, up to 2 MB each. You can also inline an image yourself with image_data_uri().
  • The base font size is small. It comes from font_size (9px by default), so set it, or set sizes on the elements themselves.
  • Name a concrete font, not only a generic family. In the header and footer document a bare font-family: sans-serif resolves to a serif face. Give a family the engine has, with fallbacks, for example font-family: "Inter", "Liberation Sans", Arial, sans-serif.
  • The area spans the full page width, including the page margins. Add horizontal padding that matches your page margins so the header lines up with the body text.
  • Background colours print as specified, without extra CSS on your side.
  • The template language runs with the same data as the document, and output is autoescaped.

Height and margins

A header is drawn inside the top margin and a footer inside the bottom margin, so the margin has to be large enough to hold it.

  • If you don't set margin.top or margin.bottom, the margin follows the height of the header or footer.
  • If you do set them, keep the margin at least as large as height, plus a few millimetres of breathing room. A margin smaller than the height produces a header_footer_overlap warning on the render, and body content can end up underneath the header.

For the example above, a height of 16mm pairs well with a margin.top of 22mm.

Template defaults

Templates store their own header and footer in the editor's Header & footer tab, so most requests don't need to send anything. Options you send with a render request override the template's defaults, which is useful for a "COPY" or "DRAFT" banner in one specific call.

Examples

A footer with page numbers and a company line:

{
  "footer": {
    "enabled": true,
    "html": "<div style=\"width:100%;padding:0 18mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;font-size:7.5pt;color:#646a7c\"><span>Example GmbH · VAT DE123456789</span><span>Page <span class=\"page\"></span> of <span class=\"pages\"></span></span></div>",
    "height": "14mm"
  }
}

A letterhead header with a logo, using a data URI so the image is always available:

<div style="width:100%;padding:0 20mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;align-items:flex-end;font-size:8pt;color:#333">
  <img src="{{ image_data_uri(company.logo_url) }}" alt="" style="height:10mm">
  <span>{{ document.type }} {{ document.number }} · {{ document.date | format_date("medium", "en-GB") }}</span>
</div>

A centred confidentiality note in simple mode:

{
  "header": {
    "enabled": true,
    "center": "Confidential — prepared for {{ customer.name }}",
    "font_size": "8pt",
    "height": "12mm"
  }
}

Planned

  • Different headers and footers on the first page, and separate odd and even page variants
  • A visual header and footer builder in the editor
  • Running section titles, which repeat the current section's heading in the header, as part of the pagination toolkit

On this page