HTML to PDF: Templates, Invoices & Arabic RTL
Pricing
from $2.40 / 1,000 pdf generateds
HTML to PDF: Templates, Invoices & Arabic RTL
HTML to PDF converter for templates and Markdown. Merge JSON, CSV or dataset rows with Handlebars to make one PDF per row: invoices, certificates, reports. Page size, margins, header, footer, page numbers, Arabic RTL fonts. Public PDF links.
Pricing
from $2.40 / 1,000 pdf generateds
Rating
0.0
(0)
Developer
Malik Said
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Security
Limited permissionsShare
Turn an HTML or Markdown template into PDF files. Add a list of data rows, and the Actor makes one PDF per row. Use it for invoices, receipts, certificates, letters and reports. Each PDF gets a public download link in the dataset.
The template uses Handlebars fields such as {{customer}}. Rows can come from JSON, pasted CSV, an Apify dataset or a URL. You choose the page size, margins, header, footer and page numbers. Arabic and other right-to-left documents print correctly with bundled Arabic fonts.
What you can use it for
- Invoices and receipts from an order export, one PDF per order.
- Course certificates or badges from a list of names.
- Monthly reports with tables and charts, in English or Arabic.
- Letters, quotes and contracts from a spreadsheet.
- Markdown notes or documentation as clean printable PDFs.
- PDFs from the output of another Actor: pass its dataset ID.
How to use it
- Paste your template in Template. HTML gives full control with your own CSS. For Markdown, set Template type to Markdown.
- Add your rows in Data rows (JSON), Data rows (CSV), Apify dataset ID or Data URL.
- Set File name, for example
invoice-{{number}}. - Pick the page size, margins and an optional header and footer.
- Optional: turn on Also save one ZIP of all PDFs or Also save one merged PDF to get every document in one download or one printable file. Both are free.
- Run. Open the PDF links in the dataset, or use the ZIP and merged PDF links in the
BUNDLESrecord.
Leave the template empty to try the built-in sample: three two-language invoices.
Input example
{"template": "<h1>Invoice {{number}}</h1><p>Bill to: {{customer}}</p><table>{{#each items}}<tr><td>{{name}}</td><td>{{formatCurrency (multiply qty price) ../currency}}</td></tr>{{/each}}</table><p>Total: {{formatCurrency (sum items \"qty\" \"price\") currency}}</p>","data": [{ "number": "INV-1001", "customer": "Example Trading LLC", "currency": "USD", "items": [{ "name": "Design work", "qty": 10, "price": 45 }] }],"fileName": "invoice-{{number}}","pageFormat": "A4","marginTop": "20mm","footerTemplate": "<div style=\"text-align:center\">Invoice {{number}}, page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>"}
Template fields and helpers
Handlebars fills {{field}} with the value from the row and escapes HTML in it. Use {{{field}}} with three braces only for HTML you trust. Loops ({{#each items}}), conditions ({{#if paid}}) and nested fields ({{customer.name}}) work.
| Helper | Example | Result |
|---|---|---|
formatNumber | {{formatNumber 1234.5 decimals=2 locale="de-DE"}} | 1.234,50 |
formatCurrency | {{formatCurrency 99 "SAR" locale="ar-SA"}} | ٩٩٫٠٠ ر.س. |
formatDate | {{formatDate date style="long"}} | October 1, 2026 |
sum | {{sum items "qty" "price"}} | Sum of qty x price |
add, subtract, multiply, divide | {{multiply qty price}} | Number |
eq | {{#if (eq status "paid")}} | true or false |
upper, lower, default | {{default note "None"}} | Text |
json | {{{json labels}}} | JSON for a script, such as a chart |
Every row also gets _rowNumber (from 1), _index (from 0) and _runDate (today, YYYY-MM-DD).
Arabic and right-to-left documents
- Text direction Auto keeps the
dirof your HTML. Without one, the Actor picks right to left when most letters are Arabic or Hebrew. You can also force left to right or right to left. - Five fonts are bundled: Noto Sans, Noto Sans Arabic, Noto Naskh Arabic, Cairo and Amiri, in regular and bold. Use them by name in your CSS, for example
font-family: 'Cairo'. Default font sets the font for text your CSS does not style. - The default page-number footer is in Arabic for right-to-left pages: صفحة 1 من 3.
- Use CSS logical properties such as
text-align: startso one template works in both directions.
Images, fonts and scripts
The page has no network access by default. This keeps your data private and stops a template from reaching other systems. To load images, web fonts, CSS or chart libraries, add their hosts to Allowed asset hosts, for example cdn.jsdelivr.net, or fonts.googleapis.com and fonts.gstatic.com for Google Fonts. Subdomains are included.
- Requests to other hosts are blocked. The row counts them in
blockedRequestsand lists the hosts inblockedHosts. - Local and private network addresses are always refused, even when allowed.
- Each asset is fetched once per run and reused for every PDF, up to 15 MB per file.
- Images in the header and footer must be
data:URLs. This is a Chromium rule. - For charts, turn off animation and fixed-size canvases print best (Chart.js:
animation: false, responsive: false). - When a script fills the page late, set Wait for element to a CSS selector, for example
#chart[data-ready]. Each PDF prints as soon as that element exists, up to 20 s per row. A row whose element never appears is saved as failed with a reason and is free.
Output
One dataset row per data row. Generated PDFs are saved in the run's key-value store.
With the ZIP or merged PDF options on, the key-value store also holds all-pdfs.zip and all-pdfs.pdf (or numbered parts for very large runs). The BUNDLES record lists each file with its link, file count, size and, for the merged PDF, its page count. They are not charged and add no dataset rows.
| Field | Meaning |
|---|---|
rowNumber | Position of the data row, from 1. Rows can finish out of order |
status | generated, or failed with a reason |
fileName | The name from your File name template |
fileKey | Key in the key-value store. Letters other than a to z become dashes |
fileUrl | Public link to download the PDF |
fileSizeBytes, pageCount | Size and number of pages |
pageFormat, landscape, direction | How the PDF was printed |
blockedRequests, blockedHosts, assetErrors | Network requests that were blocked or failed |
renderMs | Time to render and save the PDF |
The row does not copy your data row.
Sample record from a test run of the default input:
{"id": "6c5e00e6325074a4","rowNumber": 1,"status": "generated","reason": null,"fileName": "invoice-INV-1001.pdf","fileKey": "invoice-INV-1001.pdf","fileUrl": "https://api.apify.com/v2/key-value-stores/y70ojoszSBrciNR8w/records/invoice-INV-1001.pdf?signature=1nveIAM9YRl1h0XwFpPZz","fileSizeBytes": 21569,"pageCount": 1,"pageFormat": "A4","landscape": false,"direction": "ltr","templateType": "html","blockedRequests": 0,"blockedHosts": [],"assetErrors": [],"renderMs": 451,"scrapedAt": "2026-10-22T09:18:42.527Z"}
Limits and memory
- One PDF may take up to 60 seconds. A slower page is retried twice, then saved as a free failed row.
- The Actor prints one PDF at a time. At the default 1,024 MB, a 1,000-invoice run finished with no failures in tests.
- Long documents need more memory. In tests, 100-page tables printed at 1,024 MB. 200-page tables needed 2,048 MB. Set the memory in the run options.
- A PDF that runs out of memory is saved as a free failed row. The other rows go on.
- Tagged PDF (for screen readers) makes files much larger. It is off by default.
- A ZIP part holds up to 1,000 PDFs or 1 GB. A merged PDF part holds up to 100 MB of PDFs. Larger runs get numbered parts. If a bundle cannot be built, each PDF keeps its own link and the run still succeeds.
Pricing
You pay a small start fee per run and a fee for each PDF generated. Failed rows are free. See the Pricing tab for the current prices.
Related Actor
To capture live web pages as screenshots or PDFs, use Website Screenshot & PDF Capture. This Actor is for documents you build from your own templates and data.
Support
Report a problem in the Issues tab. Include the run link and, if you can, a small template that shows the problem.