Skip to main content

rapiddoc_core/
lib.rs

1//! 不依赖 Python、PDFium 或推理运行库的基础契约。
2
3use serde::{Deserialize, Serialize};
4pub mod document;
5pub mod geometry;
6pub mod ocr;
7pub mod pillow_resize;
8pub mod progress;
9pub mod resources;
10
11/// 解析错误的稳定分类,调用方不应依靠错误文案分支。
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum ErrorCode {
15    InvalidInput,
16    Unsupported,
17    Encrypted,
18    CorruptDocument,
19    ResourceLimit,
20    ModelMissing,
21    DeviceUnavailable,
22    RuntimeUnavailable,
23}
24
25impl ErrorCode {
26    /// 跨语言稳定的错误码,与 serde 名称一致。
27    pub fn as_str(&self) -> &'static str {
28        match self {
29            Self::InvalidInput => "invalid_input",
30            Self::Unsupported => "unsupported",
31            Self::Encrypted => "encrypted",
32            Self::CorruptDocument => "corrupt_document",
33            Self::ResourceLimit => "resource_limit",
34            Self::ModelMissing => "model_missing",
35            Self::DeviceUnavailable => "device_unavailable",
36            Self::RuntimeUnavailable => "runtime_unavailable",
37        }
38    }
39}
40
41/// 不包含裸 FFI 句柄的错误。
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct Error {
44    pub code: ErrorCode,
45    pub message: String,
46}
47
48impl std::fmt::Display for Error {
49    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        write!(formatter, "{:?}: {}", self.code, self.message)
51    }
52}
53impl std::error::Error for Error {}
54
55/// 有限、非反向的矩形;单位由所在页面坐标系声明。
56#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
57#[serde(try_from = "[f64; 4]", into = "[f64; 4]")]
58pub struct Rect([f64; 4]);
59
60impl TryFrom<[f64; 4]> for Rect {
61    type Error = Error;
62
63    /// 校验反向边界及 NaN/Infinity;允许零面积框表示空几何。
64    fn try_from(value: [f64; 4]) -> Result<Self, Error> {
65        if value.iter().any(|x| !x.is_finite()) || value[2] < value[0] || value[3] < value[1] {
66            return Err(Error {
67                code: ErrorCode::InvalidInput,
68                message: "矩形坐标必须有限且有序".into(),
69            });
70        }
71        Ok(Self(value))
72    }
73}
74
75impl From<Rect> for [f64; 4] {
76    fn from(value: Rect) -> Self {
77        value.0
78    }
79}
80
81/// 页范围使用从零开始的原始页码,结束位置不包含在结果中。
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
83pub struct PageRange {
84    pub start: usize,
85    pub end: Option<usize>,
86}
87
88impl PageRange {
89    /// 校验范围,不静默截断超界请求或重排页码。
90    pub fn resolve(&self, page_count: usize) -> Result<std::ops::Range<usize>, Error> {
91        let end = self.end.unwrap_or(page_count);
92        if self.start > end || end > page_count {
93            return Err(Error {
94                code: ErrorCode::InvalidInput,
95                message: "页范围越界".into(),
96            });
97        }
98        Ok(self.start..end)
99    }
100}
101
102fn default_native_chars() -> usize {
103    20_000
104}
105/// 输入资源限制必须在打开原生库之前检查。
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct Limits {
108    pub max_input_bytes: usize,
109    pub max_pages: usize,
110    pub max_chars_per_page: usize,
111    #[serde(default = "default_native_chars")]
112    pub max_native_chars_per_page: usize,
113    pub max_total_chars: usize,
114    pub max_objects_per_page: usize,
115    pub max_path_segments_per_page: usize,
116    pub max_form_depth: usize,
117    pub max_links_per_page: usize,
118}
119
120impl Default for Limits {
121    fn default() -> Self {
122        Self {
123            max_input_bytes: 256 * 1024 * 1024,
124            max_pages: 10_000,
125            max_chars_per_page: 1_000_000,
126            max_native_chars_per_page: 20_000,
127            max_total_chars: 10_000_000,
128            max_objects_per_page: 200_000,
129            max_path_segments_per_page: 2_000_000,
130            max_form_depth: 32,
131            max_links_per_page: 100_000,
132        }
133    }
134}
135
136impl Limits {
137    /// Match Python's document API: no invented input/count/work cutoffs.
138    /// Explicit limits remain available through `Limits::default()` and fields.
139    pub fn pdf_host() -> Self {
140        Self {
141            max_input_bytes: usize::MAX,
142            max_pages: usize::MAX,
143            max_chars_per_page: usize::MAX,
144            max_native_chars_per_page: usize::MAX,
145            max_total_chars: usize::MAX,
146            max_objects_per_page: usize::MAX,
147            max_path_segments_per_page: usize::MAX,
148            max_form_depth: usize::MAX,
149            max_links_per_page: usize::MAX,
150        }
151    }
152    /// 空输入和超限输入明确返回错误,避免误认为成功的空文档。
153    pub fn check_input(&self, bytes: &[u8]) -> Result<(), Error> {
154        if bytes.is_empty() {
155            return Err(Error {
156                code: ErrorCode::InvalidInput,
157                message: "输入为空".into(),
158            });
159        }
160        if bytes.len() > self.max_input_bytes {
161            return Err(Error {
162                code: ErrorCode::ResourceLimit,
163                message: "输入超过字节限制".into(),
164            });
165        }
166        Ok(())
167    }
168}