← dompdf.js Studio

RTL and Arabic Text in dompdf.js: A Practical Guide

Global products eventually face right-to-left (RTL) text: Arabic, Hebrew, Persian, and Urdu are written from right to left, paragraphs start at the right edge, and punctuation, numbers, and mixed-script ordering each follow their own rules. With a library that does not handle RTL, a generated PDF either scrambles word order or becomes unreadable outright, and a product delivered to Middle Eastern or Arabic-speaking users fails at the moment it should impress them. dompdf.js processes bidirectional text through a real typesetting engine, supporting the dir attribute, direction-aware text-align behavior, and Arabic ligatures and glyph shaping, so RTL layout that is correct in the browser is correct in the PDF. This guide builds the subject from the ground up: what RTL means for layout beyond the obvious direction flip, how the bidirectional algorithm orders mixed content, how to write templates that combine RTL paragraphs with Latin fragments, how Arabic shaping and ligatures actually work and what can break them, and the specific rules for numbers and punctuation. The closing section collects the failure modes that trip up real projects. By the end you will be able to ship Arabic and Hebrew PDF exports with confidence instead of treating them as an unverifiable special case, and your multilingual templates will handle direction as a designed property rather than an accident. Along the way, expect to revisit assumptions you did not know you had: the direction of a table's first column, the side a checkmark belongs on, the order of a signature block. Each of these is a direction decision, and templates that make them explicitly are the ones that survive contact with real Arabic-speaking users.

RTL Languages and the Basics of Direction

RTL languages include Arabic, Hebrew, Persian, and Urdu, covering a large share of users across the Middle East, North Africa, and South Asia. Their text flows from right to left, paragraphs start at the right edge and wrap leftward, and punctuation follows mirrored conventions: an opening parenthesis appears at the visual right in RTL context, the opposite of LTR. These differences are not cosmetic — they are part of how the script is read, and they must be preserved exactly in printed and exported documents.

Direction is a document-level property. The HTML dir attribute (ltr, rtl, or auto) sets the writing direction of an element, and the browser derives text ordering, alignment defaults, and punctuation mirroring from it. dompdf.js inherits this direction system when it renders the DOM, so a template with dir="rtl" produces paragraphs flowing right to left in the PDF exactly as in the browser, with no coordinate-level manipulation and no extra library configuration.

RTL affects far more than text: list markers move to the right side, table columns flow right to left, the default text-align flips to the right, and flex containers reverse their main axis. Designing an RTL template means accounting for these coupled effects. Changing only the text direction while leaving the layout untouched produces content in the right order but visually broken structure, so RTL templates need full-page review rather than paragraph-level spot checks.

The flip side is that well-built LTR templates can become RTL-ready cheaply: logical properties and direction-aware layout (flex with row-reverse where appropriate, start/end alignment) let one template serve both directions, which is the long-term architecture for multilingual products. Budget for a dedicated RTL QA pass in every release cycle: direction bugs are cheap to fix when found early and expensive when a customer reports them, because they touch layout, extraction, and localization at once.

Bidirectional Text and the bidi Algorithm

RTL documents routinely embed LTR content: English product names inside Arabic articles, phone numbers, URLs, variable names. The bidirectional algorithm decides how these mixed runs display. Its core rules: each character keeps its own direction, the paragraph flows according to its base direction, and adjacent runs of different directions reorder by nesting level so the final visual order is correct. The algorithm is part of the typesetting engine, so dompdf.js inherits it; you never implement character reordering yourself.

What developers do need to understand is how the base direction is determined: the element's dir attribute wins over its parent, which wins over the document default. When direction is genuinely unknown, dir="auto" lets the engine infer it from the first strong-directional character, which is ideal for user-generated fields that may contain English or Arabic. One attribute handles input you cannot predict, and it removes a whole class of ordering bugs from multilingual forms.

In practice, most bidi bugs live in the data layer, not the rendering layer. Concatenating RTL and LTR text into one string — an order label plus an ID, a name plus an email — mixes directions so the display order can scramble. The correct pattern is to keep each text fragment in its own element and let the engine render each by its own direction, or to mark boundaries explicitly with Unicode direction control characters. Keep the data layer clean and the rendering layer follows.

A debugging note: bidi ordering bugs look identical to encoding bugs, and teams waste time chasing character sets. If the text decodes correctly but reads in the wrong order, the problem is direction handling, not encoding — check the base direction of the container before touching anything else.

Code Example: RTL Paragraphs and Mixed-Script Text

Setting direction: rtl or dir="rtl" establishes the base direction, paragraphs flow right to left, and text-align: right matches the natural alignment of RTL paragraphs. Arabic copy needs an actual Arabic font to render correctly; dompdf.js loads faces like Noto Naskh Arabic through @font-face, and every rule from the custom-fonts guide applies — including awaiting document.fonts.ready before generation, because an unready Arabic font falls back to a face without Arabic coverage and produces broken glyphs.

For mixed content, wrap the LTR fragment in a span with direction: ltr and unicode-bidi: isolate. The isolate keyword keeps the inner direction from influencing the outer paragraph's direction resolution, so order numbers, emails, and URLs read left to right inside an RTL paragraph while the surrounding Arabic flows correctly. Without isolation, neighboring text can interfere with each other's direction resolution, producing ordering bugs that are hard to reproduce and harder to explain.

Numbers deserve their own consideration. Western digits (0-9) normally stay LTR in Arabic contexts, while Arabic-Indic digits (٠-٩) follow RTL ordering. font-variant-numeric: tabular-nums makes digits monospaced for aligned columns of amounts and quantities. For critical data such as amounts and dates, wrap the value in an explicitly directed element instead of relying on implicit rules, so the PDF always shows the digits in the correct order regardless of surrounding text.

Test with real Arabic strings from your content, not transliterations or placeholder text. Shaping behavior is sensitive to the exact characters used, and a template verified with sample text can still surprise you with production copy — so make the verification loop use real data from day one.

<style>
  body { direction: rtl; font-family: 'Noto Naskh Arabic', 'Source Han Sans SC', serif; }
  .rtl-paragraph { text-align: right; }
  .ltr-inline { direction: ltr; unicode-bidi: isolate; }
</style>
<p class="rtl-paragraph">هذا نص عربي يوضح دعم اتجاه النص من اليمين إلى اليسار</p>
<p class="rtl-paragraph">
  Order number: <span class="ltr-inline">ORD-2026-0818</span> confirmed successfully
</p>
<p class="rtl-paragraph">
  Email: <span dir="ltr">support@example.com</span>
</p>

Arabic Ligatures and Glyph Shaping

Arabic letters have four positional forms — isolated, initial, medial, and final — and the position of a letter within a word decides which form is used. This process, called shaping, joins the letters into the connected cursive appearance that defines Arabic writing. dompdf.js reuses the browser engine's shaping capability, so Arabic text in templates automatically gets the correct positional forms and connections, exactly as rendered in the browser, with no developer intervention.

The trap is that shaping requires the font to actually contain Arabic glyphs and the shaping tables (GSUB/GPOS) that map character sequences to joined forms. Without them, even a correct character stream renders as disconnected isolated letters — the text looks like it has been disassembled. To diagnose, first confirm the font declares Arabic support, then render the same text in the browser and compare: matching output means shaping works; a visible difference points at the font or its loading path, and the custom-fonts troubleshooting steps apply.

Punctuation and symbols mirror automatically: parentheses, quotes, and angle brackets render mirrored in RTL context as a standard engine behavior. Images and charts, however, do not mirror — their internal text direction is fixed at design time, so mirrored artwork must be prepared manually for RTL locales. Building a visual acceptance pass with real Arabic text before release is dramatically cheaper than discovering a shaping or mirroring problem in a customer-facing document.

Ligatures beyond the standard joins, such as lam-alef combinations, are handled by the same shaping tables; if a specific ligature renders oddly, verify the font actually includes it rather than assuming the engine synthesizes it, because engines render what the font provides. When selecting an Arabic font, verify it covers the specific script variants your content uses — not every Arabic font includes every character used across the Arab world, and a missing glyph in a delivered document is the kind of defect that erodes trust.

Numbers, Punctuation, and Mixed-Direction Details

Number direction in RTL documents follows Unicode's European Number rules: Western digits keep a left-to-right order overall inside RTL paragraphs, and a grouped number like 1,234 preserves its internal order. Arabic-Indic digits differ in both direction and shape, so mixing the two digit systems in one document produces inconsistent alignment in amounts and tables. Pick one digit system per document and enforce it in the data layer, and the columns stay neat.

Punctuation direction follows context: commas and periods in an RTL paragraph adopt RTL behavior, while punctuation inside an isolated LTR fragment keeps LTR behavior. unicode-bidi: isolate is the key tool here again — it confines punctuation direction resolution to inside the element, preventing punctuation from drifting to a visually wrong position. In complex mixed paragraphs, set direction explicitly on each language fragment instead of relying on inference, and the rendering becomes deterministic.

URLs and emails inside RTL paragraphs are another subtle case: internally they are LTR sequences, but as whole units inside an RTL paragraph their position relative to surrounding Arabic must be correct. Wrapping them in an isolated span is the safest pattern, combined with overflow-wrap: break-word so long URLs wrap inside narrow containers instead of overflowing. RTL handling and wrapping strategy work together; the full toolbox is needed for polished multilingual documents.

For dates and times, decide the format at the template level: an Arabic date in Hijri calendar format and an ISO date in Western digits are both valid, but mixing them across a document reads as inconsistent. Standardize the format per locale and let the direction rules handle the rest.

Measuring and dates deserve their own rule: in RTL locales, prefer a locale-aware formatter over hand-assembled strings, because the order of day, month, and year differs across Arabic-speaking regions. A hand-built date string that reads correctly for one market may be wrong for another, while a formatter driven by the user's locale produces the expected order every time and keeps the template free of language-specific logic.

Common Problems and Best Practices

Q: The Arabic PDF renders in scrambled order. A: Check whether the template sets dir="rtl" or direction: rtl. Without a declared direction the engine processes the paragraph as LTR; the Arabic characters keep their own shapes but the paragraph flow and mixed ordering go wrong. Setting the correct direction on the container resolves the vast majority of ordering complaints instantly.

Q: Arabic letters render as isolated forms instead of connected script. A: Confirm the font supports Arabic shaping first; if the font is fine, verify document.fonts.ready was awaited. An unready font falls back to whatever face is available, and a fallback without Arabic coverage degrades to isolated letterforms. Check both links in the chain before looking elsewhere.

Q: Numbers inside Arabic text appear in the wrong order. A: Verify which digit system the content uses and whether the number is inside an isolated LTR span. Western digits keep LTR order naturally, but Arabic-Indic digits follow RTL behavior; mixing systems or missing isolation are the usual causes, and both are template-level fixes.

Best-practice summary: set dir on the document root; use a dedicated Arabic font for Arabic body copy; wrap mixed fragments in unicode-bidi: isolate; await font readiness before generation; and run a visual acceptance pass with real text before release. These five practices make RTL and Arabic PDF output a solved problem, and multilingual products keep their quality bar in every locale.

Q: The layout looks right, but extracted text is not in the expected order. A: Text extraction follows the logical order of the content, not its visual order, and in RTL documents the two can diverge when direction is applied inconsistently. Keep direction on containers and isolated spans rather than on individual characters, and verify extraction order in the PDF viewer as part of the acceptance pass, because a visually correct page can still extract in a confusing sequence for assistive technology or copy-paste.

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。