Skip to main content

PDF Templates

The PDF Templates screen is the library of reusable PDF layouts that NomaUBL applies when rendering an invoice. Every layout is a pdf-template resource saved in config-pdf.json; document templates pick a layout by name via their pdfTemplate property — so a single layout can be shared across many documents.

The page covers four operations:

  • manage the catalogue (Add / Import / Copy / Remove);
  • edit a layout — section list, per-section toggles for preset sections, free-form composition for block sections;
  • mark a default for documents that don't pick an explicit layout;
  • preview the result against a sample invoice without leaving the page.

The page applies regardless of source system — JD Edwards, SAP, NetSuite or a custom ERP — since the input it consumes is the generated UBL 2.1, not the source XML.

New in 2026.05.1

PDF templates were previously edited inline on the Documents page (the PDF Template tab) and lived in the document template's properties. They are now first-class shareable resources with their own page. The Documents tab still exists, but it now picks a layout by name from this page's catalogue rather than holding the JSON inline.


Opening the page

  • Sidebar → Management → PDF Templates.
  • The page lists every layout in the catalogue. The sidebar entry built-in is the bundled factory layout — read-only, always present as a safety fallback.
  • The current default layout — i.e. what a document with no explicit pdfTemplate resolves to — is marked with a yellow ★ in the sidebar.

At a glance

PDF Templates+ Add↑ Import⎘ Copy🗑 Remove★ Set as defaultSearch…built-inFactory default · read-onlyfactoryinvoice-frStandard FR layoutcredit-noteCredit note variantrecipient-receiptCompact receiptinvoice-frStandard FR layoutSaveSECTIONS+ header+ lineTable+ block⋮⋮HeaderSupplier · invoice metadata⋮⋮PartiesCustomer + Delivery boxes⋮⋮Line TableMain 7-column tableMETAS☑ Invoice number☑ Issue date☐ Buyer referenceSUPPLIERS☑ SIREN☑ Legal form☐ Phone⋮⋮Block · payment-termsXPath-driven⋮⋮VAT Breakdown👁 PreviewToolbarmanage + set defaultCatalogue★ marks the default · factory is read-onlySection pickerpresets + block (XPath)Per-section drawergrouped toggles or canvasBlock sectionfree-form XPath layoutLive previewrenders against sample UBL

The page splits into a left catalogue of saved layouts and a right editor for the selected layout. The editor is identical to the PDF Template tab on a document template — same section list, same per-section drawer, same live preview — except the result is saved to the catalogue under a name, not to a single document.


How a layout is resolved

When NomaUBL renders a PDF for an invoice, it walks a three-step chain to choose the layout:

  1. The document template of that invoice (read from F564231.UHTMPL) carries a pdfTemplate property — if set, that name is used.
  2. Otherwise, the defaultPdfTemplate property on the global template is used — set from this page via the Set as default button.
  3. Otherwise, the bundled built-in layout is used — always present, never deletable.

The third step is the safety net: a freshly installed environment renders sensible PDFs even before any layout is created.


Toolbar actions

The toolbar at the top of the page covers the catalogue lifecycle plus the default override.

ActionEffect
AddOpens a modal asking for a name and a description. Creates an empty pdf-template resource — start by adding sections in the editor.
ImportLoads a JSON file produced by another instance (or the Export action). Updates the matching template if the name already exists, otherwise creates it. The reserved name built-in is rejected.
CopyDuplicates the selected layout under a new name. The fastest way to derive a variant from built-in or from an existing customer-specific template.
RemoveDeletes the selected layout after a confirmation. Disabled on built-in.
Set as defaultWrites the selected layout's name into global.defaultPdfTemplate. Documents without an explicit pdfTemplate then resolve to this layout.
Reset to factoryClears global.defaultPdfTemplate, restoring the chain to built-in.

The catalogue

The sidebar list contains every saved layout plus the bundled built-in.

  • built-in — pinned to the top, read-only, marked with a factory badge. Selecting it opens the editor in a banner-tagged read-only mode. Copy is the only way to derive an editable layout from it.
  • ★ marker — the layout currently used as the default for documents with no explicit pdfTemplate. Exactly one layout carries the star at any time.
  • Description — free-text label persisted on the resource. Surfaced both in the sidebar and in the PDF Template selector on a document template.

Two kinds of sections

A layout is a list of sections. Two flavours can be mixed in the same template.

Preset sections — the curated layout building blocks

Nine reorderable preset sections cover the canonical shape of an invoice PDF: Header, Parties, Agent, Line Table, Document Allowances, VAT Breakdown, Totals, Payment, Notes. Each one is backed by a Java class that knows how to render the matching part of an EN 16931 invoice; the user controls visibility via per-section toggles in the inline drawer.

The drawer groups toggles by Category · Name prefix and arranges them in side-by-side columns that mirror the PDF layout itself — Header reads as METAS | SUPPLIERS, Line Table as Group headers | Columns | Sub-details — instead of one long flat list. Toggles use the rounded-blue Checkbox component for visual consistency in dark mode.

The full list of toggles per preset section:

  • Header — eight META · … toggles (invoice number, issue date, due date, contract / order / buyer references, invoice type, profile ID) plus six Supplier · … toggles (address, SIREN, legal form, VAT, phone, email).
  • Parties — Customer and Delivery boxes, with separate toggles for SIREN, VAT, address, location ID and a Show Delivery box master switch (when off, the layout renders a single-column Customer block).
  • Line Table — three group-header toggles (Delivery group, Page break per delivery, Document Reference group), seven column toggles (Line #, Description, Quantity, Unit, Unit Price, Amount, Tax) and seven sub-detail toggles for line metadata (BT-127, BT-134/135, BT-156, BT-157, BT-158, allowances / charges, additional item properties).
  • Document Allowances — column toggles for type, reason, amount, tax.
  • VAT Breakdown — column toggles for category, rate, taxable, tax amount (an exemption column auto-appears when present).
  • Totals — seven row toggles covering the full totals stack (Total HT, Allowances, Charges, Tax Exclusive, Total Tax, Total TTC, Amount Payable).
  • Payment — payment means / IBAN / BIC / payment-terms note toggles.
  • Notes — single toggle to expand [PMD] / [PMT] tag prefixes against the note-types catalogue.

Block sections — XPath-driven free-form composition

The new block section is an XPath-driven primitive that composes any layout the preset sections don't cover. A single block holds a tree of typed nodes:

KindUse
textLiteral string.
fieldRenders an XPath value, with an optional inline label and a format selector — date, currency, number, percent.
imageEmbed an image (logo, watermark, signature).
spacer / hrVertical breathing room or a horizontal rule.
row / columnContainer with align (start / center / end) and gap controls. align: end and align: center shrink the row to ~50 % so a label + value pair stays grouped instead of stretching across the page.
repeatXPath returning a NodeList; the block's child is rendered once per match.
ifXPath returning a boolean; the block's child is rendered when true, hidden otherwise.
tableA rows × cols grid with optional cell borders and a styled header row. Setting xpath makes it iterate (one row per match), with the children acting as the per-row cell template.

Several blocks can live in the same template — e.g. one for a French legal mention block, one for a structured payment-terms table, one for a watermark image. Each block carries a user-friendly name shown next to the section row in the editor, so a layout with three blocks reads as Block · payment-terms, Block · legal-mentions, Block · watermark.

XPath conventions inside a block

  • The picker preserves the cbc: / cac: namespace prefixes — required by the namespace-aware backend; a manually-typed XPath without a prefix simply will not match.
  • Picks are emitted as /*/<full-path> so they're independent of the document root (the same path applies to Invoice and CreditNote).
  • Inside an iterating ancestor (a repeat or a table with an xpath), the picker further strips the iterator's path so child cells start with relative XPaths (cbc:TaxAmount instead of /*/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount). This keeps row templates portable when the iterator changes.

Visual canvas editor

A block section opens in the BlockCanvasEditor rather than a flat JSON textarea. The editor is split into three stacked panes plus a JSON escape hatch.

PanePurpose
TreeIndented view of every node in the block, with kind tags (text, field, repeat, table, …). Click a node to select it; the selection drives both the toolbar and the inspector.
ToolbarAdd child / wrap / unwrap / delete / move up / move down — operations applied to the currently selected node. The same operations are available via keyboard shortcuts.
InspectorPer-kind attribute form (XPath, label, format, alignment, gap, …) plus a Style sub-panel covering font, weight, size, colour, alignment and padding.

A small but important detail at the top of the inspector: a Kind select that morphs the selected node in place — turn a column into a repeat without deleting and re-adding it, the compatible attributes (children, style) are carried over by the transmuteKind helper. The same trick handles the common case of promoting a static layout block to an iterating one once the data shape is understood.

The Live preview button at the top of the form opens a 960 × 85vh modal that renders the current configuration against a sample invoice — clicking Save on the editor while the preview is open updates the iframe in place. The Load XML sample control sits one level up, on the template-level header, so a single sample feeds the XPath autocompletion of every block in the template.


REST API

The page reads and writes via the standard template endpoints — same routes used by every other resource type.

Method + pathPurpose
GET /api/templatesLists all templates; the page filters by type = pdf-template.
GET /api/templates/{name}Loads one layout (the JSON sits in the template property).
POST /api/templatesCreates a new layout (Add).
POST /api/templates/{from}/copy/{to}Duplicates (Copy).
PUT /api/templates/{name}Saves edits.
DELETE /api/templates/{name}Removes a layout. The reserved built-in is rejected.
POST /api/pdf-templates/previewRenders an arbitrary pdfTemplate JSON against a sample invoice — used by the live preview iframe.
PUT /api/templates/global (with defaultPdfTemplate)Behind Set as default / Reset to factory.

Tips & best practices

  • Start from built-in via Copy. The factory layout is a solid baseline — derive variants by copying and tuning the toggles instead of starting from an empty template.
  • One layout, many documents. For each invoice variant (standard, credit note, recipient receipt …) prefer one shared layout over per-document inline JSON. The benefit is a single edit point when the legal mentions or the column set changes.
  • Set a default early. Mark a layout as default before connecting customer-specific document templates — every new document then resolves to it implicitly, and the explicit pdfTemplate property on a doc template stays reserved for genuine variants.
  • Use block for what presets don't cover. Custom legal mentions, country-specific footers, structured signature blocks, watermarks — all best modelled as a block rather than asked of an existing preset.
  • Iterate inside the live preview modal. Toggling a section, then watching the iframe re-render in the modal is the fastest way to converge on a layout without leaving the editor.
  • Reuse the XML sample. A single Load XML sample on the template header feeds the picker for every block — load it once per editing session and every XPath autocompletion just works.
  • Don't delete built-in. The button stays disabled on it for that reason — it is the safety fallback at the end of the resolution chain.