From 875ca053fc5180ecb1ea41f419eb558adccbed21 Mon Sep 17 00:00:00 2001 From: Fu Wang Date: Mon, 23 Jun 2025 22:41:30 +0800 Subject: [PATCH 1/4] Simplify the library API calls using the windows-link macro. --- Cargo.toml | 2 +- src/errors.rs | 5 +- src/ffi.rs | 71 ++++++++++++++-------------- src/lib.rs | 38 --------------- src/ocr_engine.rs | 117 ++++++++++++++-------------------------------- src/ocr_line.rs | 51 +++++++------------- src/ocr_result.rs | 37 ++++++--------- src/ocr_word.rs | 26 +++++------ 8 files changed, 114 insertions(+), 233 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 70e08b8..f32a264 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ targets = ["x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc", "i686-pc-windows [dependencies] image = "0.25" -libloading = "0.8.7" thiserror = "2.0.12" serde = { version = "1.0.219", features = ["derive"] } +windows-link = "0.1.3" [dev-dependencies] imageproc = "0.25" diff --git a/src/errors.rs b/src/errors.rs index 3bbcd23..eab5aed 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -7,9 +7,6 @@ pub enum OneOcrError { #[error("Image format not supported: {0}")] ImageFormatError(String), - #[error("Failed to load library: {0}")] - LibraryLoadError(#[from] libloading::Error), - #[error("Failed to load model file: {0}")] ModelFileLoadError(String), @@ -17,7 +14,7 @@ pub enum OneOcrError { InvalidModelKey(String), #[error("Failed to run ocr API {result}, result: {message}")] - OcrApiError { result: i64, message: String }, + OcrApiError { result: i32, message: String }, #[error("Other error: {0}")] Other(String), diff --git a/src/ffi.rs b/src/ffi.rs index 06f1c83..3bb6088 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,43 +1,40 @@ -use std::ffi::c_char; +use std::ffi::{c_char, c_void}; +use windows_link::link; -pub type CreateOcrInitOptions = unsafe extern "C" fn(*mut i64) -> i64; -pub type OcrInitOptionsSetUseModelDelayLoad = unsafe extern "C" fn(i64, c_char) -> i64; -pub type CreateOcrPipeline = unsafe extern "C" fn( +link!("oneocr.dll" "system" fn CreateOcrInitOptions(init_option: *mut *mut c_void) -> i32); +link!("oneocr.dll" "system" fn OcrInitOptionsSetUseModelDelayLoad(init_option: *mut c_void) -> i32); +link!("oneocr.dll" "system" fn CreateOcrPipeline( model_path: *const c_char, key: *const c_char, - ctx: i64, - pipeline: *mut i64, -) -> i64; - -pub type CreateOcrProcessOptions = unsafe extern "C" fn(*mut i64) -> i64; -pub type OcrProcessOptionsGetMaxRecognitionLineCount = unsafe extern "C" fn(i64, *mut i64) -> i64; -pub type OcrProcessOptionsSetMaxRecognitionLineCount = unsafe extern "C" fn(i64, i64) -> i64; -pub type OcrProcessOptionsGetResizeResolution = - unsafe extern "C" fn(i64, *mut i64, *mut i64) -> i64; -pub type OcrProcessOptionsSetResizeResolution = unsafe extern "C" fn(i64, i64, i64) -> i64; - -/// Image resolution must be great than 50*50, otherwise it will return error code 3. -/// For images with a resolution less than 50*50, you should manually scale up the image first. -pub type RunOcrPipeline = unsafe extern "C" fn(i64, *const RawImage, i64, *mut i64) -> i64; - -pub type GetImageAngle = unsafe extern "C" fn(i64, *mut f32) -> i64; - -pub type GetOcrLineCount = unsafe extern "C" fn(i64, *mut i64) -> i64; -pub type GetOcrLine = unsafe extern "C" fn(i64, i64, *mut i64) -> i64; -pub type GetOcrLineContent = unsafe extern "C" fn(i64, *mut i64) -> i64; -pub type GetOcrLineBoundingBox = unsafe extern "C" fn(i64, *mut *const RawBBox) -> i64; -pub type GetOcrLineStyle = unsafe extern "C" fn(i64, *mut i32, *mut f32) -> i64; - -pub type GetOcrLineWordCount = unsafe extern "C" fn(i64, *mut i64) -> i64; -pub type GetOcrWord = unsafe extern "C" fn(i64, i64, *mut i64) -> i64; -pub type GetOcrWordContent = unsafe extern "C" fn(i64, *mut i64) -> i64; -pub type GetOcrWordBoundingBox = unsafe extern "C" fn(i64, *mut *const RawBBox) -> i64; -pub type GetOcrWordConfidence = unsafe extern "C" fn(i64, *mut f32) -> i64; - -pub type ReleaseOcrResult = unsafe extern "C" fn(i64); -pub type ReleaseOcrInitOptions = unsafe extern "C" fn(i64); -pub type ReleaseOcrPipeline = unsafe extern "C" fn(i64); -pub type ReleaseOcrProcessOptions = unsafe extern "C" fn(i64); + ctx: *mut c_void, + pipeline: *mut *mut c_void +) -> i32); +link!("oneocr.dll" "system" fn CreateOcrProcessOptions(option: *mut *mut c_void) -> i32); +link!("oneocr.dll" "system" fn OcrProcessOptionsGetMaxRecognitionLineCount(option: *mut c_void, count: *mut i32) -> i32); +link!("oneocr.dll" "system" fn OcrProcessOptionsSetMaxRecognitionLineCount(option: *mut c_void, count: i32) -> i32); +link!("oneocr.dll" "system" fn OcrProcessOptionsGetResizeResolution(option: *mut c_void, width: *mut i64, height: *mut i64) -> i32); +link!("oneocr.dll" "system" fn OcrProcessOptionsSetResizeResolution (option: *mut c_void, width: i32, height: i32) -> i32); +link!("oneocr.dll" "system" fn RunOcrPipeline( + pipeline: *mut c_void, + image: *const RawImage, + process_options: *mut c_void, + result: *mut *mut c_void +) -> i32); +link!("oneocr.dll" "system" fn GetImageAngle(pipeline: *mut c_void, angle: *mut f32) -> i32); +link!("oneocr.dll" "system" fn GetOcrLineCount(result: *mut c_void, count: *mut i64) -> i32); +link!("oneocr.dll" "system" fn GetOcrLine(result: *mut c_void, index: i64, line: *mut *mut c_void) -> i32); +link!("oneocr.dll" "system" fn GetOcrLineContent(line: *mut c_void, content: *mut *const c_char) -> i32); +link!("oneocr.dll" "system" fn GetOcrLineBoundingBox(line: *mut c_void, bbox: *mut *const RawBBox) -> i32); +link!("oneocr.dll" "system" fn GetOcrLineStyle(line: *mut c_void, style: *mut i32, confidence: *mut f32) -> i32); +link!("oneocr.dll" "system" fn GetOcrLineWordCount(line: *mut c_void, count: *mut i64) -> i32); +link!("oneocr.dll" "system" fn GetOcrWord(line: *mut c_void, index: i64, word: *mut *mut c_void) -> i32); +link!("oneocr.dll" "system" fn GetOcrWordContent(word: *mut c_void, content: *mut *const c_char) -> i32); +link!("oneocr.dll" "system" fn GetOcrWordBoundingBox(word: *mut c_void, bbox: *mut *const RawBBox) -> i32); +link!("oneocr.dll" "system" fn GetOcrWordConfidence(word: *mut c_void, confidence: *mut f32) -> i32); +link!("oneocr.dll" "system" fn ReleaseOcrResult(result: *mut c_void)); +link!("oneocr.dll" "system" fn ReleaseOcrInitOptions(init_options: *mut c_void)); +link!("oneocr.dll" "system" fn ReleaseOcrPipeline(pipeline: *mut c_void)); +link!("oneocr.dll" "system" fn ReleaseOcrProcessOptions(process_options: *mut c_void)); /// This `RawImage` struct represents an image in a format suitable for OCR processing. Used for FFI. /// - t: Type of the image (e.g., RGB, RGBA). diff --git a/src/lib.rs b/src/lib.rs index f7c923f..5b82804 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,44 +18,6 @@ pub use ocr_word::OcrWord; pub(crate) const ONE_OCR_MODEL_FILE_NAME: &str = "oneocr.onemodel"; pub(crate) const ONE_OCR_MODEL_KEY: &str = r#"kj)TGtrK>f]b[Piow.gU+nC@s""""""4"#; -/// A macro to load a symbol from the library. -/// This macro takes three arguments: -/// - `$library`: The library from which to load the symbol. -/// - `$var_name`: The name of the variable to store the loaded symbol. -/// - `$symbol_name_type`: The type of the symbol to load. -/// -/// This macro is used to simplify the process of loading symbols from the library. -/// It helps to avoid repetitive code and makes the code cleaner and more readable. -macro_rules! load_symbol { - ($library:expr, $var_name:ident, $symbol_name_type:ident) => { - let $var_name: libloading::Symbol<$symbol_name_type> = - unsafe { $library.get(stringify!($symbol_name_type).as_bytes())? }; - }; -} - -pub(crate) use load_symbol; - -/// A macro to attempt to load a symbol and call it, for use in contexts like `drop` -/// Errors during symbol loading are logged to stderr, and the call is skipped. -/// - `$library`: The library instance. -/// - `$symbol_name_type`: The type of the FFI function (also used as the symbol name). -/// - $($arg:expr),*`: The arguments to pass to the function if loaded successfully. -macro_rules! release_ocr_resource { - ($library:expr, $symbol_name_type:ident, $($arg:expr),* ) => { - match unsafe { $library.get::<$symbol_name_type>(stringify!($symbol_name_type).as_bytes()) } { - Ok(func_symbol) => { - unsafe { func_symbol($($arg),*) }; - } - Err(_) => { - // Ignore the error, as this is best effort - // and we are in the drop context. - } - } - }; -} - -pub(crate) use release_ocr_resource; - /// A macro to check the result of an OCR call and return an error if it fails. /// This macro takes an expression `$call` and an error message `$err_msg`. /// If the result of `$call` is not 0, it returns an `OneOcrError::OcrApiError` error with the provided message. diff --git a/src/ocr_engine.rs b/src/ocr_engine.rs index 1a70632..9e9b4e3 100644 --- a/src/ocr_engine.rs +++ b/src/ocr_engine.rs @@ -1,12 +1,4 @@ use crate::errors::OneOcrError; -use crate::ocr_result::OcrResult; -use crate::{ONE_OCR_MODEL_FILE_NAME, ONE_OCR_MODEL_KEY}; -use image::DynamicImage; -use libloading::Library; -use std::ffi::{CString, c_char}; -use std::path::Path; - -// FFI types use crate::ffi::{ CreateOcrInitOptions, CreateOcrPipeline, CreateOcrProcessOptions, OcrInitOptionsSetUseModelDelayLoad, OcrProcessOptionsGetMaxRecognitionLineCount, @@ -14,36 +6,31 @@ use crate::ffi::{ OcrProcessOptionsSetResizeResolution, RawImage, ReleaseOcrInitOptions, ReleaseOcrPipeline, ReleaseOcrProcessOptions, RunOcrPipeline, }; +use crate::ocr_result::OcrResult; +use crate::{ONE_OCR_MODEL_FILE_NAME, ONE_OCR_MODEL_KEY}; +use image::DynamicImage; +use std::ffi::{CString, c_void}; +use std::path::Path; +use std::ptr; + // Macros -use crate::{check_ocr_call, load_symbol, release_ocr_resource}; +use crate::check_ocr_call; /// The `OcrEngine` struct represents the OneOcr processing engine. #[derive(Debug)] pub struct OcrEngine { - lib: Library, - init_options: i64, - pipeline: i64, - process_options: i64, + init_options: *mut c_void, + pipeline: *mut c_void, + process_options: *mut c_void, } impl OcrEngine { /// Creates a new instance of the OCR engine. /// This function loads the necessary library and initializes the OCR pipeline. pub fn new() -> Result { - let lib = unsafe { Library::new("oneocr.dll")? }; - - load_symbol!(lib, create_ocr_init_options, CreateOcrInitOptions); - load_symbol!( - lib, - ocr_init_options_set_use_model_delay_load, - OcrInitOptionsSetUseModelDelayLoad - ); - load_symbol!(lib, create_ocr_pipeline, CreateOcrPipeline); - load_symbol!(lib, create_ocr_process_options, CreateOcrProcessOptions); - - let mut init_options: i64 = 0; + let mut init_options: *mut c_void = ptr::null_mut(); check_ocr_call!( - unsafe { create_ocr_init_options(&mut init_options) }, + unsafe { CreateOcrInitOptions(&mut init_options) }, "Failed to create init options" ); @@ -51,7 +38,7 @@ impl OcrEngine { // In C, char can be signed or unsigned by default depending on the compiler/platform. // Rust's c_char is i8. Assuming 0 is a valid value for false. check_ocr_call!( - unsafe { ocr_init_options_set_use_model_delay_load(init_options, 0 as c_char) }, + unsafe { OcrInitOptionsSetUseModelDelayLoad(init_options) }, "Failed to set model delay load" ); @@ -67,10 +54,10 @@ impl OcrEngine { OneOcrError::InvalidModelKey(format!("Failed to convert model key to CString: {}", e)) })?; - let mut pipeline: i64 = 0; + let mut pipeline: *mut c_void = ptr::null_mut(); check_ocr_call!( unsafe { - create_ocr_pipeline( + CreateOcrPipeline( model_path_cstr.as_ptr(), key_cstr.as_ptr(), init_options, @@ -80,14 +67,13 @@ impl OcrEngine { "Failed to create OCR pipeline" ); - let mut process_options: i64 = 0; + let mut process_options: *mut c_void = ptr::null_mut(); check_ocr_call!( - unsafe { create_ocr_process_options(&mut process_options) }, + unsafe { CreateOcrProcessOptions(&mut process_options) }, "Failed to create OCR process options" ); Ok(Self { - lib, init_options, pipeline, process_options, @@ -96,16 +82,11 @@ impl OcrEngine { /// Retrieves the maximum number of lines that can be recognized. /// Default is 100. - pub fn get_max_recognition_line_count(&self) -> Result { - load_symbol!( - self.lib, - ocr_process_options_get_max_recognition_line_count, - OcrProcessOptionsGetMaxRecognitionLineCount - ); - let mut count: i64 = 0; + pub fn get_max_recognition_line_count(&self) -> Result { + let mut count: i32 = 0; check_ocr_call!( unsafe { - ocr_process_options_get_max_recognition_line_count(self.process_options, &mut count) + OcrProcessOptionsGetMaxRecognitionLineCount(self.process_options, &mut count) }, "Failed to get max recognition line count" ); @@ -114,16 +95,9 @@ impl OcrEngine { /// Sets the maximum number of lines that can be recognized. /// Default is 100, range is 0-1000. - pub fn set_max_recognition_line_count(&self, count: i64) -> Result<(), OneOcrError> { - load_symbol!( - self.lib, - ocr_process_options_set_max_recognition_line_count, - OcrProcessOptionsSetMaxRecognitionLineCount - ); + pub fn set_max_recognition_line_count(&self, count: i32) -> Result<(), OneOcrError> { check_ocr_call!( - unsafe { - ocr_process_options_set_max_recognition_line_count(self.process_options, count) - }, + unsafe { OcrProcessOptionsSetMaxRecognitionLineCount(self.process_options, count) }, "Failed to set max recognition line count" ); Ok(()) @@ -136,20 +110,11 @@ impl OcrEngine { /// /// Default is 1152*768. pub fn get_resize_resolution(&self) -> Result<(i64, i64), OneOcrError> { - load_symbol!( - self.lib, - ocr_process_options_get_resize_resolution, - OcrProcessOptionsGetResizeResolution - ); let mut width: i64 = 0; let mut height: i64 = 0; check_ocr_call!( unsafe { - ocr_process_options_get_resize_resolution( - self.process_options, - &mut width, - &mut height, - ) + OcrProcessOptionsGetResizeResolution(self.process_options, &mut width, &mut height) }, "Failed to get resize resolution" ); @@ -162,16 +127,9 @@ impl OcrEngine { /// It’s a performance and accuracy trade-off rather than a restriction on the original image’s resolution. /// /// The maximum resolution is 1152*768. - pub fn set_resize_resolution(&self, width: i64, height: i64) -> Result<(), OneOcrError> { - load_symbol!( - self.lib, - ocr_process_options_set_resize_resolution, - OcrProcessOptionsSetResizeResolution - ); + pub fn set_resize_resolution(&self, width: i32, height: i32) -> Result<(), OneOcrError> { check_ocr_call!( - unsafe { - ocr_process_options_set_resize_resolution(self.process_options, width, height) - }, + unsafe { OcrProcessOptionsSetResizeResolution(self.process_options, width, height) }, "Failed to set resize resolution" ); Ok(()) @@ -206,22 +164,13 @@ impl OcrEngine { data_ptr, }; - load_symbol!(self.lib, run_ocr_pipeline, RunOcrPipeline); - - let mut ocr_result_handle: i64 = 0; + let mut ocr_result: *mut c_void = ptr::null_mut(); check_ocr_call!( - unsafe { - run_ocr_pipeline( - self.pipeline, - &image, - self.process_options, - &mut ocr_result_handle, - ) - }, + unsafe { RunOcrPipeline(self.pipeline, &image, self.process_options, &mut ocr_result) }, "Failed to run OCR pipeline" ); - OcrResult::new(&self.lib, ocr_result_handle, word_level_detail) + OcrResult::new(ocr_result, word_level_detail) } /// Retrieves the path to the model file. @@ -245,8 +194,10 @@ impl OcrEngine { impl Drop for OcrEngine { fn drop(&mut self) { - release_ocr_resource!(self.lib, ReleaseOcrPipeline, self.pipeline); - release_ocr_resource!(self.lib, ReleaseOcrInitOptions, self.init_options); - release_ocr_resource!(self.lib, ReleaseOcrProcessOptions, self.process_options); + unsafe { + ReleaseOcrPipeline(self.pipeline); + ReleaseOcrInitOptions(self.init_options); + ReleaseOcrProcessOptions(self.process_options); + }; } } diff --git a/src/ocr_line.rs b/src/ocr_line.rs index ade7966..fb8911c 100644 --- a/src/ocr_line.rs +++ b/src/ocr_line.rs @@ -1,9 +1,9 @@ use crate::bounding_box::BoundingBox; use crate::errors::OneOcrError; use crate::ocr_word::OcrWord; -use libloading::Library; use serde::Serialize; -use std::ffi::{CStr, c_char}; +use std::ffi::{CStr, c_char, c_void}; +use std::ptr; // FFI types use crate::ffi::{ @@ -11,41 +11,35 @@ use crate::ffi::{ RawBBox, }; // Macros -use crate::{check_ocr_call, load_symbol}; +use crate::check_ocr_call; /// The `OcrLine` struct represents a line of text recognized by the OCR engine. /// It contains the recognized text, its bounding box, and optionally the words within the line. #[derive(Debug, Serialize)] -pub struct OcrLine<'a> { +pub struct OcrLine { #[serde(skip_serializing)] - lib: &'a Library, - #[serde(skip_serializing)] - line_handle: i64, + line_handle: *mut c_void, pub text: String, pub bounding_box: BoundingBox, pub words: Option>, } -impl<'a> OcrLine<'a> { +impl OcrLine { pub(crate) fn new( - lib: &'a Library, - line_handle: i64, + line_handle: *mut c_void, word_level_detail: bool, ) -> Result { - load_symbol!(lib, get_ocr_line_content, GetOcrLineContent); - load_symbol!(lib, get_ocr_line_bounding_box, GetOcrLineBoundingBox); - - let mut line_content: i64 = 0; + let mut line_content: *const c_char = ptr::null(); check_ocr_call!( - unsafe { get_ocr_line_content(line_handle, &mut line_content) }, + unsafe { GetOcrLineContent(line_handle, &mut line_content) }, "Failed to get line content" ); - let line_content_cstr = unsafe { CStr::from_ptr(line_content as *const c_char) }; + let line_content_cstr = unsafe { CStr::from_ptr(line_content) }; let line_content_str = line_content_cstr.to_string_lossy().to_string(); - let mut bounding_box_ptr: *const RawBBox = std::ptr::null(); + let mut bounding_box_ptr: *const RawBBox = ptr::null(); check_ocr_call!( - unsafe { get_ocr_line_bounding_box(line_handle, &mut bounding_box_ptr) }, + unsafe { GetOcrLineBoundingBox(line_handle, &mut bounding_box_ptr) }, "Failed to get line bounding box" ); @@ -56,12 +50,11 @@ impl<'a> OcrLine<'a> { }); } - let raw_bbox = unsafe { std::ptr::read(bounding_box_ptr) }; + let raw_bbox = unsafe { ptr::read(bounding_box_ptr) }; let bounding_box = BoundingBox::new(raw_bbox); if !word_level_detail { return Ok(Self { - lib, line_handle, text: line_content_str, bounding_box, @@ -69,29 +62,25 @@ impl<'a> OcrLine<'a> { }); } - load_symbol!(lib, get_ocr_line_word_count, GetOcrLineWordCount); - load_symbol!(lib, get_ocr_word, GetOcrWord); - let mut word_count: i64 = 0; check_ocr_call!( - unsafe { get_ocr_line_word_count(line_handle, &mut word_count) }, + unsafe { GetOcrLineWordCount(line_handle, &mut word_count) }, "Failed to get word count" ); let mut words = Vec::with_capacity(word_count as usize); for i in 0..word_count { - let mut word: i64 = 0; + let mut word: *mut c_void = ptr::null_mut(); check_ocr_call!( - unsafe { get_ocr_word(line_handle, i, &mut word) }, + unsafe { GetOcrWord(line_handle, i, &mut word) }, "Failed to get word" ); - let ocr_word = OcrWord::new(lib, word)?; // Corrected: OcrWord::new instead of Word::new + let ocr_word = OcrWord::new(word)?; words.push(ocr_word); } Ok(Self { - lib, line_handle, text: line_content_str, bounding_box, @@ -107,17 +96,13 @@ impl<'a> OcrLine<'a> { /// - 1.0: Printed /// - Returns an error if the OCR API call fails. pub fn get_line_style(&self) -> Result<(bool, f32), OneOcrError> { - load_symbol!(self.lib, get_ocr_line_style_fn, GetOcrLineStyle); - // style: 0 = Handwritten, 1 = Printed let mut style: i32 = 0; // handwritten_confidence: 0.0 = Handwritten, 1.0 = Printed let mut handwritten_confidence: f32 = 0.0; check_ocr_call!( - unsafe { - get_ocr_line_style_fn(self.line_handle, &mut style, &mut handwritten_confidence) - }, + unsafe { GetOcrLineStyle(self.line_handle, &mut style, &mut handwritten_confidence) }, "Failed to get OCR line style" ); diff --git a/src/ocr_result.rs b/src/ocr_result.rs index feec681..c7ff961 100644 --- a/src/ocr_result.rs +++ b/src/ocr_result.rs @@ -1,58 +1,51 @@ use crate::errors::OneOcrError; use crate::ocr_line::OcrLine; -use libloading::Library; use serde::Serialize; +use std::os::raw::c_void; +use std::ptr; // FFI types use crate::ffi::{GetImageAngle, GetOcrLine, GetOcrLineCount, ReleaseOcrResult}; // Macros -use crate::{check_ocr_call, load_symbol, release_ocr_resource}; +use crate::check_ocr_call; /// The `OcrResult` struct represents the result of an OCR operation. /// It contains the recognized text lines, their bounding boxes, and the image angle. #[derive(Debug, Serialize)] -pub struct OcrResult<'a> { +pub struct OcrResult { #[serde(skip_serializing)] - lib: &'a Library, - #[serde(skip_serializing)] - result_handle: i64, - pub lines: Vec>, + result_handle: *mut c_void, + pub lines: Vec, pub image_angle: f32, } -impl<'a> OcrResult<'a> { +impl OcrResult { pub(crate) fn new( - lib: &'a Library, - result_handle: i64, + result_handle: *mut c_void, word_level_detail: bool, ) -> Result { - load_symbol!(lib, get_ocr_line_count, GetOcrLineCount); - load_symbol!(lib, get_ocr_line, GetOcrLine); - load_symbol!(lib, get_image_angle, GetImageAngle); - let mut line_count: i64 = 0; check_ocr_call!( - unsafe { get_ocr_line_count(result_handle, &mut line_count) }, + unsafe { GetOcrLineCount(result_handle, &mut line_count) }, "Failed to get line count" ); let mut lines = Vec::with_capacity(line_count as usize); for i in 0..line_count { - let mut line: i64 = 0; + let mut line: *mut c_void = ptr::null_mut(); check_ocr_call!( - unsafe { get_ocr_line(result_handle, i, &mut line) }, + unsafe { GetOcrLine(result_handle, i, &mut line) }, "Failed to get line" ); - let ocr_line = OcrLine::new(lib, line, word_level_detail)?; + let ocr_line = OcrLine::new(line, word_level_detail)?; lines.push(ocr_line); } let mut angle: f32 = 0.0; check_ocr_call!( - unsafe { get_image_angle(result_handle, &mut angle) }, + unsafe { GetImageAngle(result_handle, &mut angle) }, "Failed to get image angle" ); Ok(Self { - lib, result_handle, lines, image_angle: angle, @@ -60,8 +53,8 @@ impl<'a> OcrResult<'a> { } } -impl Drop for OcrResult<'_> { +impl Drop for OcrResult { fn drop(&mut self) { - release_ocr_resource!(self.lib, ReleaseOcrResult, self.result_handle); + unsafe { ReleaseOcrResult(self.result_handle) }; } } diff --git a/src/ocr_word.rs b/src/ocr_word.rs index b5eb181..2ec7636 100644 --- a/src/ocr_word.rs +++ b/src/ocr_word.rs @@ -1,13 +1,13 @@ use crate::bounding_box::BoundingBox; use crate::errors::OneOcrError; -use libloading::Library; use serde::Serialize; -use std::ffi::{CStr, c_char}; +use std::ffi::{CStr, c_char, c_void}; +use std::ptr; // FFI types used by this module use crate::ffi::{GetOcrWordBoundingBox, GetOcrWordConfidence, GetOcrWordContent, RawBBox}; // Macros -use crate::{check_ocr_call, load_symbol}; +use crate::check_ocr_call; /// The `OcrWord` struct represents a word recognized by the OCR engine. /// It contains the recognized word, its confidence score, and its bounding box. @@ -19,22 +19,18 @@ pub struct OcrWord { } impl OcrWord { - pub(crate) fn new(lib: &Library, word_handle: i64) -> Result { - load_symbol!(lib, get_ocr_word_content, GetOcrWordContent); - load_symbol!(lib, get_ocr_word_bounding_box, GetOcrWordBoundingBox); - load_symbol!(lib, get_ocr_word_confidence, GetOcrWordConfidence); - - let mut word_content: i64 = 0; + pub(crate) fn new(word_handle: *mut c_void) -> Result { + let mut word_content: *const c_char = ptr::null(); check_ocr_call!( - unsafe { get_ocr_word_content(word_handle, &mut word_content) }, + unsafe { GetOcrWordContent(word_handle, &mut word_content) }, "Failed to get word content" ); - let word_content_cstr = unsafe { CStr::from_ptr(word_content as *const c_char) }; + let word_content_cstr = unsafe { CStr::from_ptr(word_content) }; let word_content_str = word_content_cstr.to_string_lossy().to_string(); - let mut bounding_box_ptr: *const RawBBox = std::ptr::null(); + let mut bounding_box_ptr: *const RawBBox = ptr::null(); check_ocr_call!( - unsafe { get_ocr_word_bounding_box(word_handle, &mut bounding_box_ptr) }, + unsafe { GetOcrWordBoundingBox(word_handle, &mut bounding_box_ptr) }, "Failed to get word bounding box" ); @@ -45,12 +41,12 @@ impl OcrWord { }); } - let raw_bbox = unsafe { std::ptr::read(bounding_box_ptr) }; + let raw_bbox = unsafe { ptr::read(bounding_box_ptr) }; let bounding_box = BoundingBox::new(raw_bbox); let mut confidence: f32 = 0.0; check_ocr_call!( - unsafe { get_ocr_word_confidence(word_handle, &mut confidence) }, + unsafe { GetOcrWordConfidence(word_handle, &mut confidence) }, "Failed to get word confidence" ); From 71835a78faec2c18a324a6c8bfbff1851ae11428 Mon Sep 17 00:00:00 2001 From: Fu Wang Date: Mon, 23 Jun 2025 22:48:28 +0800 Subject: [PATCH 2/4] Update src/ocr_result.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ocr_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ocr_result.rs b/src/ocr_result.rs index c7ff961..b4334f7 100644 --- a/src/ocr_result.rs +++ b/src/ocr_result.rs @@ -1,7 +1,7 @@ use crate::errors::OneOcrError; use crate::ocr_line::OcrLine; use serde::Serialize; -use std::os::raw::c_void; +use std::ffi::c_void; use std::ptr; // FFI types From cfd5f218e8a275fbc55f7d310897774dd32815ff Mon Sep 17 00:00:00 2001 From: Fu Wang Date: Mon, 23 Jun 2025 22:55:54 +0800 Subject: [PATCH 3/4] Refine error messages. --- src/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/errors.rs b/src/errors.rs index eab5aed..8766b63 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -13,7 +13,7 @@ pub enum OneOcrError { #[error("Invalid model decryption key: {0}")] InvalidModelKey(String), - #[error("Failed to run ocr API {result}, result: {message}")] + #[error("Failed to run OCR API (code: {result}): {message}")] OcrApiError { result: i32, message: String }, #[error("Other error: {0}")] From b1edcb8f8027a3b5eebcebe0e7be5e86ca571471 Mon Sep 17 00:00:00 2001 From: Fu Wang Date: Mon, 23 Jun 2025 22:56:25 +0800 Subject: [PATCH 4/4] Delete outdated comments. --- src/ocr_engine.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ocr_engine.rs b/src/ocr_engine.rs index 9e9b4e3..2927f43 100644 --- a/src/ocr_engine.rs +++ b/src/ocr_engine.rs @@ -34,9 +34,6 @@ impl OcrEngine { "Failed to create init options" ); - // The FFI function OcrInitOptionsSetUseModelDelayLoad expects a c_char (i8). - // In C, char can be signed or unsigned by default depending on the compiler/platform. - // Rust's c_char is i8. Assuming 0 is a valid value for false. check_ocr_call!( unsafe { OcrInitOptionsSetUseModelDelayLoad(init_options) }, "Failed to set model delay load"