If you've ever tried to generate a Thai tax invoice (ใบกำกับภาษี) with one of those "HTML-to-PDF" libraries, you know the pain: missing glyphs, tofu boxes (□□□), broken flexbox, or worse — the library renders English fine but Thai shows up as gibberish.
Most of the open-source options use an outdated rendering engine. Real Chromium is the only thing that handles Thai fonts and modern CSS correctly — but spinning up Playwright in every project is overkill.
So I built an API that does it for you.
Try it: HTML to PDF API — free tier, Thai fonts built-in, no signup.
What It Does
Send HTML (or a URL). Get back a PDF.
- ✅ Thai fonts pre-installed — Loma, Garuda, Noto Sans Thai
- ✅ Real Chromium rendering — CSS Grid, flexbox, web fonts,
@pagerules, SVG, canvas - ✅ Headers & footers with page numbers — perfect for multi-page invoices
- ✅ Custom paper sizes — A4, A3, A5, Letter, Legal, Tabloid, landscape
- ✅ Binary or base64 output — your choice
The API auto-injects a Thai-optimized font stack when you set preferThaiFont: true (default), so you don't need to manually configure @font-face rules.
Quick Start — Thai Receipt
curl -X POST https://hubaiasia.com/html-pdf-api/pdf \
-H "X-Api-Key: demo-key-html-pdf" \
-H "Content-Type: application/json" \
-o receipt.pdf \
-d '{
"html": "<h1>ใบเสร็จรับเงิน</h1><p>ลูกค้า: สมชาย ใจดี</p><p>ยอดรวม 1,500 บาท</p>",
"format": "A4",
"filename": "receipt.pdf"
}'
Open receipt.pdf — Thai characters render perfectly, no font config needed.
Real Use Case: E-commerce Order Receipt (Node.js)
async function generateReceipt(order) {
const html = `
<!DOCTYPE html>
<html lang="th">
<head>
<style>
body { padding: 40px; font-size: 14px; }
h1 { color: #2563eb; border-bottom: 2px solid #e5e7eb; padding-bottom: 12px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; border-bottom: 1px solid #e5e7eb; text-align: left; }
th { background: #f8fafc; }
.total { font-size: 20px; font-weight: 800; text-align: right; margin-top: 20px; }
</style>
</head>
<body>
<h1>ใบเสร็จรับเงิน #${order.id}</h1>
<p><strong>ลูกค้า:</strong> ${order.customerName}</p>
<p><strong>วันที่:</strong> ${order.date}</p>
<table>
<tr><th>รายการ</th><th>จำนวน</th><th>ราคา</th></tr>
${order.items.map(i => `
<tr>
<td>${i.name}</td>
<td>${i.qty}</td>
<td>${i.price.toLocaleString()} บาท</td>
</tr>
`).join('')}
</table>
<div class="total">รวมทั้งสิ้น: ${order.total.toLocaleString()} บาท</div>
</body>
</html>
`;
const r = await fetch('https://hubaiasia.com/html-pdf-api/pdf', {
method: 'POST',
headers: {
'X-Api-Key': process.env.HTML_PDF_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html,
format: 'A4',
filename: `receipt-${order.id}.pdf`,
margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' },
}),
});
return Buffer.from(await r.arrayBuffer());
}
Advanced: Headers, Footers, Page Numbers
For multi-page reports or contracts:
{"html":"<h1>Annual Report 2026</h1>...","format":"A4","displayHeaderFooter":true,"headerTemplate":"<div style='font-size:9px;width:100%;text-align:right;padding-right:15mm;'>ACME Corp</div>","footerTemplate":"<div style='font-size:9px;width:100%;text-align:center;'>Page <span class='pageNumber'></span> / <span class='totalPages'></span></div>","margin":{"top":"25mm","bottom":"25mm","left":"15mm","right":"15mm"}}
Note: use .pageNumber and .totalPages class names — Chromium replaces those at render time.
From URL Instead of HTML
Want to archive a web page? Pass url instead:
curl -X POST https://hubaiasia.com/html-pdf-api/pdf \
-H "X-Api-Key: demo-key-html-pdf" \
-H "Content-Type: application/json" \
-o archive.pdf \
-d '{"url": "https://news.example.com/article-2026", "format": "A4"}'
The API waits for the page to finish loading (networkidle) before rendering, so dynamic content loaded via JavaScript ends up in the PDF too.
Why Not Use Puppeteer Directly?
You can — but you'll be:
- Installing Chromium in every project (~170MB)
- Managing Thai/CJK font packages
- Handling browser lifecycle (launch, crash, memory leaks)
- Paying for bigger servers because headless Chrome is memory-hungry
The API handles all that. You just call fetch and get a PDF.
Performance
From my tests:
- Single page, simple HTML: ~0.8s
- Single page, complex CSS Grid + images: ~1.5s
- Multi-page report (5 pages): ~2.8s
- Thai tax invoice with header/footer: ~1.8s
The browser stays warm between requests, so there's no cold-start penalty.
Pricing
-
Free: 100 PDFs/day — use
demo-key-html-pdf - Basic: $12/mo — 3,000 PDFs/day
- Pro: $39/mo — 20,000 PDFs/day
- Ultra: $129/mo — 100,000 PDFs/day
Higher pricing than text-only APIs because Chromium is expensive to run.
Use Cases
- 🇹🇭 Thai tax invoices (ใบกำกับภาษี / ใบเสร็จรับเงิน) with proper font rendering
- 🛒 E-commerce order receipts — generate on checkout
- 📄 Contract & quotation generation from templates + data
- 📊 Dashboard-to-PDF exports for reports
- 🎓 Certificates & tickets for events and courses
- 📑 Web page archival for compliance or legal
Try It Now
👉 HTML to PDF API docs — interactive demo, free tier.
The demo widget on the docs page lets you paste HTML and preview the generated PDF instantly.
Built by HubAI Asia — because I was tired of fighting with Thai fonts in wkhtmltopdf.