use std::path::Path;
use image::RgbImage;
use ppocr_rs::LayoutAnalyzer;
use crate::engine::{OcrEngine, OcrPageResult};
use crate::error::{Error, Result};
use crate::input::{load_image, load_tiff_pages};
use crate::output::to_markdown;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OutputFormat {
#[default]
Markdown,
Json,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum InputKind { Image, Tiff }
pub struct DocPipeline {
engine: Box<dyn OcrEngine>,
layout: LayoutAnalyzer,
}
impl DocPipeline {
pub fn new(engine: Box<dyn OcrEngine>, layout_model: impl AsRef<Path>) -> Result<Self> {
let layout_model = layout_model.as_ref();
if !layout_model.exists() {
return Err(Error::ModelNotFound(format!(
"PP-DocLayoutV3.onnx non trovato: {}",
layout_model.display()
)));
}
let layout = LayoutAnalyzer::from_path(layout_model)?;
Ok(Self { engine, layout })
}
pub fn process_file(
&mut self,
path: impl AsRef<Path>,
format: OutputFormat,
) -> Result<String> {
let path = path.as_ref();
let kind = detect_kind(path);
let pages = self.load_pages(path, kind)?;
let results = self.process_pages(pages)?;
Ok(render(&results, format))
}
pub fn process_image(&mut self, img: &RgbImage) -> Result<OcrPageResult> {
self.engine.process_page(img, &mut self.layout)
}
fn load_pages(&self, path: &Path, kind: InputKind) -> Result<Vec<RgbImage>> {
match kind {
InputKind::Tiff => load_tiff_pages(path),
InputKind::Image => load_image(path).map(|img| vec![img]),
}
}
fn process_pages(&mut self, pages: Vec<RgbImage>) -> Result<Vec<OcrPageResult>> {
pages.iter()
.map(|img| self.engine.process_page(img, &mut self.layout))
.collect()
}
}
fn detect_kind(path: &Path) -> InputKind {
match path.extension().and_then(|e| e.to_str()).map(|s| s.to_ascii_lowercase()).as_deref() {
Some("tif" | "tiff") => InputKind::Tiff,
_ => InputKind::Image,
}
}
fn render(results: &[OcrPageResult], format: OutputFormat) -> String {
match format {
OutputFormat::Markdown => to_markdown(results),
OutputFormat::Json => serde_json::to_string_pretty(results)
.unwrap_or_else(|e| format!("{{\"error\":\"{e}\"}}") ),
}
}