Do you need OCR at all?#
Open the PDF and try to select a sentence. If you can, the PDF has a text layer and Docling reads it directly — exact characters, no OCR needed. In that case OCR only adds text found inside images, at a high price:
| Our 9-page born-digital paper | Time per page | Markdown characters |
|---|---|---|
| Default (OCR on bitmap regions) | 6.58 s | 34,063 |
--no-ocr | 1.83 s | 29,036 |
The extra 5,000 characters came from screenshots inside the paper's figures. Keep OCR when such text matters; otherwise switch it off.
Pick an engine#
The engines#
--ocr-engine | What it is | Install | Platforms | Default languages |
|---|---|---|---|---|
auto (default) | Picks the first usable engine (see below) | — | All | engine's own |
rapidocr | PaddleOCR (PP-OCR) models via RapidOCR; backends onnxruntime, openvino, paddle, torch | Included (torch backend); docling[rapidocr] for onnxruntime | All | ch (Simplified Chinese, also reads English) |
easyocr | EasyOCR, 80+ languages, benefits from a GPU | docling[easyocr] | All | en, es, fr, de |
tesseract | Tesseract command-line program | System Tesseract + language packs | All | eng, spa, fra, deu |
tesserocr | Tesseract via its C API | System Tesseract + docling[tesserocr] | Linux, macOS (hard on Windows) | eng, spa, fra, deu |
ocrmac | Apple Vision framework | docling[ocrmac] | macOS only | en-US, es-ES, fr-FR, de-DE |
nemotron-ocr | NVIDIA Nemotron OCR | docling-slim[feat-ocr-nemotron] | Linux x86_64, Python 3.12, CUDA | — |
kserve_v2_ocr | Remote OCR model behind a KServe v2 / Triton endpoint | docling[remote-serving] for gRPC | Any (remote) | passed through |
Engine list and default languages read from docling options ocr <engine> in 2.148.0. Licences of the engine packages: RapidOCR, EasyOCR and Nemotron OCR are Apache-2.0; tesserocr and ocrmac are MIT; Tesseract itself is Apache-2.0.
How auto chooses
- macOS: ocrmac, if installed.
- Linux with CUDA: Nemotron OCR, if installed.
- RapidOCR with onnxruntime, if onnxruntime is installed.
- EasyOCR, if installed.
- RapidOCR with PyTorch — what a plain
pip install doclingends up using.
Engines that cannot serve the requested language are skipped. Tesseract is never chosen automatically — select it with --ocr-engine tesseract. Order read from auto_ocr_model.py in 2.148.0; our test runs confirmed the last step on Linux.
Languages#
--ocr-lang accepts the engine's own codes, or BCP-47 tags behind iso:, which Docling translates for the chosen engine. An empty value (--ocr-lang '') lets the engine decide.
| Language | tesseract / tesserocr | easyocr | rapidocr | ocrmac | Any engine |
|---|---|---|---|---|---|
| English | eng | en | en · latin | en-US | iso:en |
| German | deu | de | latin | de-DE | iso:de |
| Simplified Chinese | chi_sim | ch_sim | ch | zh-Hans | iso:zh-Hans |
| Traditional Chinese | chi_tra | ch_tra | chinese_cht | zh-Hant | iso:zh-Hant |
| Japanese | jpn | ja | japan | ja-JP | iso:ja |
| Russian | rus | ru | cyrillic* | ru-RU | iso:ru |
- RapidOCR runs one language per run; extra languages are ignored with a warning. Its PyTorch backend only has script-level models for many languages — on the default install we got
OcrLanguageNotSupportedErrorforiso:ruand had to usecyrillic. *eslavwas rejected the same way;en,latin,ch,chinese_cht,japanandcyrillicall ran on the PyTorch backend in our tests. - Tesseract combines languages in the order given (
deu,eng) and needs the matching traineddata installed; a missing one raises an error when the engine starts. - EasyOCR runs several languages at once if they share a script family.
OCR modes#
--ocr-mode | What gets OCR'd | Use for |
|---|---|---|
default | Currently the same as pdf_aware_layout_regions | Most documents |
pdf_aware_layout_regions | Layout regions, except those consisting only of PDF text cells | Digital PDFs with images, scans |
layout_regions | All layout regions, ignoring the PDF text layer | PDFs whose text layer is partly wrong |
full_page | The whole page; replaces existing text | Scans with a broken or garbage text layer (replaces the deprecated --force-ocr) |
Definitions from the OcrMode enum in 2.148.0.
Replace a broken text layer (new in 2.144.0)#
Some PDFs look perfect on screen, but their text copies out as nonsense — 8VceZWZTReV instead of Certificate — because a font maps its glyphs to the wrong characters. OCR modes do not help here: the default mode trusts the PDF text, and full_page throws the text layer away on every page. --replace-broken-text-layer sits in between: Docling checks each page's text layer and reads only the broken pages with full-page OCR.
docling convert contract.pdf --replace-broken-text-layer
docling convert contract.pdf --replace-broken-text-layer --ocr-engine tesseract --ocr-lang eng- Off by default; works with every engine. In Python it is
replace_broken_text_layer=Trueon any OCR options class, e.g.RapidOcrOptions(replace_broken_text_layer=True); on the CLI it is also available as--ocr-option replace_broken_text_layer=true. - What counts as broken: control characters,
�or private-use characters on the page or in many of its lines (any script), plus — for Latin text — accented letters standing in for another script (Îò÷¸òfor Russian) and words with too few vowels. One bad cell on an otherwise good page does not trigger a replacement. - What it cannot detect: text in the right alphabet but with wrong or shuffled letters (
Cartifecita). Use--ocr-mode full_pagefor those files. - A replaced page keeps its low
parse_scorein the confidence report, because that score describes the PDF text layer. Pick an engine and--ocr-langthat can read the document's language.
Engine settings worth knowing#
List every key for an engine with docling options ocr ENGINE, then pass it with --ocr-option KEY=VALUE (repeatable).
| Key | Engines | Default | Effect |
|---|---|---|---|
scale | all | 3.0 | Render multiplier: 72 DPI × scale = 216 DPI. Lower it for already high-resolution scans. |
backend | rapidocr | onnxruntime | onnxruntime, openvino, paddle or torch |
replace_broken_text_layer | all | false | OCR only the pages whose PDF text layer is broken (above); same as --replace-broken-text-layer |
detect_orientation | rapidocr | false | Re-read a region at 90°, 180° and 270° when fewer than half its lines are read with confidence — fixes pages scanned sideways or upside down (new in 2.145.0) |
model_size | rapidocr | small | tiny, small, medium (PP-OCRv6 languages only; tiny not for Japanese) |
text_score / confidence_threshold | rapidocr / easyocr | 0.5 | Drop low-confidence text |
psm | tesseract, tesserocr | Tesseract's own | Page segmentation mode 0–13 (replaces --psm) |
tesseract_cmd | tesseract | tesseract | Full path to the binary (Windows!) |
recognition | ocrmac | accurate | fast trades accuracy for speed |
use_gpu | easyocr | auto | Force CPU with false |
docling convert scan.pdf --ocr-engine rapidocr --ocr-option backend=torch --ocr-option scale=2
docling convert scan.pdf --ocr-engine tesseract --ocr-lang deu,eng --ocr-option psm=6
docling convert scan.pdf --ocr-engine easyocr --ocr-lang iso:fr --ocr-option confidence_threshold=0.3
docling convert sideways.pdf --ocr-engine rapidocr --ocr-option backend=torch --ocr-option detect_orientation=truePython#
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import OcrMode, PdfPipelineOptions, RapidOcrOptions
from docling.document_converter import DocumentConverter, PdfFormatOption
opts = PdfPipelineOptions()
opts.do_ocr = True
opts.ocr_options = RapidOcrOptions(lang=["iso:ja"], backend="torch", mode=OcrMode.FULL_PAGE)
converter = DocumentConverter(format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)})
print(converter.convert("scan.pdf").document.export_to_markdown()[:500])Option classes: OcrAutoOptions, RapidOcrOptions, EasyOcrOptions, TesseractCliOcrOptions (engine tesseract), TesseractOcrOptions (tesserocr), OcrMacOptions, NemotronOcrOptions, KserveV2OcrOptions.
Engine head-to-head on a scan#
Measured by docling.org with Docling 2.148.0 on a 4-vCPU cloud machine with no GPU.
Show data table
| Words recovered | |
|---|---|
| Tesseract 5 (CLI) | 96.1% |
| RapidOCR · onnxruntime | 93.3% |
| RapidOCR · PyTorch (default) | 93.3% |
| EasyOCR (default threshold 0.5) | 85.1% |
| EasyOCR (threshold 0.1) | 94.4% |
Measured by docling.org with Docling 2.148.0 on a 4-vCPU cloud machine with no GPU.
Show data table
| s/page | |
|---|---|
| Tesseract 5 (CLI) | 5.5 s |
| RapidOCR · onnxruntime | 7.0 s |
| RapidOCR · PyTorch (default) | 15.4 s |
| EasyOCR (default threshold 0.5) | 28.9 s |
| EasyOCR (threshold 0.1) | 29.4 s |
Same scanned document (the first four pages of the Docling paper rasterised at 150 DPI with slight blur and skew), same standard pipeline, only the OCR engine changed. Each engine got one warm-up and three timed conversions on a 4-vCPU machine without a GPU, with Docling 2.148.0 on 6 Nov 2026.
- Tesseract was both fastest and most accurate here (5.5 s/page, 96.1% of words) — on clean, English, typeset pages it is hard to beat. It needs the system binary and language packs.
- RapidOCR on onnxruntime is about twice as fast as the default PyTorch backend (7.0 vs 15.4 s/page) with identical output. Installing
docling[rapidocr]is the cheapest speed-up for a pure-Python setup — andautopicks it automatically once onnxruntime is present. - EasyOCR was the slowest on CPU (29 s/page). With Docling's default confidence threshold (0.5) it kept 85.1% of the words; lowering it to 0.1 (
--ocr-option confidence_threshold=0.1) raised that to 94.4%. Its results also depend strongly on the image: on the scan copy we used for the 27 October run, the same default threshold kept only 12.3%.
Frequently asked questions
Which OCR engine does Docling use by default?
The auto engine. In Docling 2.148.0 it tries ocrmac on macOS (if installed), Nemotron OCR on Linux with CUDA (if installed), then RapidOCR with onnxruntime, then EasyOCR, then RapidOCR with PyTorch. It never picks Tesseract. With a plain pip install docling that means RapidOCR on PyTorch.
How do I fix a PDF whose text copies out as garbage?
Some PDFs look right on screen but their fonts map glyphs to the wrong characters, so the text layer reads like 8VceZWZTReV. Since 2.144.0, docling convert --replace-broken-text-layer (or replace_broken_text_layer=True on any OCR options class) detects such pages and replaces only their text with OCR. To OCR every page regardless, use --ocr-mode full_page.
How do I turn off OCR in Docling?
Use --no-ocr on the command line, or set pipeline_options.do_ocr = False in Python. Do this for PDFs that already contain selectable text.
How do I OCR a scanned PDF in another language with Docling?
Pick an engine and pass its language codes with --ocr-lang, for example --ocr-engine tesseract --ocr-lang deu,eng, or use a BCP-47 tag with the iso: prefix such as --ocr-lang iso:de, which Docling maps to the engine's own code.
What is the difference between tesseract and tesserocr in Docling?
tesseract calls the Tesseract command-line program; tesserocr uses Tesseract's C API through the tesserocr Python package, which must be compiled against your system Tesseract. Both read the same traineddata language files.
Why is Docling's OCR so slow?
OCR renders regions at 216 DPI by default (scale 3.0) and runs neural text detection and recognition. On our 4-vCPU machine a scanned page took about 15 seconds. Use a GPU, a faster engine, a lower --ocr-option scale, or skip OCR on born-digital files.
Sources
- Docling source: OCR engines (docling/models/stages/ocr)
- Captured: docling options ocr <engine> output, Docling 2.148.0
- RapidOCR on PyPI
- EasyOCR on PyPI
- Tesseract OCR