Open Source · Research

The islands that vanish inside Hebrew.

A line of Hebrew with an English word in the middle is completely ordinary — an institution, a date, a catalogue number. Most OCR models hand that line back with the middle silently missing. We traced it to the decoder, fixed it, and released the whole stack under Apache 2.0.

In short
What it is
paddleocr-hebrew: open-source OCR for printed Hebrew in images and PDFs, including lines that mix Hebrew with English words and numbers. It returns text in reading order and runs on your own machine, on a CPU or an NVIDIA GPU (ONNX Runtime, including Jetson); nothing is uploaded.
Accuracy
Character error rate against Tesseract on our test sets: receipts 2.09% vs 26.32%, invoices 2.68% vs 7.99%, full pages 7.56% vs 14.20%. Our own measurements; details below.
Install
pip install paddleocr-hebrew huggingface_hub (PyPI), then the few lines under Get it. For an NVIDIA GPU, install onnxruntime-gpu in place of onnxruntime; the GPU is then used automatically.
Licence
Apache-2.0 for the code and the model weights, commercial use included.
Not for
Handwriting or historical manuscripts.
01 — The failure

A compression bias at the boundary

Hebrew documents are rarely purely Hebrew. An institution name, a gene symbol, a catalogue number, a citation, a date, a URL — real pages are full of short left-to-right runs sitting inside right-to-left text. We call them islands.

To understand why they disappear, it helps to know how a CTC decoder reads. It scans the image in vertical slices, left to right, and emits one character — or a blank — per slice. Built into that is a hard assumption: the order of the slices is the order of the characters.

Hebrew runs right to left, so its characters arrive in the reverse of the scanning order. A model trained on Hebrew learns to handle that consistently. But an embedded English word runs left to right inside that reversed line, so at each of its two edges the direction of the text and the direction of the scan disagree. There is no single alignment that satisfies both the island and the Hebrew around it.

CTC has an escape hatch for exactly this kind of conflict: the blank symbol. Emitting blanks across the whole island is cheaper than committing to an alignment that contradicts the rest of the line. So the island isn't misread — it is emitted as nothing at all.

What makes this diagnosable rather than mysterious is where it doesn't happen. Isolated Latin reads at 1.20% CER. Isolated digits at 1.41%. Pure Hebrew at 0.64%. The model can plainly see every glyph involved. The failure is specific to the mixed boundary, and it isn't an ordering bug either — the pure-reorder rate is near zero. It is a compression bias, and it is silent: you get back clean, fluent, confident Hebrew with a hole in it.

1.20%
Latin alone
1.41%
Digits alone
0.64%
Hebrew alone
12.63%
Hebrew + embedded Latin

Per-slice micro-CER, SVTRv2 CTC head. Every glyph class is solved in isolation. Only the seam fails.

02 — The fix

Swap the head, not the model

An attention decoder has no monotonic constraint. It attends over the whole encoded line and emits the next character from context, so an embedded island is just more context. We kept the SVTRv2 backbone exactly as it was and changed only the decoding head.

Same backbone, different head — micro-CER
SliceCTCAttentionGain
Real Hebrew + Latin (n=233)12.63%2.33%5.4×
Hebrew + digits4.99%0.56%8.9×

Attention is slower, so running it on every crop would be wasteful — roughly 90% of real crops are single-script and CTC handles them perfectly. So production runs a script-gated cascade: CTC first on everything, then fall back to attention only when the CTC output contains a Latin or digit character, or comes back low-confidence.

That buys the attention ceiling on mixed text at about 1.25–1.36× CTC latency, with no retraining and no second model to maintain.

Why this generalises

Nothing in the mechanism is specific to Hebrew. Any bidirectional script that embeds LTR runs — Arabic, Farsi, Urdu, Hebrew — should exhibit the same CTC boundary collapse, and should respond to the same fix. If you are benchmarking a bidirectional OCR model, measure the mixed-script slice separately. A single aggregate CER will hide this completely.

03 — The long-line wall

A 52% plateau that turned out to be the wrong question

Long Hebrew lines — 61 to 80 characters — sat at 52–55% CER across every line-level model we trained. No amount of additional data moved it, and three independent levers (learning rate, input width, data up-weighting) all failed the same way.

Long-sequence degradation is not a Hebrew problem, though. It shows up in the PaddleOCR issue tracker independently of script: issue #11482 reports that recognition accuracy on long sequences "never matches the accuracy of single words" across CRNN, SAR, ABINET and SVTR, and issue #13938 documents a Chinese model stuck at ~50% accuracy on ~135-character lines. Different scripts, different metrics, same shape of failure — which was the clue that ours wasn't a Hebrew data problem either.

We first beat it with sliding-window inference: process the line in overlapping ~320px windows and stitch the seams by Levenshtein alignment. That was a genuine 45-point drop — 52% down to 6.93%. It was also a trap. It only worked on one exact model at one exact scale, cost 3–5× the recognition passes, and every seam was a fresh opportunity for duplication garbage. The next backbone couldn't slide at all.

The actual answer was to stop patching a short-crop model and use a reader built for the sequence length. SVTRv2 reads the whole line in a single pass, no windows, no seams — and drops 71–80 character Hebrew to 0.49% CER. The wall wasn't a data problem or an inference problem. It was an architecture mismatch.

52%
Community plateau, long lines
6.93%
Sliding window (a dead end)
0.49%
SVTRv2, single pass
04 — Benchmarks

Robustness is the edge, not pristine text

The obvious target — beat Tesseract on clean Hebrew — is the wrong one. On clean academic Hebrew, Tesseract is genuinely good, and our earlier word-level stack narrowly lost to it. Chasing that margin was low-value work.

What matters is what happens on the documents people actually scan. Tesseract collapses on those; we don't.

About these numbers

Every CER figure on this page comes from our own in-house evaluation harness, run on our own test sets. They are measured rather than estimated, and BiDi-normalised on both sides before the Levenshtein distance — but they are vendor-reported and have not been independently verified. The methodology, the caveats and the per-slice breakdowns are all in the repository, so you can disagree with them specifically.

Real-domain crop CER — where documents actually come from
DomainTesseractOurs
Thermal receipts26.32%2.09%
Menus12.41%2.80%
Invoices7.99%2.68%
Page-level, 71 pages, recogniser held constant
Page pipelinemicro-CER
Word detection + SVTRv2 cascade7.56%
Hebrew-finetuned line detection10.36%
Stock line detection10.67%
Tesseract 514.20%

Word-level detection beats line-level on Hebrew pages, and we tested that fairly: our first comparison was confounded because the word detector was Hebrew-finetuned while the line detector was stock. So we trained a Hebrew line detector on the same architecture and the same warm-start base, converged it to 0.913 val hmean, and re-ran. The finetune closed only 0.31pt. The gap is the word-vs-line difference itself, not detector tuning.

And we did close the academic gap too. On clean rendered public-domain Hebrew with perfect ground truth (n=600), the attention reader now beats Tesseract outright: 0.35% versus 1.34% micro-CER. But robustness is what makes the thing worth shipping.

05 — Get it

Seven models, finetuned from PaddleOCR

Every model here is a finetune of a PaddleOCR model (PaddleOCR v3.7.0, Apache-2.0), released in the same licence. All of them ship as ONNX and share one 120-character charset.

What's in the release
ModelRoleFinetuned from
server-svtrv2Flagship recogniser (CTC + attention)SVTRv2, Chinese pretrain
server-v6Word-level server recogniserPP-OCRv6, PPLCNetV4-medium
server-v5Word-level server recogniserPP-OCRv5, PPHGNetV2-B4
light-svtrv2smallEdge / CPU recogniserSVTRv2-small, distilled from the flagship
mobile-wordMobile recogniser (7.4 MB)PP-OCRv5 mobile, PPLCNetV3
word-detFlagship word detectorPP-OCR mobile DBNet
line-detLine detector (hard layouts)PP-OCR mobile DBNet

The flagship pairs word-det with server-svtrv2. Start there; the rest are alternatives for smaller footprints or harder layouts.

pip install paddleocr-hebrew huggingface_hub

from huggingface_hub import snapshot_download
from paddleocr_hebrew import HebrewOCR

# download the flagship models once (~190 MB)
files = ["config.json", "charset_v2f.txt",
         "word-det/*", "server-svtrv2/*"]
models = snapshot_download("Rivok/paddleocr-hebrew",
                           allow_patterns=files)

ocr = HebrewOCR.word(models)
result = ocr.read("page.png")  # PDFs: add page=n
for line in result["lines"]:
    print(line["text"])        # logical order
Already using Tesseract?

heb_rivok is our Hebrew model for Tesseract: one file that works anywhere Tesseract does, including OCRmyPDF and Paperless-ngx, and reads Hebrew, English and digits. On real document lines it cuts character errors from 4.7% to 1.4% compared with the stock Hebrew model Ubuntu ships.

Converting documents with Docling?

docling-hebrew-ocr plugs these models into Docling as an OCR engine, for scanned Hebrew documents and for PDFs whose Hebrew text layer is reversed or garbled: pip install docling-hebrew-ocr, then docling --allow-external-plugins --ocr-engine hebrew --ocr-mode full_page scan.pdf.

Apache-2.0, code and weights, finetuned from PaddleOCR models under the same licence. The training weights and configs are not published — the ONNX files are all you need to run the models. If you want to finetune on your own Hebrew data, adapt the charset, or collaborate, open an issue or write to [email protected].

Known limitation

Lines are put in reading order by a simple rule, not the full Unicode bidirectional algorithm (UAX #9): a line with a Hebrew word reads right to left, and each run of English words inside it keeps its own left-to-right order. Since version 0.1.0 this handles Hebrew lines that contain English names and phrases, which earlier versions could put in the wrong order. Unusual mixes can still come out differently from a strict BiDi engine, so if your documents switch language often within a line, test a few pages before you rely on it.

06 — Questions

Common questions

How do I OCR a Hebrew PDF in Python?

Install paddleocr-hebrew (above) and pass the PDF to ocr.read() one page at a time. Each call renders the page, finds the words, reads them and returns the text line by line in reading order, all on your own machine:

import pypdfium2   # installed with paddleocr-hebrew

pdf = "document.pdf"
pages = len(pypdfium2.PdfDocument(pdf))
for n in range(1, pages + 1):
    for line in ocr.read(pdf, page=n)["lines"]:
        print(line["text"])

If the PDF was born digital, try its text layer first. OCR is the fallback for scanned pages, and for Hebrew PDFs whose stored text comes out reversed or garbled, which happens often.

If you'd rather keep Tesseract or OCRmyPDF, see our heb_rivok model, which plugs into both.

Why does Hebrew text come out reversed?

Usually because the text is in visual order instead of logical order. Some PDF generators store Hebrew the way it is drawn, left to right, and some OCR tools sort words by their position on the page. Copy, search and AI tools then see reversed words, or English and numbers in the wrong place.

The other cause is display: correct text shown in a left-to-right context, such as a terminal, looks reversed even though it isn't. paddleocr-hebrew returns logical order, the order you read in. Use a bidi library such as python-bidi only when you need to display it left to right.

Which Hebrew OCR keeps English words and numbers?

OCR models with a CTC decoder, the most common design, can silently drop short English words or numbers inside a Hebrew line (section 01). paddleocr-hebrew re-reads lines that show Latin letters, digits or low confidence with an attention decoder, which keeps them: on real mixed Hebrew and English lines the character error rate falls from 12.63% to 2.33%.

Cloud OCR services and large vision models also read mixed lines, but they need your documents uploaded. paddleocr-hebrew runs locally.