Skip to content

v0.5.0 — OCR + render-backend pipeline (100% on insurance corpus) - #4

Merged
rrader26 merged 1 commit into
feat/m2-pdf-supportfrom
feat/v0.5-ocr-backends
May 10, 2026
Merged

v0.5.0 — OCR + render-backend pipeline (100% on insurance corpus)#4
rrader26 merged 1 commit into
feat/m2-pdf-supportfrom
feat/v0.5-ocr-backends

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

Summary

Pages with no extractable text — scanner output, "Microsoft Print To PDF" exports, image-only PDFs — can now be rasterized + OCR'd transparently. Two render backends + two OCR backends ship; the interfaces let callers plug in any provider.

Stacked on top of #3 (M2). Merge #2#3 → this PR in order.

Real-world impact

12-doc insurance corpus (the same one used to drive M2 design):

Mode 🟢 ≥70 🟡 30-69 🔴 <30
Without OCR (v0.4) 6 (50%) 6 (50%) 0
With OCR (Poppler+Tesseract) 12 (100%) 0 0

Failing categories before — all now resolved by OCR:

  • "Microsoft Print To PDF" vector-glyph PDFs (4 docs)
  • Scanner output (2 docs)

Sample output from an Erie Auto Quote PDF (was: 0 text items extracted; now: a full structured snapshot):

HAMMETT INSURANCE AGENCY INC 05/26/2026 ...
TRACEY LYNN RADER ...
[PAGE:p_2]
Total Annual Policy Premium Quoted: $2,107.16
[PAGE:p_3]
### Payment Plan Options
- Annual (Plan A) ...
- Monthly Auto-Pay (Plan G) ...
[PAGE:p_4]
### PA0021 02/26 Page 5 of §
- Diminishing deductible — your deductible is reduced for each consecutive claims-free year ...

Architecture

```
PDF page ──[RenderBackend]──► PNG/JPEG bytes ──[OcrBackend]──► Text + positions
```

  • `OcrBackend` — `extractPage(image, opts)` → text + confidence + optional positioned items
  • `RenderBackend` — `renderPage(pdfData, opts)` → image bytes
  • `convertPdf({ ocr: { render, ocr, mode } })` — opt-in pipeline:
    • `'auto'` (default): OCR only pages with no extractable text — mixed PDFs get cheap partial OCR
    • `'always'`: OCR every page (overrides extracted text)
    • `'never'`: disable OCR
  • `document.ocr_used` flag set to `true` when OCR was applied

Bundled backends

Render:

Backend Install When
`PopplerRenderBackend` `brew install poppler` / `apt install poppler-utils` Lightest — no native modules
`PdfjsRenderBackend` `npm install canvas` Pure-Node, no system deps

OCR:

Backend Install Cost Quality
`TesseractOcrBackend` `npm install tesseract.js@^5` Free Decent on clean text
`MistralOcrBackend` (none — uses fetch) ~$1/1k pages Excellent, layout-aware

Bring-your-own (AWS Textract, Google Document AI, Apple Vision) all fit the same interface.

Tests

  • 8 new OCR pipeline unit tests with mocked backends (deterministic, fast)
  • All 176 unit + 10 real-Chromium tests still passing

Test plan

🤖 Generated with Claude Code

Pages with no extractable text — scanner output, "Microsoft Print To PDF"
exports, image-only PDFs — can now be rasterized + OCR'd transparently.
Two render backends + two OCR backends ship; the interfaces let callers
plug in any provider.

Real-world impact (insurance corpus, 12 docs)
                            🟢 ≥70   🟡 30-69   🔴 <30
Without OCR (v0.4)            6        6         0
With OCR (Poppler+Tesseract)  12       0         0    ← all docs handled

Architecture
- OcrBackend / RenderBackend interfaces. Minimal, plug-and-play.
- convertPdf({ ocr: { render, ocr, mode } }) — opt-in pipeline:
    'auto' (default) — OCR only pages with no extractable text
    'always'         — OCR every page (overrides extracted text)
    'never'          — disable OCR entirely
- Pages with extractable text are not re-OCR'd in 'auto' mode (cost
  optimization), making mixed text+image PDFs cheap.
- document.ocr_used flag set to true when OCR was applied.
- Capabilities map: ocr: true on snapshots produced via OCR.

Bundled render backends
- PopplerRenderBackend  — shells out to pdftoppm. Lightest, no native modules.
- PdfjsRenderBackend    — pure-Node via pdfjs-dist + node-canvas (optional).

Bundled OCR backends
- TesseractOcrBackend   — in-process WASM. Free, offline, ~10s/page.
                          Long-lived worker reused across pages; close()
                          terminates it. Honors language hint.
- MistralOcrBackend     — cloud API. ~$1/1k pages, best quality. Auth
                          via MISTRAL_API_KEY env var or constructor opt.

Bring-your-own — AWS Textract, Google Document AI, Apple Vision Framework,
etc. all fit the same OcrBackend / RenderBackend shape. Reference impls
welcome via PR.

Devex
- examples/ocr-pdf.ts demonstrates Poppler + Tesseract end-to-end.
- examples/diagnose-pdf.ts gains a --ocr flag; quality scores reflect
  whether OCR rescued otherwise-failing docs.

Tests
- 8 new OCR pipeline tests with mocked backends (deterministic, fast).
- Total: 176 unit + 10 real-Chromium = 186 (was 176).

Optional peer deps
- tesseract.js@^5 (Tesseract backend)
- canvas (PdfjsRenderBackend)
- pdfjs-dist@^4 (already opt-peer; required for both render backends and
  text extraction generally)

Not in this release
- AWS Textract / Google Document AI / Apple Vision adapters — interface
  ships, community impls welcome
- Form-structure inference (label/value pairs on non-AcroForm PDFs) —
  paired with M3 / v0.6
- AcroForm support — M3 / v0.6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants