use crate::ocr::error::OcrError;
use std::path::Path;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn download_language_pack(lang: &str, output_dir: &Path) -> Result<(), OcrError> {
let traineddata_path = output_dir.join(format!("{}.traineddata", lang));
if traineddata_path.exists() {
tracing::debug!(
"Language pack '{}' already exists at {}",
lang,
traineddata_path.display()
);
return Ok(());
}
let urls = [
format!(
"https://github.com/tesseract-ocr/tessdata_fast/raw/main/{}.traineddata",
lang
),
format!(
"https://raw.githubusercontent.com/tesseract-ocr/tessdata_fast/main/{}.traineddata",
lang
),
];
for url in &urls {
match download_file(url, &traineddata_path) {
Ok(_) => {
tracing::info!(
"Successfully downloaded language pack '{}' to {}",
lang,
traineddata_path.display()
);
return Ok(());
}
Err(e) => {
tracing::warn!("Failed to download from {}: {}", url, e);
continue;
}
}
}
Err(OcrError::TesseractInitializationFailed(format!(
"Failed to download language pack '{}' from tessdata_fast repository. \
Tried URLs: {}. Check your network connection and verify the language code is valid.",
lang,
urls.join(", ")
)))
}
#[cfg(not(target_arch = "wasm32"))]
const MAX_TRAINEDDATA_BYTES: u64 = 64 * 1024 * 1024;
#[cfg(not(target_arch = "wasm32"))]
fn download_file(url: &str, output_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let response = ureq::get(url).call()?;
if response.status() != 200 {
return Err(format!("HTTP {}", response.status()).into());
}
let bytes = response
.into_body()
.with_config()
.limit(MAX_TRAINEDDATA_BYTES)
.read_to_vec()?;
let tmp_path = output_path.with_extension("traineddata.partial");
std::fs::write(&tmp_path, bytes)?;
std::fs::rename(&tmp_path, output_path)?;
Ok(())
}