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
//! Basic OCR example: TIFF → Markdown via PaddleOCR PP-OCRv6.
//!
//! Models are auto-downloaded from HuggingFace on first run (requires feature `fetch-models`
//! on the ppocr-rs dep). To use local models, set `PPOCR_MODELS_DIR`.
//!
//! ## Usage
//!
//! ```powershell
//! # Provide the ONNX Runtime shared library:
//! $env:ORT_DYLIB_PATH = "C:\path\to\onnxruntime.dll"
//!
//! # Run with a TIFF file:
//! cargo run --example basic_ocr -- path\to\document.tiff
//!
//! # Or set input via env var:
//! $env:OCR_INPUT = "path\to\document.tiff"
//! cargo run --example basic_ocr
//! ```

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ── Input file ────────────────────────────────────────────────────────────
    let input: PathBuf = std::env::args().nth(1)
        .or_else(|| std::env::var("OCR_INPUT").ok())
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            eprintln!("Usage: basic_ocr <input_file>");
            eprintln!("       OCR_INPUT=path/to/doc.tiff cargo run --example basic_ocr");
            std::process::exit(1);
        });

    // ── Model paths ───────────────────────────────────────────────────────────
    let layout_model = layout_model_path();
    let ocr_models   = ocr_model_paths();

    // ── Engine + pipeline ─────────────────────────────────────────────────────
    let engine = PpOcrEngine::new(PpOcrEngineConfig {
        ocr_models,
        ..PpOcrEngineConfig::default()
    })?;
    let mut pipeline = DocPipeline::new(Box::new(engine), &layout_model)?;

    // ── Process ───────────────────────────────────────────────────────────────
    eprintln!("Processing: {}", input.display());
    let markdown = pipeline.process_file(&input, OutputFormat::Markdown)?;
    print!("{markdown}");

    Ok(())
}

fn layout_model_path() -> PathBuf {
    if let Ok(p) = std::env::var("PPOCR_LAYOUT_MODEL") {
        return PathBuf::from(p);
    }
    let base = std::env::var("PPOCR_MODELS_DIR")
        .unwrap_or_else(|_| "models/paddleocr".into());
    PathBuf::from(base).join("layout/PP-DocLayoutV3.onnx")
}

fn ocr_model_paths() -> Option<OcrModelPaths> {
    let base = std::env::var("PPOCR_MODELS_DIR").ok()?;
    let latin = PathBuf::from(base).join("latin");
    let det  = latin.join("det.onnx");
    let rec  = latin.join("rec_latin.onnx");
    let dict = latin.join("dict_latin.txt");
    if det.exists() && rec.exists() && dict.exists() {
        Some(OcrModelPaths { det, rec, dict })
    } else {
        None  // fallback: ppocr-rs ModelHub auto-download
    }
}