OCR Ingest
The ingest stage lives in crates/ocr and exposes one entry point:
pub fn extract_text(bytes: &[u8], on_progress: impl Fn(Progress)) -> Result<String, String>
It returns the document text (pages joined by newlines) or an error, and calls
on_progress as it advances so the UI can show page-level progress.
Step 1 — probe for a native text layer
extract_native_text_pages uses pdf-extract to pull the embedded text layer,
one string per page. detects_native_text then decides whether that layer is
real:
- Only the first 3 pages (
NATIVE_TEXT_PROBE_PAGES) are examined. - A PDF is text-bearing when the average number of non-whitespace characters
across those pages is at least 50 (
NATIVE_TEXT_MIN_CHARS_PER_PAGE). - An empty page list counts as scanned.
Probing a small prefix is fast and reliable: scanned documents almost never contain extractable text, and a late text layer (e.g. an appendix) can't trick the heuristic into treating a scanned document as native.
Step 2a — native extraction
If the probe passes, the per-page strings are already the desired output. The
pipeline reports extract progress per page and returns them joined by
newlines. No rendering or OCR is performed.
Step 2b — render + OCR fallback
If the probe fails, or native extraction errored (encrypted or exotic files), the pipeline falls back:
- Render —
render_pdf_pagesdraws each page to an image with PDFium. Rendering is sequential because a PDFium document cannot be shared across threads.renderprogress is reported per page. - OCR — each rendered page is passed to
ocr_image, which runs theocrstext-detection and text-recognition models over it and returns the recognized lines. OCR is CPU-heavy, so pages are processed in parallel with Rayon; results are reported in document order.
The OCR engine (ocr/src/ocr.rs)
- Two
.rtenmodels — text detection and text recognition — are embedded into the binary at compile time viainclude_bytes!. - A process-wide
OcrEngineis constructed once (OnceLock) from those models. ocr_imageconverts the image to RGB, prepares it, and extracts text in reading order.
The models are not committed to git; download them with
crates/ocr/scripts/download-models.sh before building.
Progress reporting
Each stage advances a Progress { stage, current, total } value, where stages
are Detect, Extract, Render, and Ocr. The Tauri layer maps these to the
event phase strings consumed by the frontend (see
Import and Events).