Counts below come from verify-textlayer.py (pypdf) — the same script wired into ArabDoc CI.
Pass requires zero presentation forms and non-zero base Arabic.
Be fair: with a real Arabic font, some libraries (e.g. react-pdf, DomPDF) can pass that numeric check
and still mangle shaping, table direction, or copy order — use the screenshots and paste samples.
jsPDF + reshaper often scores 0/0 (no real Arabic in the text layer at all).
Text layer (the part PNG review cannot see)
Engine
Base Arabic
Presentation forms
Verdict
jsPDF + arabic-reshaper + bidi-js
0
0
fail
@react-pdf/renderer (direction: rtl)
199
0
pass
pdfmake + Noto Naskh Arabic
0
0
fail
DomPDF (PHP, DejaVu, dir=rtl)
165
0
pass
ArabDoc (Chromium + ToUnicode NFKC)
535
0
pass
What the correct line should be
فاتورة ضريبية — رقم INV-2026-08421 — البريد billing@nukhbat-jazira.sa
About the paste samples below.
They are raw text-extractor output (pdfjs), not what a human reader sees after BiDi.
Logical Arabic order can look scrambled in a plain textarea; that alone is not a fail.
The scored metric is codepoint counts: presentation forms must be 0 and base Arabic > 0.
Crops and full-page PNGs show visual shaping; download each PDF to copy from a real viewer yourself.
Zoomed failure crops
Same regions, 2× screenshots — failures should be obvious without zooming the full page.
Ligatures / letter joining
Header + seller name. Detached glyphs vs joined Naskh/Sans shaping.
jspdfreact-pdfarabdoc
Table column order
Line-item table. First logical column should be rightmost in RTL.
jspdfdompdfarabdoc
Latin isolation
IDs, emails, iPad 11", +12.4% — must not jump to the wrong side.
pdfmakereact-pdfarabdoc
Side by side
jsPDF + arabic-reshaper + bidi-js
FAILbase Arabic: 0presentation forms: 0
→ PASS on forms
Raw extractor output (pdfjs text layer), not a reading-order transcription.
Word order may look wrong for Arabic; we score codepoints
(presentation forms U+FB50–FEFF), not display order.
Prefer downloading the PDF and copying from a real viewer if you want the browser’s BiDi reordering.
PASSbase Arabic: 199presentation forms: 0
→ PASS on forms
Raw extractor output (pdfjs text layer), not a reading-order transcription.
Word order may look wrong for Arabic; we score codepoints
(presentation forms U+FB50–FEFF), not display order.
Prefer downloading the PDF and copying from a real viewer if you want the browser’s BiDi reordering.
FAILbase Arabic: 0presentation forms: 0
→ PASS on forms
Raw extractor output (pdfjs text layer), not a reading-order transcription.
Word order may look wrong for Arabic; we score codepoints
(presentation forms U+FB50–FEFF), not display order.
Prefer downloading the PDF and copying from a real viewer if you want the browser’s BiDi reordering.
PASSbase Arabic: 165presentation forms: 0
→ PASS on forms
Raw extractor output (pdfjs text layer), not a reading-order transcription.
Word order may look wrong for Arabic; we score codepoints
(presentation forms U+FB50–FEFF), not display order.
Prefer downloading the PDF and copying from a real viewer if you want the browser’s BiDi reordering.
PASSbase Arabic: 535presentation forms: 0
→ PASS on forms
Raw extractor output (pdfjs text layer), not a reading-order transcription.
Word order may look wrong for Arabic; we score codepoints
(presentation forms U+FB50–FEFF), not display order.
Prefer downloading the PDF and copying from a real viewer if you want the browser’s BiDi reordering.
From a clone of the ArabDoc repo, run the benchmark package to regenerate these numbers and this page.
Setup details (including a live API target) are in the repository README / benchmark/README.md.