HTML PDF Templates
Zoho ERP includes standard PDF templates with built-in customisation options. If you need more control over the design and structure of PDFs created for custom modules, you can build templates from scratch using HTML and CSS.
Note: This feature is available only for Custom Modules and is currently in early access. Email support@zohoerp.com to enable it for your organization.
Rendering Capabilities and Limitations
Before creating an HTML PDF template, understand which HTML elements and CSS properties the rendering engine supports. Zoho ERP uses the Flying Saucer rendering engine, which supports a subset of HTML and CSS standards.
Flying Saucer Limitations
Zoho ERP uses the Flying Saucer rendering engine to generate PDFs. Since Flying Saucer is designed for XHTML and CSS 2.1, the following limitations apply:
- Flexbox, Grid, and CSS animations are not supported.
- Sticky and fixed positioning are not supported.
- Transforms, transitions, filters, gradients, backdrop filters, and clip-path are not supported.
- Only PNG, JPEG, and GIF image formats are supported.
- Relative image URLs do not work. Use absolute URLs or inline images instead.
- All HTML tags must be properly closed, attributes must be quoted, and tags must be nested correctly.
- Malformed inline CSS will be ignored.
- Pseudo-elements (::before, ::after) work, but complex pseudo-classes like :nth-child and :not may not work reliably.
- HTML5 semantic tags such as <section>, <article>, and <video> are not supported.
- SVG support is limited.
- Right-to-left (RTL) text and Unicode complex scripts may not render correctly.
- Long tables and large elements may split across pages.
- Page numbering must be configured manually using CSS counters.
- Custom list-style images may not render.
Supported HTML Tags
HTML PDF Templates support a limited set of HTML tags.
| Category | Tags |
|---|---|
| Structural | div, p, span, table, thead, tbody, tfoot, pre |
| Text Formatting | b, strong, small, label, sup, sub, font |
| Lists | ul, ol, li |
| Media | img |
| Links | a |
| Headings | h1-h6 |
| Others | hr |
Common attributes such as id, class, style, align, width, and height are supported where applicable.
Note: Only supported tags will render correctly in the generated PDF.
Supported CSS Properties
HTML PDF Templates support a subset of CSS 2.1.
| Category | Properties |
|---|---|
| Layout | display, float, position |
| Positioning | top, bottom, left, right |
| Box Model | margin, padding, border |
| Text Styling | font-*, text-align, line-height |
| Colours | color and background properties |
| Page Breaks | page-break-before, page-break-after, page-break-inside |
| Tables | table layout properties |
Note: Modern CSS features like Flexbox, Grid, and CSS animations are not supported.
Create an HTML PDF Template
You can build an HTML PDF template from scratch for a custom module. To do this:
- Go to Settings in the top right corner of the page.
- Select PDF Templates under Customization.
- Select the required custom module in the Templates pane.
- Click + New in the top right corner.
- Click Build From Scratch.
You can then structure the template using the following elements.
- Define page layout and page rules
- Add headers and footers
- Configure pagination using CSS counters
- Insert dynamic content using placeholders
- Apply conditional rendering
- Render tables and line items
- Use functions
Define Page Layout and Page Rules
Use the @page CSS rule to control the page size, margins, and layout.
The @page rule can control:
- Page size
- Margins and padding
- Borders and background
- Header and footer placement
- Page numbering
@page {
/* Page-level CSS */
}Global Page Styles
Apply this to all pages in the document.
@page {
margin: 20px;
border: 1px solid #ccc;
}You can also use page-specific selectors such as :first, :left, and :right when different pages need different layouts.
Page-Specific Styles
First Page
@page :first {
/* First page only */
}Left Pages (Even Pages)
@page :left {
/* Left-hand pages */
}Right Pages (Odd Pages)
@page :right {
/* Right-hand pages */
}Add Headers and Footers
You can add content to the Header and Footer tabs in the editor.
- Click the Header or Footer tab.
- Enter the required content.
- To use different content for the first page, enable the separate header or footer option for pages after the first page.
- Enter the required content in the first page and subsequent page sections.
- Click Run Code to preview the output.
Configure Pagination Using CSS Counters
You can add page numbers using CSS counters.
Use one of these approaches:
- Using a custom footer class
- Using separate page number and total page elements
Using a Custom Footer Class
Use this when page numbering needs to appear with other footer content.
<div class="my-custom-pagination-class-name"></div>.my-custom-pagination-class-name::after {
content: " | Page " counter(page) " of " counter(pages);
}Here:
- counter(page) returns the current page number.
- counter(pages) returns the total number of pages.
- The counter() function works only inside pseudo-elements such as ::after.
Using Separate Page Number and Total Page Elements
Use this when you want independent styling and placement.
.pagination {
font-size: 12px;
text-align: center;
}
.pageNumber::after {
content: counter(page);
}
.totalPages::after {
content: counter(pages);
}<div class="pagination">
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</div>Insert Dynamic Content Using Placeholders
Use placeholders in the template to render dynamic values such as record details, field values, and table data.
<p>Customer Name: ${invoice.customer_name}</p>Click Insert Placeholders to add placeholders to the template.
Apply Conditional Rendering
Use conditions in the template when you want to show specific content only when a field has a value or when a condition is met.
Show content if a value exists:
${#invoice.field_placeholder}
<div>Field exists</div>
${/invoice.field_placeholder}Show content if a value does not exist:
${^invoice.field_placeholder}
<div>Field missing</div>
${/invoice.field_placeholder}Use equality conditions:
${#`invoice.status = Paid`}
<div>Paid Invoice</div>
${/`invoice.status = Paid`}Use inequality conditions:
${^`invoice.status = Paid`}
<div>Unpaid Invoice</div>
${/`invoice.status = Paid`}Render Tables and Line Items
Use HTML tables to render line items and other tabular data in the PDF.
Line items are rendered by looping through rows using Mustache-style sections.
${#sales_receipt.line_items}
<tr>
<td>${name}</td>
<td>${quantity}</td>
<td>${rate_formatted}</td>
</tr>
${/sales_receipt.line_items}Example:
<table>
<thead>
<tr>
<th>Description</th>
<th>Qty</th>
<th>Unit Price</th>
</tr>
</thead>
<tbody>
${#TableCF.cm_products}
<tr>
<td>${TableCF.cm_products.cf_description.Value}</td>
<td>${TableCF.cm_products.cf_quantity.Value}</td>
<td>${TableCF.cm_products.cf_unit_price.Value}</td>
</tr>
${/TableCF.cm_products}
</tbody>
</table>Note: Use only <tr> and <th> inside <thead>, and <tr> and <td> inside <tbody>. Avoid using <div> or <span> inside table headers.
Use Functions
You can use functions to perform calculations, comparisons, formatting, and logical checks directly in your template.
Function syntax:
${`FUNCTION_NAME(arguments)`}Supported function types:
Learn more about the Functions Library.
- Click Save in the top right corner after you’ve customized the PDF template.
You can also edit a template, mark it as default, clone it, mark it as inactive, mark it as active, or delete it.