RTL and Bidirectional Text Support#
Full right-to-left (RTL) text support for Arabic, Farsi, and Hebrew is a known, unresolved limitation in Docling. Maintainers have acknowledged the gap and placed it on the roadmap, tracked in issue #455. Resolution is dependent on completing upstream work in the docling-parse library .
There are two distinct sub-problems:
- Extraction / BiDi reordering — The pipeline does not reorder RTL or bidirectional text during extraction. Arabic, Farsi, and Hebrew text is typically output reversed at both character and word levels.
- Output serialization —
dir="rtl"attributes on HTML elements are implemented, using a post-extraction Unicode analysis pass.
Mixed Arabic-English (bidirectional) documents are particularly problematic: BiDi reordering must occur at the paragraph level, not per-glyph, which makes it non-trivial to add .
Extraction Pipeline: What Is Not Working#
The docling-parse C++ backend extracts text cells with no BiDi reordering pass. Extracted Arabic text routinely appears reversed at both the character and word level (see issue #1938, issue #2179) .
A TextDirection enum (LEFT_TO_RIGHT, RIGHT_TO_LEFT, UNSPECIFIED) exists in docling_core/types/doc/page.py and is stored on TextCell / PdfTextCell, but it is not used to reorder text during extraction.
Default OCR has no Arabic coverage. The default OCR engine (RapidOCR) bundles only chinese, english, and latin model sets — there are no Arabic, Hebrew, or Farsi models . The default language lists for all engines (["fra","deu","spa","eng"] for Tesseract; ["fr","de","es","en"] for EasyOCR) also exclude Arabic.
An additional complication: some PDFs store Arabic text in visual order (a pre-Unicode legacy encoding) rather than logical order. This produces garbled output even from OCR, because the glyph sequence itself is backwards. For these, bypassing the native text layer via force_full_page_ocr=True is the most effective path.
Output Serialization: What Is Implemented#
Text direction detection after extraction is implemented in docling-core. The get_text_direction() function in docling_core/types/doc/utils.py determines direction using Unicode bidirectional character properties (unicodedata.bidirectional). It returns "rtl" if the first character belongs to Unicode category R or AL (Arabic Letter), or if more than 50% of characters are RTL.
The HTML serializer in docling_core/transforms/serializer/html.py uses get_html_tag_with_text_direction() to apply dir="rtl" attributes on: headings (<h1>–<h6>), paragraphs (<p>), list items (<li>), table cells (<td>, <th>), captions, and picture annotations.
The DocLang serializer uses a dedicated self-closing <rtl/> token to mark RTL text regions.
Caveat: These serialization fixes operate on already-extracted (and potentially already-reversed) text. They add correct HTML directionality for rendering, but do not fix character/word ordering errors from the extraction stage.
Workarounds#
For image-based / scanned PDFs#
Switch from RapidOCR (default) to Tesseract or EasyOCR with an Arabic language model:
Tesseract — requires ara.traineddata installed and TESSDATA_PREFIX set:
from docling.datamodel.pipeline_options import PdfPipelineOptions, TesseractCliOcrOptions
pipeline_options = PdfPipelineOptions(
do_ocr=True,
ocr_options=TesseractCliOcrOptions(lang=["ara", "eng"])
)
EasyOCR — downloads the Arabic model automatically:
from docling.datamodel.pipeline_options import PdfPipelineOptions, EasyOcrOptions
pipeline_options = PdfPipelineOptions(
do_ocr=True,
ocr_options=EasyOcrOptions(lang=["ar", "en"])
)
For PDFs with a native text layer (non-scanned)#
Force full-page OCR to bypass the broken native text extraction:
pipeline_options = PdfPipelineOptions(do_ocr=True)
pipeline_options.force_full_page_ocr = True
pipeline_options.ocr_options = EasyOcrOptions(lang=["ar", "en"])
Post-processing fix (all cases)#
Apply python-bidi and arabic-reshaper after extraction to reorder and reshape Arabic text:
from bidi.algorithm import get_display
import arabic_reshaper
def fix_arabic_text(text):
reshaped = arabic_reshaper.reshape(text)
return get_display(reshaped)
Key References#
| Resource | Notes |
|---|---|
| Issue #455 — RTL PDF text | Primary tracking issue; roadmap item |
| Issue #3462 — Mixed Arabic-English PDFs | Workaround discussion |
| Issue #3021 — RapidOCR Arabic support | No Arabic models in default engine |
| Issue #253 — BiDi reordering | Paragraph-level BiDi requirement |
docling_core/types/doc/utils.py | get_text_direction(), get_html_tag_with_text_direction() |
docling_core/transforms/serializer/html.py | HTML dir="rtl" serialization |
| OCR Language Support KB article | Engine-by-engine language config reference |