Skip to main content

tesseract_ocr_static/
vars.rs

1use core::ffi::CStr;
2use std::fs::File;
3use std::io::BufRead;
4use std::io::BufReader;
5use std::path::Path;
6
7use crate::InvalidVariable;
8use crate::Tesseract;
9use crate::WriteFailed;
10
11impl Tesseract {
12    /// Set tesseract variable.
13    ///
14    /// # How to improve text recognition?
15    ///
16    /// There is a guide on how to improve text recognition:
17    /// <https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html>
18    ///
19    /// # Variables
20    ///
21    #[doc = include_str!("../variables.md")]
22    pub fn set_variable(&mut self, name: &CStr, value: &CStr) -> Result<(), InvalidVariable> {
23        let ret =
24            unsafe { c::TessBaseAPISetVariable(self.ptr.as_ptr(), name.as_ptr(), value.as_ptr()) };
25        if ret != 0 {
26            Err(InvalidVariable)
27        } else {
28            Ok(())
29        }
30    }
31
32    /// Set tesseract variable.
33    ///
34    /// Includes debug variables.
35    ///
36    /// See [set_variable](Self::set_variable) for more information.
37    pub fn set_debug_variable(&mut self, name: &CStr, value: &CStr) -> Result<(), InvalidVariable> {
38        let ret = unsafe {
39            c::TessBaseAPISetDebugVariable(self.ptr.as_ptr(), name.as_ptr(), value.as_ptr())
40        };
41        if ret != 0 {
42            Err(InvalidVariable)
43        } else {
44            Ok(())
45        }
46    }
47
48    /// Get integer variable value.
49    pub fn get_variable_i32(&self, name: &CStr) -> Option<i32> {
50        let mut value = 0;
51        let ret =
52            unsafe { c::TessBaseAPIGetIntVariable(self.ptr.as_ptr(), name.as_ptr(), &mut value) };
53        (ret != 0).then_some(value)
54    }
55
56    /// Get boolean variable value.
57    pub fn get_variable_bool(&self, name: &CStr) -> Option<bool> {
58        let mut value = 0;
59        let ret =
60            unsafe { c::TessBaseAPIGetBoolVariable(self.ptr.as_ptr(), name.as_ptr(), &mut value) };
61        (ret != 0).then_some(value != 0)
62    }
63
64    /// Get floating point variable value.
65    pub fn get_variable_f64(&self, name: &CStr) -> Option<f64> {
66        let mut value = 0.0;
67        let ret = unsafe {
68            c::TessBaseAPIGetDoubleVariable(self.ptr.as_ptr(), name.as_ptr(), &mut value)
69        };
70        (ret != 0).then_some(value)
71    }
72
73    /// Get string variable value.
74    pub fn get_variable_c_str<'a>(&'a self, name: &CStr) -> Option<&'a CStr> {
75        let ptr = unsafe { c::TessBaseAPIGetStringVariable(self.ptr.as_ptr(), name.as_ptr()) };
76        if ptr.is_null() {
77            return None;
78        }
79        Some(unsafe { CStr::from_ptr(ptr) })
80    }
81
82    /// Read variables from the configuration file.
83    ///
84    /// Doesn't include debug variables.
85    ///
86    /// This is a re-implementation of the original library call that handles I/O errors and
87    /// invalid variables.
88    pub fn read_config_file(&mut self, filename: &Path) -> std::io::Result<()> {
89        do_read_config_file(filename, |name, value| self.set_variable(name, value))
90    }
91
92    /// Read variables from the configuration file.
93    ///
94    /// Includes debug variables.
95    ///
96    /// This is a re-implementation of the original library call that handles I/O errors and
97    /// invalid variables.
98    pub fn read_debug_config_file(&mut self, filename: &Path) -> std::io::Result<()> {
99        do_read_config_file(filename, |name, value| self.set_debug_variable(name, value))
100    }
101
102    /// Print all variables to a file.
103    pub fn print_variables_to_file(&self, filename: &CStr) -> Result<(), WriteFailed> {
104        let ret =
105            unsafe { c::TessBaseAPIPrintVariablesToFile(self.ptr.as_ptr(), filename.as_ptr()) };
106        if ret < 0 { Err(WriteFailed) } else { Ok(()) }
107    }
108}
109
110fn do_read_config_file(
111    filename: &Path,
112    mut set_variable: impl FnMut(&CStr, &CStr) -> Result<(), InvalidVariable>,
113) -> std::io::Result<()> {
114    use std::io::ErrorKind::InvalidData;
115    let file = BufReader::new(File::open(filename)?);
116    for line in file.lines() {
117        let mut line = line?.into_bytes();
118        // Add NUL byte.
119        line.push(0);
120        let mut line = line.as_mut_slice();
121        // Trim whitespace from the left.
122        while let Some(ch) = line.first() {
123            if !ch.is_ascii_whitespace() {
124                break;
125            }
126            line = &mut line[1..];
127        }
128        // Skip comments and empty lines.
129        match line.first() {
130            Some(&b'#') | None => continue,
131            _ => {}
132        }
133        let i = line
134            .iter()
135            .position(|ch| ch.is_ascii_whitespace())
136            .ok_or(InvalidData)?;
137        // Insert NUL byte.
138        line[i] = 0;
139        let name = &line[..i];
140        let mut value = &line[i + 1..];
141        // Trim whitespace from the left.
142        while let Some(ch) = value.first() {
143            if !ch.is_ascii_whitespace() {
144                break;
145            }
146            value = &value[1..];
147        }
148        let name = CStr::from_bytes_with_nul(name).map_err(|_| InvalidData)?;
149        let value = CStr::from_bytes_with_nul(value).map_err(|_| InvalidData)?;
150        set_variable(name, value).map_err(std::io::Error::other)?;
151    }
152    Ok(())
153}