processors-rs 0.7.1

Embed anything at lightning speed
Documentation
use input::{Args, Image};

use super::*;
use std::process::{Command, Stdio};
use std::string::ToString;

use crate::pdf::tesseract::error::{TessError, TessResult};
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;

#[cfg(target_os = "windows")]
const CREATE_NO_WINDOW: u32 = 0x08000000;

pub(crate) fn get_tesseract_command(path: Option<&str>) -> Command {
    if let Some(path) = path {
        Command::new(path)
    } else {
        let tesseract = if cfg!(target_os = "windows") {
            "tesseract.exe"
        } else {
            "tesseract"
        };

        Command::new(tesseract)
    }
}

pub fn get_tesseract_version() -> TessResult<String> {
    let mut command = get_tesseract_command(None);
    command.arg("--version");

    run_tesseract_command(&mut command)
}

pub fn get_tesseract_langs() -> TessResult<Vec<String>> {
    let mut command = get_tesseract_command(None);
    command.arg("--list-langs");

    let output = run_tesseract_command(&mut command)?;
    let langs = output.lines().skip(1).map(|x| x.into()).collect();
    Ok(langs)
}

pub(crate) fn run_tesseract_command(command: &mut Command) -> TessResult<String> {
    if cfg!(debug_assertions) {
        show_command(command);
    }

    #[cfg(target_os = "windows")]
    command.creation_flags(CREATE_NO_WINDOW);

    let child = command
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .map_err(|_| TessError::TesseractNotFoundError)?;

    let output = child
        .wait_with_output()
        .map_err(|_| TessError::TesseractNotFoundError)?;

    let out = String::from_utf8(output.stdout).unwrap();
    let err = String::from_utf8(output.stderr).unwrap();
    let status = output.status;

    match status.code() {
        Some(0) => Ok(out),
        _ => Err(TessError::CommandExitStatusError(status.to_string(), err)),
    }
}

fn show_command(command: &Command) {
    let params: Vec<String> = command
        .get_args()
        .map(|x| x.to_str().unwrap_or(""))
        .map(|s| s.to_string())
        .collect();

    println!(
        "Tesseract Command: {} {}",
        command.get_program().to_str().unwrap(),
        params.join(" ")
    );
}

pub fn image_to_string(image: &Image, args: &Args) -> TessResult<String> {
    let mut command = create_tesseract_command(image, args)?;
    let output = run_tesseract_command(&mut command)?;

    Ok(output)
}

pub(crate) fn create_tesseract_command(image: &Image, args: &Args) -> TessResult<Command> {
    let path = args.path.clone();
    let mut command = get_tesseract_command(path.as_deref());
    command
        .arg(image.get_image_path()?)
        .arg("stdout")
        .arg("-l")
        .arg(args.lang.clone());

    if let Some(dpi) = args.dpi {
        command.arg("--dpi").arg(dpi.to_string());
    }

    if let Some(psm) = args.psm {
        command.arg("--psm").arg(psm.to_string());
    }

    if let Some(oem) = args.oem {
        command.arg("--oem").arg(oem.to_string());
    }

    for parameter in args.get_config_variable_args() {
        command.arg("-c").arg(parameter);
    }

    Ok(command)
}

#[cfg(test)]
mod tests {
    use crate::pdf::tesseract::command::get_tesseract_langs;

    #[test]
    fn test_get_tesseract_langs() {
        let langs = get_tesseract_langs().unwrap();

        assert!(langs.contains(&"eng".into()));
    }
}