Independent guide — not affiliated with IBM or the Docling project. Every command verified on Docling 2.148.0.
  1. Home
  2. OCR
OCR guide

OCR in Docling — the right engine, the right language, or none

OCR is both Docling's most useful and most expensive stage: it recovered 93% of the words from our scanned test pages, and on a born-digital paper it consumed 79% of the conversion time for a few extra lines of text. Here is how to get the first without paying for the second.

Verified on Docling 2.148.0Updated 6 Nov 2026Includes our own measurements12 min read
In short
  • Born-digital PDF (text is selectable)? Use --no-ocr — 3.6× faster in our test.
  • Scans and images: OCR is required. The default auto engine uses ocrmac on macOS if installed, then RapidOCR (onnxruntime), EasyOCR, then RapidOCR on PyTorch — the last is what a plain install gets.
  • On our scan, Tesseract was fastest and most accurate (5.5 s/page, 96.1% of words); RapidOCR on onnxruntime (docling[rapidocr]) was 2× faster than the default with identical output.
  • Garbage text from a born-digital PDF? --replace-broken-text-layer (new in 2.144.0) swaps in OCR only on the broken pages; --ocr-mode full_page OCRs every page.
  • Language codes are engine-specific (eng vs en vs en-US); iso: tags like iso:de are translated for you.

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 paperTime per pageMarkdown characters
Default (OCR on bitmap regions)6.58 s34,063
--no-ocr1.83 s29,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-engineWhat it isInstallPlatformsDefault languages
auto (default)Picks the first usable engine (see below)—Allengine's own
rapidocrPaddleOCR (PP-OCR) models via RapidOCR; backends onnxruntime, openvino, paddle, torchIncluded (torch backend); docling[rapidocr] for onnxruntimeAllch (Simplified Chinese, also reads English)
easyocrEasyOCR, 80+ languages, benefits from a GPUdocling[easyocr]Allen, es, fr, de
tesseractTesseract command-line programSystem Tesseract + language packsAlleng, spa, fra, deu
tesserocrTesseract via its C APISystem Tesseract + docling[tesserocr]Linux, macOS (hard on Windows)eng, spa, fra, deu
ocrmacApple Vision frameworkdocling[ocrmac]macOS onlyen-US, es-ES, fr-FR, de-DE
nemotron-ocrNVIDIA Nemotron OCRdocling-slim[feat-ocr-nemotron]Linux x86_64, Python 3.12, CUDA—
kserve_v2_ocrRemote OCR model behind a KServe v2 / Triton endpointdocling[remote-serving] for gRPCAny (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

  1. macOS: ocrmac, if installed.
  2. Linux with CUDA: Nemotron OCR, if installed.
  3. RapidOCR with onnxruntime, if onnxruntime is installed.
  4. EasyOCR, if installed.
  5. RapidOCR with PyTorch — what a plain pip install docling ends 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.

Languagetesseract / tesserocreasyocrrapidocrocrmacAny engine
Englishengenen · latinen-USiso:en
Germandeudelatinde-DEiso:de
Simplified Chinesechi_simch_simchzh-Hansiso:zh-Hans
Traditional Chinesechi_trach_trachinese_chtzh-Hantiso:zh-Hant
Japanesejpnjajapanja-JPiso:ja
Russianrusrucyrillic*ru-RUiso: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 OcrLanguageNotSupportedError for iso:ru and had to use cyrillic. * eslav was rejected the same way; en, latin, ch, chinese_cht, japan and cyrillic all 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-modeWhat gets OCR'dUse for
defaultCurrently the same as pdf_aware_layout_regionsMost documents
pdf_aware_layout_regionsLayout regions, except those consisting only of PDF text cellsDigital PDFs with images, scans
layout_regionsAll layout regions, ignoring the PDF text layerPDFs whose text layer is partly wrong
full_pageThe whole page; replaces existing textScans 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.

bash
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=True on 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_page for those files.
  • A replaced page keeps its low parse_score in the confidence report, because that score describes the PDF text layer. Pick an engine and --ocr-lang that 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).

KeyEnginesDefaultEffect
scaleall3.0Render multiplier: 72 DPI × scale = 216 DPI. Lower it for already high-resolution scans.
backendrapidocronnxruntimeonnxruntime, openvino, paddle or torch
replace_broken_text_layerallfalseOCR only the pages whose PDF text layer is broken (above); same as --replace-broken-text-layer
detect_orientationrapidocrfalseRe-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_sizerapidocrsmalltiny, small, medium (PP-OCRv6 languages only; tiny not for Japanese)
text_score / confidence_thresholdrapidocr / easyocr0.5Drop low-confidence text
psmtesseract, tesserocrTesseract's ownPage segmentation mode 0–13 (replaces --psm)
tesseract_cmdtesseracttesseractFull path to the binary (Windows!)
recognitionocrmacaccuratefast trades accuracy for speed
use_gpueasyocrautoForce CPU with false
bashexamples
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=true

Python#

python
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#

Words recovered from the scanned pagesShare of unique words (≥4 letters) of the original text found in the output. Higher is better.
0255075100Tesseract 5 (CLI)96.1%RapidOCR · onnxruntime93.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
Words recovered from the scanned pages
Words recovered
Tesseract 5 (CLI)96.1%
RapidOCR · onnxruntime93.3%
RapidOCR · PyTorch (default)93.3%
EasyOCR (default threshold 0.5)85.1%
EasyOCR (threshold 0.1)94.4%
OCR speed per page on CPUSeconds per page for the whole standard pipeline, median of 3 warm runs. Lower is faster.
0102030Tesseract 5 (CLI)5.5 sRapidOCR · onnxruntime7.0 sRapidOCR · PyTorch (default)15.4 sEasyOCR (default threshold 0.5)28.9 sEasyOCR (threshold 0.1)29.4 s

Measured by docling.org with Docling 2.148.0 on a 4-vCPU cloud machine with no GPU.

Show data table
OCR speed per page on CPU
s/page
Tesseract 5 (CLI)5.5 s
RapidOCR · onnxruntime7.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 — and auto picks 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