df-ocr-switcher
Document OCR pipeline in pure Rust — scanned PDF / multi-page TIFF / image → Markdown.

Two OCR engines, one interface:
| Engine |
Crate |
Feature flag |
| PaddleOCR PP-OCRv6 (default) |
ppocr-rs |
(always on) |
| Tesseract 5.5 |
tesseract5-rs |
tesseract-engine |
Layout analysis (PP-DocLayoutV3) and document-orientation correction are shared by both engines.
Table structure recognition (SLANet_plus → GFM Markdown) is available in the PaddleOCR path.
Installation
[dependencies]
df-ocr-switcher = "0.1"
df-ocr-switcher = { version = "0.1", features = ["tesseract-engine"] }
Runtime requirement — ONNX Runtime shared library. Set ORT_DYLIB_PATH to the path
of onnxruntime.dll / libonnxruntime.so before running. Download from
github.com/microsoft/onnxruntime/releases.
Quick start
Process a TIFF to Markdown (PaddleOCR)
use df_ocr_switcher::{DocPipeline, OutputFormat, PpOcrEngine, PpOcrEngineConfig};
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let layout_model = PathBuf::from("models/paddleocr/layout/PP-DocLayoutV3.onnx");
let engine = PpOcrEngine::new(PpOcrEngineConfig::default())?;
let mut pipeline = DocPipeline::new(Box::new(engine), &layout_model)?;
let markdown = pipeline.process_file(
&PathBuf::from("document.tiff"),
OutputFormat::Markdown,
)?;
println!("{markdown}");
Ok(())
}
With local model paths (no auto-download)
use df_ocr_switcher::{DocPipeline, OcrModelPaths, OutputFormat, PpOcrEngine, PpOcrEngineConfig};
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let models = PathBuf::from("models/paddleocr");
let engine = PpOcrEngine::new(PpOcrEngineConfig {
ocr_models: Some(OcrModelPaths {
det: models.join("latin/det.onnx"),
rec: models.join("latin/rec_latin.onnx"),
dict: models.join("latin/dict_latin.txt"),
}),
..PpOcrEngineConfig::default()
})?;
let layout_model = models.join("layout/PP-DocLayoutV3.onnx");
let mut pipeline = DocPipeline::new(Box::new(engine), &layout_model)?;
let md = pipeline.process_file(&PathBuf::from("scan.tiff"), OutputFormat::Markdown)?;
println!("{md}");
Ok(())
}
With table recognition (GFM Markdown)
use df_ocr_switcher::{DocPipeline, OcrModelPaths, OutputFormat,
PpOcrEngine, PpOcrEngineConfig, TableModelPaths};
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let models = PathBuf::from("models/paddleocr");
let engine = PpOcrEngine::new(PpOcrEngineConfig {
ocr_models: Some(OcrModelPaths {
det: models.join("latin/det.onnx"),
rec: models.join("latin/rec_latin.onnx"),
dict: models.join("latin/dict_latin.txt"),
}),
enable_tables: true,
table_models: Some(TableModelPaths {
structure_onnx: models.join("table/SLANet_plus.onnx"),
structure_dict: models.join("table/table_structure_dict.txt"),
input_size: Some(488), }),
..PpOcrEngineConfig::default()
})?;
let layout_model = models.join("layout/PP-DocLayoutV3.onnx");
let mut pipeline = DocPipeline::new(Box::new(engine), &layout_model)?;
let md = pipeline.process_file(&PathBuf::from("document_with_tables.tiff"), OutputFormat::Markdown)?;
println!("{md}");
Ok(())
}
Tesseract engine (requires features = ["tesseract-engine"])
#[cfg(feature = "tesseract-engine")]
use df_ocr_switcher::{DocPipeline, OutputFormat, TesseractEngine, TesseractEngineConfig};
use std::path::PathBuf;
#[cfg(feature = "tesseract-engine")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let layout_model = PathBuf::from("models/paddleocr/layout/PP-DocLayoutV3.onnx");
let engine = TesseractEngine::new(TesseractEngineConfig {
lang: "ita+eng".into(),
tessdata: Some(PathBuf::from("/usr/share/tessdata")),
ori_model: None,
psm: None,
})?;
let mut pipeline = DocPipeline::new(Box::new(engine), &layout_model)?;
let md = pipeline.process_file(&PathBuf::from("document.tiff"), OutputFormat::Markdown)?;
println!("{md}");
Ok(())
}
CLI — ocr-doc
The crate ships an ocr-doc binary:
# ARM64 Windows
$env:ORT_DYLIB_PATH = "C:\path\to\onnxruntime.dll"
$env:PPOCR_MODELS_DIR = "models\paddleocr"
# Basic OCR → Markdown
ocr-doc document.tiff
# With table recognition
ocr-doc document.tiff --tables
# Tesseract engine
ocr-doc document.tiff --engine tesseract --lang ita+eng
# Save to file
ocr-doc document.tiff --tables --output result.md
Output formats
| Format |
OutputFormat |
Notes |
| Markdown |
OutputFormat::Markdown |
GFM tables, # headings, $$ LaTeX equations |
| JSON |
OutputFormat::Json |
Per-page structured output with bounding boxes |
Environment variables
| Variable |
Description |
ORT_DYLIB_PATH |
Path to onnxruntime.dll / libonnxruntime.so (required) |
PPOCR_MODELS_DIR |
Base dir for models (layout/, latin/, table/) |
PPOCR_LAYOUT_MODEL |
Override PP-DocLayoutV3.onnx path |
TESSDATA_PREFIX |
tessdata directory (Tesseract engine) |
Features
| Feature |
Default |
Description |
tesseract-engine |
off |
Enable Tesseract 5.5 as alternate OCR engine |
searchable-pdf |
off |
Add invisible text layer to PDF output (lopdf) |
License
MIT — see LICENSE.