Arabic and Hebrew PDFs from HTML: fixing right-to-left layout
Once the letters show up (and not as boxes), the next problem is usually layout: text hugging the wrong edge, table columns in the wrong order, or an invoice number and punctuation jumping around inside a sentence. This guide covers the right-to-left settings that matter when you turn HTML into a PDF with Puppeteer, headless Chrome or an API.
Set the direction Write direction-neutral CSS Mixed-direction text Full example One API call FAQ
Set the direction on the document
Put dir="rtl" and a matching lang on your <html> tag: lang="ar" for Arabic, lang="he" for Hebrew. This sets the base direction of the whole page. Text blocks align to the right, lists and tables flow from the right, and punctuation lands on the correct side.
Chrome joins Arabic letters and orders mixed text for you, as long as a suitable font is installed. If you see boxes instead of letters, that is a font problem, covered in our guide to empty boxes in Puppeteer PDFs.
Write direction-neutral CSS
Hard-coded left and right values are the most common reason a layout looks wrong in Arabic or Hebrew. Use logical properties, which follow the document direction:
text-align: startinstead oftext-align: leftmargin-inline-startandpadding-inline-endinstead ofmargin-leftandpadding-rightinset-inline-startinstead ofleftfor positioned elements
Tables need no special handling: with dir="rtl" the first column appears on the right. Be careful with letter-spacing on Arabic text, because it can break the joining of letters. Numbers stay as you wrote them: Chrome does not convert 123 into Arabic-Indic digits, so send the digits you want printed.
Mixed-direction text: invoice numbers, emails, URLs
An Arabic or Hebrew sentence that contains an invoice number, an email address or a product code mixes two directions. The browser’s bidirectional algorithm usually handles it, but punctuation next to the left-to-right part can end up on the wrong side.
The fix is to isolate the left-to-right piece. Wrap it in <bdi>, or use <span dir="ltr">. Dynamic values, such as names and codes that come from a database, are the ones to wrap, because you cannot predict their direction.
Full example
An Arabic invoice with the direction set, logical CSS, an isolated invoice number and a table:
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8">
<style>
body { font-family: "Noto Naskh Arabic", "Noto Sans Arabic", sans-serif; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: start; border-bottom: 1px solid #ccd; }
.note { margin-inline-start: 24px; }
</style>
</head>
<body>
<h1>فاتورة</h1>
<p>رقم الفاتورة: <bdi>INV-00123</bdi></p>
<table>
<tr><th>الوصف</th><th>الكمية</th><th>السعر</th></tr>
<tr><td>خدمات استشارية</td><td>2</td><td>500.00</td></tr>
</table>
</body>
</html>Printing it with Puppeteer takes only the usual call. Make sure the machine has an Arabic font installed first.
await page.setContent(html);
await page.pdf({
path: "invoice.pdf",
format: "A4",
printBackground: true,
});The same result with one API call
Getprintoza is an HTML to PDF API that runs real Chromium with fonts for 12 languages already installed, including Arabic and Hebrew. Send your HTML and the language; Arabic and Hebrew documents are set to right-to-left for you, and footer: true adds a page footer with page numbers that follows the document language.
curl -X POST https://getprintoza.com/v1/convert \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"language": "ar", "footer": true, "html": "<h1>فاتورة</h1><p>رقم الفاتورة: <bdi>INV-00123</bdi></p>"}' \
--output invoice.pdfIf your HTML contains characters that none of the installed fonts can draw, the PDF is still created and the response carries an X-Getprintoza-Warnings header that lists them. This is a best-effort check, so look at a few real documents before you launch. Details are in the API documentation. The Free plan includes 50 PDFs a month and needs no card: get a free API key or see the pricing.
FAQ
My Arabic text is correct but aligned to the left. Why?
The document direction is still left-to-right. Add dir="rtl" to the <html> tag, or set direction: rtl on the container.
Do Hebrew documents need different settings from Arabic?
No. Use lang="he" and dir="rtl". Hebrew does not join letters, so fonts are simpler, but the layout rules are the same.
Parentheses or a dash appear on the wrong side of a number. What now?
Isolate the number or code with <bdi> or <span dir="ltr">, as shown above.
Should I mirror images and icons?
Only those that show direction, such as arrows. Logos and photos stay as they are.