df-ocr-switcher 0.1.0

Document OCR pipeline in pure Rust: scanned PDF / multi-page TIFF / image → Markdown. PaddleOCR PP-OCRv6 and Tesseract 5.5 as interchangeable engines, PP-DocLayoutV3 layout for both.
Documentation
# df-ocr-switcher

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

[![Crates.io](https://img.shields.io/crates/v/df-ocr-switcher)](https://crates.io/crates/df-ocr-switcher)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Rust 2021](https://img.shields.io/badge/Rust-2021-orange.svg)](https://www.rust-lang.org/)

Two OCR engines, one interface:

| Engine | Crate | Feature flag |
|--------|-------|-------------|
| **PaddleOCR PP-OCRv6** (default) | [`ppocr-rs`]https://crates.io/crates/ppocr-rs | _(always on)_ |
| **Tesseract 5.5** | [`tesseract5-rs`]https://crates.io/crates/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

```toml
[dependencies]
# PaddleOCR engine only (default):
df-ocr-switcher = "0.1"

# + Tesseract engine:
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]https://github.com/microsoft/onnxruntime/releases.

---

## Quick start

### Process a TIFF to Markdown (PaddleOCR)

```rust
use df_ocr_switcher::{DocPipeline, OutputFormat, PpOcrEngine, PpOcrEngineConfig};
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // PP-DocLayoutV3 model (download from PaddlePaddle ModelHub or build ppocr-rs)
    let layout_model = PathBuf::from("models/paddleocr/layout/PP-DocLayoutV3.onnx");

    // Engine with auto-download of PP-OCRv6 Tiny models
    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)

```rust
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)

```rust
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),  // SLANet_plus uses 488×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)?;
    // Tables are rendered as GFM:
    //   | Col A | Col B |
    //   |-------|-------|
    //   | val 1 | val 2 |
    println!("{md}");
    Ok(())
}
```

### Tesseract engine (requires `features = ["tesseract-engine"]`)

```rust
#[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:

```powershell
# 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](LICENSE).