1use anyhow::Result;
2pub use custom_ocr::Credentials;
3use image::DynamicImage;
4use std::time::Duration;
5
6#[derive(Debug, Clone)]
7pub enum OcrProvider {
8 Auto,
9 MacOS,
10 Windows,
11 Tesseract,
12 Custom { credentials: Credentials },
13}
14
15#[derive(Debug, Clone)]
16pub struct OcrOptions {
17 languages: Vec<Language>,
18 confidence_threshold: f32,
19 timeout: Duration,
20}
21
22impl Default for OcrOptions {
23 fn default() -> Self {
24 Self {
25 languages: vec![Language::English],
26 confidence_threshold: 0.0,
27 timeout: Duration::from_secs(30),
28 }
29 }
30}
31
32impl OcrOptions {
33 pub fn languages(mut self, langs: Vec<Language>) -> Self {
34 self.languages = langs;
35 self
36 }
37
38 pub fn confidence_threshold(mut self, threshold: f32) -> Self {
39 self.confidence_threshold = threshold;
40 self
41 }
42
43 pub fn timeout(mut self, timeout: Duration) -> Self {
44 self.timeout = timeout;
45 self
46 }
47}
48
49pub struct OcrEngine {
50 provider: OcrProvider,
51 options: OcrOptions,
52}
53
54impl OcrEngine {
55 pub fn new(provider: OcrProvider) -> Result<Self> {
56 Ok(Self {
57 provider,
58 options: OcrOptions::default(),
59 })
60 }
61
62 pub fn with_options(mut self, options: OcrOptions) -> Self {
63 self.options = options;
64 self
65 }
66
67 pub async fn recognize_image(
68 &self,
69 image: &DynamicImage,
70 ) -> Result<(String, String, Option<f64>)> {
71 match &self.provider {
72 #[cfg(target_os = "macos")]
73 OcrProvider::MacOS => Ok(perform_ocr_apple(image, &self.options.languages)),
74 OcrProvider::Windows => {
75 #[cfg(target_os = "windows")]
76 {
77 perform_ocr_windows(image).await
78 }
79 #[cfg(not(target_os = "windows"))]
80 {
81 Err(anyhow::anyhow!(
82 "Windows OCR is not available on this platform"
83 ))
84 }
85 }
86 OcrProvider::Tesseract => {
87 Ok(perform_ocr_tesseract(image, self.options.languages.clone()))
88 }
89 OcrProvider::Custom { credentials } => {
90 perform_ocr_custom(image, self.options.languages.clone(), &credentials).await
91 }
92 OcrProvider::Auto => {
93 #[cfg(target_os = "macos")]
94 {
95 Ok(perform_ocr_apple(image, &self.options.languages))
96 }
97 #[cfg(target_os = "windows")]
98 {
99 perform_ocr_windows(image).await
100 }
101 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
102 {
103 Ok(perform_ocr_tesseract(image, self.options.languages.clone()))
104 }
105 }
106 _ => Err(anyhow::anyhow!("Invalid OCR provider")),
107 }
108 }
109
110 pub async fn recognize_file(&self, path: &str) -> Result<(String, String, Option<f64>)> {
111 let img = image::open(path)?;
112 self.recognize_image(&img).await
113 }
114
115 pub async fn recognize_batch(
116 &self,
117 paths: Vec<&str>,
118 ) -> Result<Vec<(String, String, Option<f64>)>> {
119 let mut results = Vec::with_capacity(paths.len());
120 for path in paths {
121 results.push(self.recognize_file(path).await?);
122 }
123 Ok(results)
124 }
125}
126
127#[cfg(target_os = "macos")]
128pub mod apple;
129pub mod custom_ocr;
130pub mod language;
131#[cfg(target_os = "windows")]
132pub mod microsoft;
133pub mod tesseract;
134
135#[cfg(target_os = "macos")]
136pub use apple::perform_ocr_apple;
137pub use custom_ocr::perform_ocr_custom;
138pub use language::*;
139#[cfg(target_os = "windows")]
140pub use microsoft::perform_ocr_windows;
141pub use tesseract::perform_ocr_tesseract;