Skip to main content

easyofd_reader/
ofd_reader_facade.rs

1//! OFD 解析器门面。
2//!
3//! 对应 Java: org.ofdrw.reader.OFDReader
4//!
5//! Java 版 `OFDReader` 是一个功能丰富的门面类,管理临时目录解压、
6//! 资源定位器、资源管理器等。Rust 版已有的 `OfdReader` 实现了核心
7//! 解析功能,此门面提供额外的资源定位和管理能力。
8
9use std::io::{Read, Seek};
10
11use easyofd_core::{OfdPage, OfdResult};
12
13use crate::OfdReader;
14use crate::page_info::PageInfo;
15use crate::read_options::ReadOptions;
16use crate::resource_locator::ResourceLocator;
17use crate::resource_manage::ResourceManage;
18
19/// OFD 解析器门面,提供完整的 OFD 文档读取能力。
20///
21/// 对应 Java: `org.ofdrw.reader.OFDReader`
22///
23/// 封装 [`OfdReader`] 的核心解析能力,额外提供:
24/// - [`ResourceLocator`] 资源路径定位
25/// - [`ResourceManage`] 资源 ID 索引
26/// - [`PageInfo`] 页面元数据访问
27///
28/// # 用法
29///
30/// ```rust,no_run
31/// use easyofd_reader::OfdReaderFacade;
32///
33/// # fn example() -> Result<(), easyofd_core::OfdError> {
34/// let facade = OfdReaderFacade::open("document.ofd")?;
35/// println!("页数: {}", facade.page_count());
36/// # Ok(())
37/// # }
38/// ```
39pub struct OfdReaderFacade {
40    /// 内部 OFD 读取器。
41    reader: OfdReader,
42    /// 资源定位器。
43    locator: ResourceLocator,
44    /// 资源管理器(懒加载)。
45    resource_manage: Option<ResourceManage>,
46}
47
48impl std::fmt::Debug for OfdReaderFacade {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        f.debug_struct("OfdReaderFacade")
51            .field("page_count", &self.reader.page_count())
52            .field("locator", &self.locator)
53            .field("resource_manage", &self.resource_manage.is_some())
54            .finish()
55    }
56}
57
58impl OfdReaderFacade {
59    /// 从文件路径打开 OFD 文档。
60    ///
61    /// 对应 Java: `OFDReader(Path ofdFile)`
62    ///
63    /// # 错误
64    ///
65    /// 文件无法读取或 OFD 数据无效时返回错误。
66    pub fn open(path: impl AsRef<std::path::Path>) -> OfdResult<Self> {
67        let reader = OfdReader::open(path)?;
68        Ok(Self::from_reader(reader))
69    }
70
71    /// 从字节数组解析 OFD 文档。
72    ///
73    /// 对应 Java: `OFDReader(InputStream stream)`
74    ///
75    /// # 错误
76    ///
77    /// 数据无效时返回错误。
78    pub fn from_bytes(data: &[u8]) -> OfdResult<Self> {
79        let reader = OfdReader::from_bytes(data)?;
80        Ok(Self::from_reader(reader))
81    }
82
83    /// 从已有的 `OfdReader` 创建门面。
84    #[must_use]
85    pub fn from_reader(reader: OfdReader) -> Self {
86        Self {
87            reader,
88            locator: ResourceLocator::new(),
89            resource_manage: None,
90        }
91    }
92
93    /// 从 `Read + Seek` 输入创建门面。
94    ///
95    /// # 错误
96    ///
97    /// ZIP 包或 XML 无效时返回错误。
98    pub fn from_seek<R: Read + Seek>(source: R, options: ReadOptions) -> OfdResult<Self> {
99        let reader = OfdReader::from_seek(source, options)?;
100        Ok(Self::from_reader(reader))
101    }
102
103    /// 获取内部的 `OfdReader` 引用。
104    #[must_use]
105    pub fn reader(&self) -> &OfdReader {
106        &self.reader
107    }
108
109    /// 获取资源定位器。
110    ///
111    /// 对应 Java: `OFDReader.getResourceLocator()`
112    #[must_use]
113    pub fn resource_locator(&self) -> &ResourceLocator {
114        &self.locator
115    }
116
117    /// 获取可变资源定位器。
118    #[must_use]
119    pub fn resource_locator_mut(&mut self) -> &mut ResourceLocator {
120        &mut self.locator
121    }
122
123    /// 获取文档页数。
124    ///
125    /// 对应 Java: `OFDReader.getNumberOfPages()`
126    #[must_use]
127    pub fn page_count(&self) -> usize {
128        self.reader.page_count()
129    }
130
131    /// 获取所有页面。
132    #[must_use]
133    pub fn pages(&self) -> &[OfdPage] {
134        self.reader.pages()
135    }
136
137    /// 获取指定页面的信息。
138    ///
139    /// 对应 Java: `OFDReader.getPageInfo(int pageNum)`
140    ///
141    /// `page_num` 从 1 开始。
142    #[must_use]
143    pub fn get_page_info(&self, page_num: usize) -> Option<PageInfo> {
144        if page_num == 0 || page_num > self.reader.page_count() {
145            return None;
146        }
147        let page = &self.reader.pages()[page_num - 1];
148        let size = easyofd_core::ST_Box::new(0.0, 0.0, page.width, page.height);
149        let mut info = PageInfo::new(page_num, size);
150        info.page_n = page_num.saturating_sub(1);
151        Some(info)
152    }
153
154    /// 获取所有页面信息列表。
155    ///
156    /// 对应 Java: `OFDReader.getPageList()`
157    #[must_use]
158    pub fn get_page_list(&self) -> Vec<PageInfo> {
159        (1..=self.page_count())
160            .filter_map(|i| self.get_page_info(i))
161            .collect()
162    }
163
164    /// 获取文档元数据。
165    #[must_use]
166    pub fn metadata(&self) -> &easyofd_core::OfdMetadata {
167        self.reader.metadata()
168    }
169
170    /// 提取所有页面的文本。
171    #[must_use]
172    pub fn extract_text(&self) -> Vec<String> {
173        self.reader.extract_text()
174    }
175
176    /// 提取所有文本合并为单个字符串。
177    #[must_use]
178    pub fn extract_all_text(&self) -> String {
179        self.reader.extract_all_text()
180    }
181
182    /// 获取资源管理器。
183    ///
184    /// 对应 Java: `OFDReader.getResMgt()`
185    #[must_use]
186    pub fn resource_manage(&self) -> Option<&ResourceManage> {
187        self.resource_manage.as_ref()
188    }
189
190    /// 设置资源管理器。
191    pub fn set_resource_manage(&mut self, mgr: ResourceManage) {
192        self.resource_manage = Some(mgr);
193    }
194
195    /// 文档是否包含数字签名。
196    ///
197    /// 对应 Java: `OFDReader.hasSignature()`
198    #[must_use]
199    pub fn has_signature(&self) -> bool {
200        self.reader.metadata().signatures_path.is_some()
201    }
202
203    /// 获取原始 ZIP 条目(用于无损 roundtrip)。
204    #[must_use]
205    pub fn raw_entries(&self) -> &[(String, Vec<u8>)] {
206        self.reader.raw_entries()
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use easyofd_core::{OfdPage, TextObject};
214    use easyofd_writer::OfdWriter;
215
216    fn build_test_ofd() -> Vec<u8> {
217        let mut writer = OfdWriter::new();
218        let mut page = OfdPage::new(210.0, 297.0);
219        page.add_text(TextObject::new(10.0, 20.0, "Facade Test"));
220        writer.add_page(page);
221        writer.build().unwrap()
222    }
223
224    #[test]
225    fn test_facade_from_bytes() {
226        let bytes = build_test_ofd();
227        let facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
228        assert_eq!(facade.page_count(), 1);
229        assert!(!facade.has_signature());
230    }
231
232    #[test]
233    fn test_facade_from_reader() {
234        let bytes = build_test_ofd();
235        let reader = OfdReader::from_bytes(&bytes).unwrap();
236        let facade = OfdReaderFacade::from_reader(reader);
237        assert_eq!(facade.page_count(), 1);
238    }
239
240    #[test]
241    fn test_facade_get_page_info() {
242        let bytes = build_test_ofd();
243        let facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
244        let info = facade.get_page_info(1).unwrap();
245        assert_eq!(info.index, 1);
246        assert!((info.width() - 210.0).abs() < f64::EPSILON);
247    }
248
249    #[test]
250    fn test_facade_get_page_info_out_of_range() {
251        let bytes = build_test_ofd();
252        let facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
253        assert!(facade.get_page_info(0).is_none());
254        assert!(facade.get_page_info(2).is_none());
255    }
256
257    #[test]
258    fn test_facade_get_page_list() {
259        let bytes = build_test_ofd();
260        let facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
261        let list = facade.get_page_list();
262        assert_eq!(list.len(), 1);
263    }
264
265    #[test]
266    fn test_facade_extract_text() {
267        let bytes = build_test_ofd();
268        let facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
269        let texts = facade.extract_text();
270        assert!(!texts.is_empty());
271        assert!(texts[0].contains("Facade Test"));
272    }
273
274    #[test]
275    fn test_facade_resource_locator() {
276        let bytes = build_test_ofd();
277        let mut facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
278        facade.resource_locator_mut().cd("/Doc_0").unwrap();
279        assert_eq!(facade.resource_locator().pwd(), "/Doc_0");
280    }
281
282    #[test]
283    fn test_facade_resource_manage() {
284        let bytes = build_test_ofd();
285        let mut facade = OfdReaderFacade::from_bytes(&bytes).unwrap();
286        assert!(facade.resource_manage().is_none());
287        facade.set_resource_manage(ResourceManage::new());
288        assert!(facade.resource_manage().is_some());
289    }
290}