Render right-to-left Arabic HTML
At a glance
Section titled “At a glance”Render right-to-left (RTL) HTML to PDF with writeHtml(). Set the CSS direction: rtl property and register an Arabic-capable font. The engine reorders text to visual order with the Unicode Bidirectional Algorithm (UAX #9) and shapes Arabic letters into contextual forms. This recipe renders a small Arabic invoice. RTL applies to Arabic, Hebrew, Persian, and Urdu. Hebrew is reordered but not shaped, which is correct for that script.
Install
Section titled “Install”composer require nextpdf/coreYou also need an Arabic-capable TrueType or OpenType font. Its character map must cover the Arabic Presentation Forms-B block. Noto Naskh Arabic and Amiri are suitable open-licensed faces.
Conceptual overview
Section titled “Conceptual overview”You need two inputs for RTL rendering: the CSS direction: rtl property and a registered Arabic font.
direction: rtl tells the layout to place text from right to left. The engine then uses the Unicode Bidirectional Algorithm (UAX #9) to resolve visual order. Mixed content orders correctly: Latin words, Arabic words, and digits each keep their own direction. A number that follows Arabic text keeps its digits left to right.
Arabic is a cursive script, so each letter uses a different glyph based on its neighbors. The engine selects the initial, medial, final, or isolated form for each letter and applies the Lam-Alef ligature. This contextual shaping needs a font whose character map covers the Arabic Presentation Forms-B block. A Latin-only font, including the standard-14 faces, cannot draw Arabic.
In a table, each cell is reordered and shaped on its own, and cells align to the start edge: the right edge under direction: rtl. The logical text-align values start and end resolve against the direction, so start maps to the right edge for RTL content.
Set direction with the CSS direction property. The HTML dir attribute does not map to it. See RTL — current limitations for the current implementation boundaries.
API surface
Section titled “API surface”| Symbol | Location | Role |
|---|---|---|
Document::writeHtml(string $html): static | NextPDF\Core\Concerns\HasTextOutput | Render the HTML fragment at the current cursor position. |
FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo | NextPDF\Typography\FontRegistry | Register the Arabic face under an alias. |
DocumentFactory::create(): Document | NextPDF\Core\DocumentFactory | Create a document that reads your populated registry. |
The example uses these CSS properties: direction, font-family, text-align. Reference the registered font in CSS font-family by its registry alias.
Code sample — Quick start
Section titled “Code sample — Quick start”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
$fontRegistry = new FontRegistry();$fontRegistry->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'ArabicFont');
$documentFactory = new DocumentFactory($fontRegistry, new ImageRegistry(maxCacheBytes: 0));$doc = $documentFactory->create();$doc->addPage();
$doc->writeHtml( '<div style="direction: rtl; font-family: \'ArabicFont\';">' . '<h1>فاتورة</h1>' . '<p>المبلغ الإجمالي 380.00</p>' . '</div>');
$doc->save(__DIR__ . '/rtl-arabic.pdf');The heading renders right to left, and the digits 380.00 stay left to right inside the Arabic sentence.
Code sample — Production
Section titled “Code sample — Production”This self-contained example renders an Arabic invoice table. Each cell carries direction: rtl and the registered Arabic font, so the engine reorders and shapes every line, then aligns the cells to the right edge.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
// Supply an Arabic-capable face whose cmap covers Arabic Presentation Forms-B.// Embed only fonts you are licensed to embed.$fontPath = __DIR__ . '/NotoNaskhArabic-Regular.ttf';if (!is_file($fontPath)) { fwrite(STDERR, "Arabic font not found at {$fontPath}\n"); exit(1);}
$fontRegistry = new FontRegistry();$fontRegistry->register($fontPath, alias: 'ArabicFont');
$documentFactory = new DocumentFactory($fontRegistry, new ImageRegistry(maxCacheBytes: 0));$doc = $documentFactory->create();$doc->setTitle('Arabic invoice');$doc->addPage();
$html = <<<'HTML'<div style="direction: rtl; font-family: 'ArabicFont'; font-size: 12pt;"> <h1>فاتورة</h1> <table style="width: 100%; border-collapse: collapse;"> <tr> <th style="border: 1px solid #333; padding: 6px;">الوصف</th> <th style="border: 1px solid #333; padding: 6px;">المبلغ</th> </tr> <tr> <td style="border: 1px solid #333; padding: 6px;">خدمات استشارية</td> <td style="border: 1px solid #333; padding: 6px;">380.00</td> </tr> <tr> <td style="border: 1px solid #333; padding: 6px;">الإجمالي</td> <td style="border: 1px solid #333; padding: 6px;">380.00</td> </tr> </table></div>HTML;
$doc->writeHtml($html);
$out = getenv('NEXTPDF_OUT');$doc->save($out !== false ? $out : __DIR__ . '/render-rtl-arabic-html.pdf');
echo "Wrote the Arabic invoice PDF\n";Edge cases & gotchas
Section titled “Edge cases & gotchas”- Register the font before you build the document.
Document::createStandalone()constructs its own registry and cannot see a face you registered elsewhere. Build throughDocumentFactoryso the writer reads your registry, as both samples do. - Match the CSS
font-familyto the registry alias. The name you pass toregister(..., alias: 'ArabicFont')is the name you reference in CSS. - Use CSS
direction, not the HTMLdirattribute. Only the CSS property switches layout. - A number after Arabic stays left to right. This follows UAX #9: a European number after an Arabic letter resolves to an Arabic number and keeps its digit order, so
380.00is not reversed.
RTL — current limitations
Section titled “RTL — current limitations”The current implementation reorders and shapes RTL text and aligns table cells. These boundaries remain. Each needs a future per-line inline-formatting line box:
- Block and inline alignment outside tables. Block-level and inline text outside table cells is reordered and shaped, but it renders from the start (left) edge. Right or center alignment of non-table text, and
text-align: justifydistribution, are not yet applied. Table cells do align. - The HTML
dirattribute does not map todirection. Set direction with the CSSdirectionproperty. - Bidirectional resolution is per text run. Neutral characters are not resolved across two inline elements, such as a
<span>next to a<b>, on the same line. - Narrow Arabic columns measure on logical text. Line breaks are measured on the logical, pre-shaping text, so a very narrow Arabic column can break a line slightly early or late.
- Shaped Arabic needs Presentation Forms-B coverage. The face must cover the Arabic Presentation Forms-B block. Fonts that rely only on OpenType GSUB substitution, and the HarfBuzz shaping path, are future work. A non-Arabic font cannot draw Arabic.
Performance
Section titled “Performance”Rendering scales linearly with glyph count. Bidirectional reordering and contextual shaping run per line and add a small constant factor over left-to-right text. This recipe’s budget is wall_ms: 1500, peak_mb: 64.
Security notes
Section titled “Security notes”Validate the length of user-supplied strings to keep output size bounded. The engine renders text, does not interpret it, and runs no script. If you load a font through a remote @font-face source, the secure external-resource policy governs the fetch; prefer a local font file for predictable output.
Conformance
Section titled “Conformance”| Statement | Spec | Clause | reference_id |
|---|---|---|---|
| Visual order is produced by reversing character runs from the highest level down to the lowest odd level. | Unicode UAX #9 | §3.3.6 Rule L2 (uax9#3.3.6.p13) | 814977a77019d728dc562a612098a82dc260f6844f5998eca5fe7a3baf3394af |
| A European number after an Arabic letter resolves to an Arabic number, so its digits keep left-to-right order. | Unicode UAX #9 | §3.3.4 Rule W2 (uax9#3.3.4.p9) | 5747405357772797d62b3f4ba79328557fa0c4273a1dd5ffa8d996f24c78e120 |
Arabic contextual shaping (initial, medial, final, and isolated forms plus the Lam-Alef ligature) is a verified engine capability covered by the test suite, not a separate conformance claim. It requires a font whose character map covers the Arabic Presentation Forms-B block.
Commercial context
Section titled “Commercial context”Not applicable.
See also
Section titled “See also”- HTML: HTML+CSS to PDF rendering subsystem — the
writeHtml()engine and its RTL support. - Typography: font registry, subsetting, CMap, encoding, BiDi — the bidirectional engine that resolves UAX #9.
- Font and script support matrix — which scripts each font class can render.
- Render HTML to a PDF page — the left-to-right starting point.