To book a demo call 8717809030

Custom PDF Templates with Twig

Last updated

Neviscater prints every menu, bill, quantity sheet and purchase order as a PDF. Four built-in report styles and the brand colour overrides cover most needs. When a report has to look exactly like your own printed menu card or your stationery, you can write a custom PDF template: a page of HTML with Twig placeholders for the data, plus optional CSS. This guide explains how templates work, walks through a complete event-menu template, and lists everything a template can use.

Custom templates are available on every plan and are managed by the account owner.

Template or report style?

  • Want the same layout in different colours or a different font? Use Application Setup → Preferences → Report Styles: pick Classic, Royale, Metro or Letterhead and override the Brand Colors & Font. No template needed. See Printing and PDF Templates.
  • Want a different layout — the logo on the left with the customer details beside it, item names in capitals, the bill arranged like your old invoice? That is a template.

A template is written per report type. You can have a template for the Event Menu and leave Bill Details on a built-in style.

How a template works

  • Each report type can hold several templates, but only one is active. The active template is used for every print of that type by everyone in the account. A type with no active template prints in the selected built-in style.
  • You write only the content of the page. Font size, background images, cover and footer artwork and the colour overrides from Preferences are applied around your content automatically.
  • The data comes in as variables (the order, the event, the menu items) and as building blocks — ready-made pieces such as the company header or the grouped menu list that already follow your printing preferences.
  • Templates run in a sandbox. They can print values, loop, and test conditions. They cannot include files, call functions or run code, and every value is HTML-escaped when printed.
  • If an active template fails to render at print time, the report quietly prints in the built-in style instead, so a customer never receives a broken PDF.

Open the Template Studio

Application Setup → Tools & Settings → PDF Templates opens the Report Templates page. Templates are grouped by report type with Name, Active, Created and Actions:

  • + New Template, or + New on a report type, opens the editor.
  • Activate and Deactivate switch a template on or off for its type.
  • Edit, Export and Delete act on one template.
  • Import File at the top loads a template exported from another Neviscater account.

The editor has three tabs on the left — HTML (Twig), CSS and Reference — and a live preview on the right. The preview re-renders with sample data about a second after you stop typing. An error is shown in a red box above the preview. PDF Preview downloads the same page as a real PDF, page breaks included.

A complete example: an event menu

This template prints the company logo on the left half of the page, the customer and function facts on the right in capitals, and then the menu grouped under its sub-menu headings.

HTML (Twig)

<table class="top">
  <tr>
    <td class="logo"><img src="{{ company_logo() }}" alt=""></td>
    <td class="facts">
      <div><span class="label">Customer</span> {{ order.customer.customer_name|upper }}</div>
      <div><span class="label">Function</span> {{ event.event_name|upper }}</div>
      <div><span class="label">Date</span> {{ event.event_date|date("d M Y") }}</div>
      <div><span class="label">Guests</span> {{ event.guests }}</div>
    </td>
  </tr>
</table>

{{ menu_sections(sub_menu_items_array) }}

CSS

.top { width: 100%; margin-bottom: 20px; }
.top td { vertical-align: top; width: 50%; }
.top img { max-width: 100%; max-height: 150px; width: auto; height: auto; }
.facts div { font-size: 16px !important; padding: 3px 0; }
.facts .label { display: inline-block; width: 90px; color: #777; }
.prnt-list .submenu-name { color: indigo; text-decoration: underline; font-weight: normal; }
.prnt-list .item-name { text-transform: uppercase; }

What each part does:

  • {{ company_logo() }} is the logo uploaded under Preferences → Business Details, embedded in the page. It prints nothing when no logo is uploaded.
  • {{ order.customer.customer_name|upper }} prints a value through the upper filter. |date("d M Y") formats a date.
  • {{ menu_sections(sub_menu_items_array) }} is a building block: the items of the event grouped under their sub-menu headings, honouring your printing preferences for sub-menu names, Veg / Non-Veg presentation and page breaks.
  • The last two CSS rules restyle the block's output. The shared print stylesheet sets the font size and padding of table cells with high priority, so a size of your own on a cell or a row needs !important, as in the .facts div rule.

Save it as, say, event-menu-v1, check the preview, then Activate it. Every Event Menu printed from then on uses it.

Writing the menu loop yourself

The building blocks are the easy path, but you can also loop over the data directly:

{% for submenu, items in sub_menu_items_array %}
  <h3>{{ submenu }}</h3>
  <ul>
    {% for item in items %}
      <li>{{ item.display_name }}{% if item.diet_type == "non_veg" %} (non-veg){% endif %}</li>
    {% endfor %}
  </ul>
{% endfor %}

A hand-written loop gives you total control but does not follow the printing preferences and does not handle page breaks the way the blocks do.

Variables by report type

The Reference tab in the editor lists these for the report type you are editing. print_options — your company details and printing choices — is available in every template.

Report type Variables
Event Menu order (order_number, order_type, status, customer.customer_name); event (event_name, event_date, start_time, end_time, guests, specified_rate_per_plate); menu_items (flat list: display_name, display_desc, display_submenu, diet_type, diet_label); sub_menu_items_array (sub-menu name → items); menu_items_by_diet, sub_menu_items_by_diet and diet_labels for Veg / Non-Veg layouts
Order Menus order; events_menuitems_array (per event: event, menu_items, menu_items_by_diet); events_submenus_items_array (per event: event, sub_menu_items_array, sub_menu_items_by_diet); diet_labels; sort_by
Bill Details order (order_number, order_type, customer.customer_name, start_date_str, end_date_str, place); orderBillDetails (orderBills[] with date_str, description, ratePerUnitStr, quantity, costStr; taxes[] with name, rate, amountStr; orderBillsTotalStr, orderBillsGrandTotalStr); totalPaymentsStr; paymentOutstandingStr
Customer Invoice invoiceDetails (invoice_number, status, issued_at); order from the invoice snapshot; orderBillDetails as for Bill Details

Other report types — item quantities, raw materials, Payment Details, Receipt, misc expenses, All Resources Checklist, Item Tags, Catalog Menu and the Kitchen Plan reports — accept templates too. Their variable lists are not yet written up in the Reference tab, and their preview uses event-menu sample data; the building blocks still work.

Building blocks

Block Prints
page_header(event) The company header. Use page_header(null, order) for order-level reports and page_header(null, order, true) to add the GST and FSSAI numbers
menu_items(menu_items) The flat menu list, following your printing preferences
menu_sections(sub_menu_items_array) Items grouped under sub-menu headings. A short group stays whole on a page; a long one flows across pages with its heading held to its first items
bills_table(orderBillDetails, totalPaymentsStr, paymentOutstandingStr) Bill lines, taxes and totals; the two payment arguments are optional
payment_footer() The bank, UPI and QR footer according to your printing options
company_logo() The uploaded logo, for <img src="{{ company_logo() }}">; empty when none is uploaded

Both menu blocks follow the Veg / Non-Veg on menus preference. To fix a layout in the template instead, pass the mode as a second argument — menu_sections(sub_menu_items_array, "columns") — using none, markers, sections, sections_marks, columns or columns_marks. Any other word is a template error.

What the template language allows

  • {{ … }} prints a value, HTML-escaped. {% if %}, {% for %} and {% set %} are the only tags. Inside a loop, loop.index is the running number.
  • Text filters: upper, lower, capitalize, title, trim, nl2br, striptags, replace, split, join, slice, first, last, length, sort, default, escape, raw.
  • Number and date filters: date, number_format, round, abs.
  • A missing field prints as blank rather than failing.
  • Not allowed: including or extending other templates, macros, and calling methods on the data. The sandbox refuses these when the template is saved or imported, and the error names the line.

Create from Image

Have a design already — last year's printed menu card, a bill from your previous software? In the editor click Create from Image and upload a JPEG, PNG, WebP, GIF or PDF of it, up to 10 MB. Neviscater's AI redraws the design as a template and wires the data areas to the variables and blocks. The draft lands in the editor as Draft imported — review the preview and adjust. It replaces whatever was in the editor, is never saved or activated on its own, and usually needs a round of adjustment in the preview before you save it.

Export and import between accounts

Export downloads a template as a .nctemplate.json file holding its report type, name, HTML and CSS. Import File on another account — or on the same one, as a copy — reads such a file, checks that it renders on that system, and adds it inactive: It is not active yet — activate it when you are ready. A file that does not render is refused with the reason, and nothing is imported.

Use this to carry a design from a trial account to the live one, to share a layout between two businesses you run, or to receive a template someone designed for you.

Logos and images

  • company_logo() embeds the logo uploaded under Business Details. The uploaded logo is stored small, 60 × 60 pixels, which is fine for a header line but not for a half-page logo.
  • For a large logo or any other picture, paste the image into the HTML as a data URI (<img src="data:image/png;base64,…">). It then travels with the template, including through export and import. Separate image files cannot be bundled with a template.
  • The Report Background, Cover Pages and Footer Pages artwork from Preferences prints around custom templates too. If a logo appears twice, check whether a background image for that report type already carries it.

Common questions

My template prints, but in the built-in style. The active template failed to render, so Neviscater fell back. Open it under PDF Templates → Edit and read the error above the preview. The usual causes are a misspelt variable inside a loop, or a filter that is not allowed.

My font size or padding on a cell is ignored. The shared print stylesheet sets font-size and the vertical padding of table cells with high priority. Add !important to your own rule.

The sub-menu headings are missing. menu_sections follows the Print Sub Menu Names preference. Turn it on under Preferences → Printing, or pass a mode that always prints headings, such as "sections" or "columns".

Can I change the report type of a template? No. Create a new template for the other type and copy the HTML and CSS across.

Who can see the PDF Templates page? The account owner. Everyone in the account gets the active template on their prints.

Can I keep two layouts and switch between them? Yes. Keep both under the same report type and Activate the one you want; only one is active at a time.

Try it on your own orders
Every Neviscater account starts with a free 10 day trial with all features unlocked. No payment details are asked for. To book a demo, call 8717809030.
Plans & pricing Start the free trial