in Enterprise Doc Intelligence, the collection that builds an enterprise RAG system from 4 bricks. Article 5 (doc parsing) constructed the parser with PyMuPDF (fitz), which returns empty on a scanned web page with no textual content layer. This companion swaps the engine for EasyOCR, a free OCR package deal that recovers that textual content. It’s the one case on this household the place the brand new engine offers you much less, no more: it recovers the textual content and nothing round it, and that hole is the lesson.

Scanned PDFs will not be solved by “simply throw OCR at it”. The OCR step recovers textual content; that’s needed however not adequate for an enterprise RAG pipeline. What the pipeline additionally wants is every thing across the textual content: the place the web page boundaries are, which traces are part headings, what’s a determine, what’s a desk row vs a free paragraph. “Conventional OCR” (the time period of artwork for text-detection + text-recognition engines like EasyOCR, Tesseract, PaddleOCR) offers you the textual content. It offers you nothing else. The remaining is the structure drawback, and the structure drawback is the tougher half.
This text runs that distinction concretely. The standard-OCR engine is EasyOCR: the best, quickest, free, JaidedAI’s text-detection + recognition library (Apache 2.0, declared within the mission’s LICENSE file). The layout-aware engine is Docling (Article 5ter; MIT license, declared within the mission’s LICENSE file). Each can OCR a scanned web page. They differ on what they do with the consequence. The entire article is a setup for the head-to-head on an actual public-domain 1974 scan in part 5.

1. What “conventional OCR” does (and doesn’t)
Conventional OCR reads pixels and returns textual content rectangles. Every little thing else, sections, tables, figures, studying order, is a separate structure drawback the engine refuses to take a look at. The 2 fashions behind it are textual content detection (discover rectangular areas of the picture that include textual content) and textual content recognition (learn every area’s pixels and return characters with a confidence rating). The output is a flat listing of (bbox, textual content, confidence) per detected area.
That’s every thing EasyOCR (or Tesseract, or PaddleOCR) does. The engine reads pixels and returns textual content rectangles. A two-column web page comes again as a flat listing of left-and-right textual content containers intermixed by y-coordinate; the engine doesn’t know there are two columns. A desk comes again as a grid of disconnected cells the engine can’t inform other than common paragraphs. A determine caption is simply one other textual content field. The web page header, web page footer, marginalia all present up as containers too.
Something that wants “this textual content is a bit heading” or “these 4 containers are one desk row” wants a second mannequin on high, a structure mannequin. The structure mannequin reads the OCR output plus the web page picture and classifies every area (heading, paragraph, desk cell, determine, caption, footer…) and teams them right into a studying order. That’s what Article 5bis (Azure DI), Article 5ter (Docling), and Article 5quater (imaginative and prescient LLM) all add over the OCR step. With out one, you’ve got “OCR output”, not “a parsed doc”.
2. EasyOCR: the canonical conventional OCR
EasyOCR is the cleanest demonstration of “conventional OCR” as a category. The library is small (~150 MB of mannequin weights cached on first name), free, CPU-only by default, native. The entire library API is 2 calls: construct a Reader for the languages you want, then hand readtext a picture. Every detection comes again as a triple: the polygon across the textual content, the recognised string, and the recogniser’s personal confidence.
import easyocr
import fitz
import numpy as np
reader = easyocr.Reader(["en"], gpu=False) # first name downloads ~150 MB
# render web page 1 of a scanned PDF to a numpy array EasyOCR can learn
web page = fitz.open("information/contracts/scanned_amendment.pdf")[0]
pix = web page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) # 2x zoom = ~144 DPI
img = np.frombuffer(pix.samples, dtype=np.uint8).reshape(
pix.top, pix.width, pix.n,
)
# the recogniser: one picture in, one triple per detected textual content area out
detections = reader.readtext(img)
for quad, textual content, conf in detections:
# quad = [[x0,y0], [x1,y0], [x1,y1], [x0,y1]] in pixel coords
print(spherical(conf, 2), textual content)
parse_pdf_easyocr wraps that loop. It walks each web page of the PDF, renders every to a numpy array, calls readtext, converts the pixel-space polygons again to PDF coordinates, and packs the detections into the identical dict-of-tables contract as the opposite parsers, similar line_df, similar parsing_summary, similar downstream shoppers, besides that solely these two keys carry information. Each different slot (page_df, image_df, toc_df, span_df, object_registry, cross_ref_df) comes again as an empty DataFrame. That isn’t a missing-feature bug; it’s precisely what “conventional OCR” means.
parsed = parse_pdf_easyocr(
"information/contracts/scanned_amendment.pdf",
languages=("en",), # add "fr", "de", ... for multilingual scans
render_scale=2.0, # 2.0 = ~144 DPI ; increase for small fonts
gpu=False, # CPU-only by default ; set True if CUDA accessible
confidence_threshold=0.0, # filter low-confidence detections if wanted
)
parsed["line_df"] # textual content + bbox + confidence per detection
parsed["parsing_summary"] # methodology, web page rely, line rely, render scale
# Each different key (page_df, image_df, toc_df, span_df, object_registry,
# cross_ref_df) is an empty DataFrame ; EasyOCR has nothing to place there.
The signature kwargs are the one knobs:
languages: tuple of ISO-639-1 codes (en,fr,de,zh, …). A multilingual corpus masses one Reader per language set; the@lru_cacheinget_easyocr_readerretains a handful of those in reminiscence throughout calls.render_scale: what number of pixels per PDF unit when rasterising every web page.1.0is native (~72 DPI, usually too small).2.0is the candy spot for physique textual content. Increase to3.0for tiny fonts; decrease when you’re memory-bound.gpu: CPU is the default so the module works on any machine. CUDA offers a 3-5x speedup on text-heavy pages.confidence_threshold: drop low-confidence detections.0.0retains every thing (the column is preserved so downstream code can filter),0.3cuts most noise on degraded scans.
3. What line_df seems like
Pattern rows from the NIST FIPS 199 cowl (US Authorities work, public area within the US, see NIST copyright assertion), one per detected textual content area: the web page coordinate, the OCR’d textual content, and the recogniser’s personal confidence rating. That’s the entire output.

confidence column EasyOCR provides totally free – Picture by writerThe form is intentionally small:
textual content+ bbox: the recogniser’s payload, one row per detected textual content area.confidence: float between 0 and 1, EasyOCR’s self-score. Helpful each as a filter (drop under 0.3 on noisy scans) and as a suggestions sign (Article 8’s era can flag low-confidence passages to the consumer).character_count: stored for symmetry with the opposite parsers; on EasyOCR it’s simplylen(textual content).- No column / reading-order column. A two-column web page comes again as a flat listing, left-and-right containers intermixed by y-coordinate.
Each different key within the returned dict (page_df, image_df, toc_df, span_df, object_registry, cross_ref_df) is an empty DataFrame. A client that calls parsed["image_df"] doesn’t crash; it iterates an empty body.
4. What conventional OCR misses, the structure hole, merchandise by merchandise
5 structural artefacts that the RAG pipeline wants and that conventional OCR can’t produce, no matter how large the popularity mannequin is. Every one breaks a downstream operation the remainder of the collection depends on.
- TOC / sections. Cross-reference decision (Article 11) and section-scoped corpus retrieval (Article 17) each depend on
toc_df. EasyOCR returns zero rows. The dispatcher can’t route “reply in Part 3.2” questions as a result of Part 3.2 has no boundary. - Particular person figures contained in the web page. A scanned 30-page contract might include six chart screenshots embedded within the physique textual content. EasyOCR treats the entire web page as one picture and returns textual content from across the figures; the figures themselves by no means turn into rows. A downstream pipeline that should retrieve “the chart on web page 14” has no deal with.
- Studying order on multi-column / multi-zone pages. A two-column scientific paper web page comes again top-to-bottom throughout each columns intermixed: left-line-1, right-line-1, left-line-2, right-line-2… Technology reads rubbish. Sidebars, footnotes, marginalia all leak into the principle circulation.
- Desk cells. A scanned schedule of prices or premium desk comes again as a flat listing of disconnected textual content containers (the row labels in a single column, the values in one other, the unit headers some other place). The connection “this worth belongs to this label” is misplaced. Article 5 (doc parsing) opens on precisely this failure mode (“the parser walked the desk cell by cell and joined them right into a flat string”). Format-aware engines run a separate TableFormer-style mannequin to reconstruct rows × columns × headers.
- Font / weight / dimension indicators. OCR recovers character form, not its typographic encoding. “This line is in daring 18pt” is info the structure engine reads from the web page rendering; EasyOCR throws it away. Headings, emphasis, footnotes lose the cue that might have categorised them.
Take the third one, studying order, as a result of it’s the one which quietly corrupts a solution. EasyOCR returns textual content containers sorted by their y-coordinate. On a two-column web page the 2 columns sit on the similar heights, so the containers come again interleaved: first line of the left column, first line of the precise column, second line of the left, and so forth. The prose reads as a zigzag, and era quotes the zigzag.

The one sentence: the OCR step recovers textual content, the structure step recovers what makes the textual content usable. Article 5ter (Docling) and Article 5bis (Azure DI) add the structure step on high of the identical OCR. Article 5quater (imaginative and prescient LLM) folds the 2 into one name. EasyOCR stops on the OCR step.
5. EasyOCR vs Docling on an actual scanned PDF
On the identical 1974 scan, Docling extracts extra characters (5,423 vs 4,952), the web page boundaries, eleven TOC entries, and 4 determine areas. EasyOCR extracts textual content rectangles and stops. The 2 engines agree on the character stage, each OCR with the identical recogniser-class accuracy, however Docling’s structure go turns the OCR output right into a doc.
The attention-grabbing comparability will not be towards fitz (fitz returns zero on a scan) however towards the following engine up: Docling, the native layout-aware parser from Article 5ter. The comparability is cleaner than it seems: Docling’s default OCR backend is EasyOCR itself. Similar recognizer studying the identical pixels; the distinction is every thing Docling builds round it.
The check case is an actual public-domain scan: pages 1–5 of karg74.pdf, the 1974 USAF MULTICS Safety Analysis (Karger & Schell, ESD-TR-74-193 Vol. II). NIST hosts it of their Early Pc Safety Papers archive; the work is within the public area because the output of US Air Pressure officers. The PDF has Adobe’s “Paper Seize” OCR layer baked in, however we ignore it, each engines re-OCR from web page photos, which is the real looking state of affairs when the embedded OCR (when current) is unreliable.

The 2 columns inform completely different tales.
EasyOCR (left). Sooner (59.7 s vs 134.4 s, no structure mannequin to load and run), ships the recogniser’s confidence as a column (imply 0.81 on this scan), produces extra row-level detections (346 containers) as a result of each textual content area within the web page turns into one row. Zero construction: no page_df, no toc_df, no image_df. The output is textual content in bbox kind, nothing else.
Docling (proper). Slower (2.3× extra compute), joins detections into 105 traces/paragraphs moderately than 346 containers, no confidence column. The structural achieve is actual: 5 page_df rows, 11 toc_df entries (Docling’s structure mannequin classifies headings as sections), 4 image_df rows (figures detected contained in the web page as separate objects). On a PDF with tables, the hole widens additional, Docling’s TableFormer recognises rows × columns × headers, which EasyOCR can’t do in any respect. Article 5ter develops the desk case in full.
Each engines OCR with related character-level error charges on this 1974 scan (Karger → “Karger” by EasyOCR, “Karger” by Docling on the cleanest web page; degraded areas yield related noise on each, “Laboralory”, “und” as a substitute of “and”). The OCR engine inside Docling (EasyOCR or OnnxTR relying on set up) will not be magically extra correct than calling EasyOCR straight. What Docling provides is the way it organises the OCR output, not the way it OCRs.
For enterprise RAG, the precise name is Docling virtually at all times. The two.3× compute is paid as soon as at ingestion (the parse cache from Article 5 (doc parsing) reuses outcomes eternally); the structural achieve (TOC, figures, desk cells, studying order) is paid again on each downstream question. The one factor Docling doesn’t ship is EasyOCR’s row-level confidence sign, which is never value giving up sections + figures + tables.
6. When conventional OCR nonetheless earns its hold
EasyOCR is the emergency package deal of the household: much less visibility into the doc, less complicated dependencies, sooner to deploy when the constraint is operational moderately than pedagogical. 4 slim circumstances hold the door open.
- Receipt-class paperwork. A 1-page bill or receipt with no headings, no sections, no figures, no tables-with-merged-cells. Format is trivial; the recogniser is the entire job. Including Docling’s 2× compute buys construction the doc doesn’t have.
- Per-region confidence as a era suggestions sign. Technology (Article 8) can learn the row’s
confidencefrom the cited passage and warn the consumer when the reply rests on a 0.3-confidence little bit of OCR. Docling doesn’t ship that column. For pipelines the place this sign is load-bearing, EasyOCR (or working EasyOCR alongside Docling) is the reply. - Non-Latin scripts at scale. EasyOCR ships pretrained fashions for 80+ languages together with Chinese language, Japanese, Korean, Arabic, Hindi, Cyrillic. Docling’s OCR stack is extra restricted in non-Latin protection on the time of writing.
- Operational constraints that block Docling. Corp SSL inspection breaks the HuggingFace mannequin obtain. Home windows blocks symlinks with out Developer Mode. The manufacturing picture has a strict dependency price range. Air-gapped deployment with no option to ship 3 GB of structure weights. In each considered one of these, EasyOCR’s 150 MB cached mannequin + CPU inference goes by the place Docling doesn’t. You see much less of the doc, however you see one thing.
Exterior these circumstances: default to Docling on scans, Azure DI on regulated-cloud-OK retailers, imaginative and prescient LLM when the doc has handwriting / signatures / a non-textual semantic layer. The adaptive-parsing dispatcher (Article 10) routes routinely.
7. Conclusion
OCR recovers characters. Format recovers what makes the characters helpful, sections, figures, desk cells, studying order. The default engine for scans is the one which does each. The total Article-5 household traces up by the identical axis:

EasyOCR is the OCR flooring, what you get if you cease at recognising characters and by no means ask “and the place are they on the web page?” The query issues. “The place on the web page” makes the distinction between an inventory of textual content containers and a parsed doc. The dispatcher of Article 10 (adaptive parsing) picks the precise engine per web page; this text exists so the dispatcher is aware of what it offers up when it picks a budget one.
8. Sources and additional studying
EasyOCR is probably the most reachable conventional OCR engine in 2026; PaddleOCR (Baidu) and Tesseract (Google, decades-old) sit beside it in the identical household. The structure step on high is what separates “OCR” from “doc parsing”; Docling (Article 5ter) and Azure DI (Article 5bis) each add it, on native {hardware} and within the cloud respectively. The correct cross-reading is the structure literature (Smock et al. 2022 for desk construction, Auer et al. 2024 for the complete structure cascade) and the choice OCR engines for non-Latin scripts.
Similar course because the article:
- JaidedAI, EasyOCR. The library this text paperwork, together with the 80+ language mannequin packs.
- PaddleOCR (Baidu). Similar-class conventional OCR engine; higher Chinese language protection, related structure blindness.
- Tesseract OCR. The decades-old reference, nonetheless extensively deployed; similar architectural form as EasyOCR (detection + recognition, no structure).
Completely different angle, completely different context:
- Auer et al., Docling Technical Report, IBM Analysis 2024 (arXiv:2408.09869). The layout-aware cascade that turns OCR output right into a parsed doc; the comparability level in part 5 of this text.
- Smock, Pesala, Abraham, PubTables-1M / Desk Transformer (TATR), CVPR 2022 (arXiv:2110.00061). The analysis lineage behind cell-level desk extraction, the only largest functionality EasyOCR lacks.
Earlier within the collection:
- Doc Intelligence: collection intro. What the collection builds, brick by brick, and in what order.
- Baseline Enterprise RAG, from PDF to highlighted reply. The four-brick pipeline finish to finish: PDF in, highlighted reply out.
- Embeddings Aren’t Magic: The Predictable Failure Modes of RAG Retrieval. The place embedding similarity wins (synonyms, typos, paraphrase), the place it predictably breaks (unknown phrases, negation, term-vs-answer relevance), and use it anyway.
- Rerankers Aren’t Magic Both: When the Cross-Encoder Layer Is Well worth the Price. What a cross-encoder provides over bi-encoder embeddings, measured, and when it’s well worth the latency.
- RAG will not be machine studying, and the ML toolkit solves the fallacious drawback. Why chunk-size sweeps and finetuning optimize the fallacious factor; route by query sort as a substitute.
- From regex to imaginative and prescient fashions: which RAG approach suits which drawback. Two axes, doc complexity and query management, that decide the approach for every case.
- 10 widespread RAG errors we hold seeing in manufacturing. Ten manufacturing errors, organized brick by brick, with the repair for every.
- Past extract_text: the 2 layers of a PDF that drive RAG high quality. The primary half of the parsing brick: the doc’s nature, indicators, and abstract.
- Cease returning flat textual content from a PDF: the relational form RAG wants. The second half of the parsing brick: the relational tables each downstream brick reads.





