Skip to main content

easyofd_core/doc/
ct_v_preferences.rs

1//! 视图首选项(CT_VPreferences)。
2//!
3//! 对应 Java: org.ofdrw.core.basicStructure.doc.vpreferences.CT_VPreferences
4//!
5//! 本标准支持设置文档视图首选项(VPreferences)节点,
6//! 以达到限定文档初始化视图便于阅读的目的。
7//! GB/T 33190 第 7.5 节 图 10。
8
9/// 窗口模式。
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PageMode {
12    /// 不使用特殊模式(默认值)。
13    None,
14    /// 全屏模式。
15    FullScreen,
16    /// 显示大纲。
17    ShowOutline,
18    /// 显示附件。
19    ShowAttachments,
20}
21
22impl PageMode {
23    /// 获取枚举的字符串表示。
24    #[must_use]
25    pub fn as_str(&self) -> &'static str {
26        match self {
27            Self::None => "None",
28            Self::FullScreen => "FullScreen",
29            Self::ShowOutline => "ShowOutline",
30            Self::ShowAttachments => "ShowAttachments",
31        }
32    }
33
34    /// 从字符串解析。
35    #[allow(clippy::should_implement_trait)]
36    pub fn from_str(s: &str) -> Result<Self, String> {
37        match s {
38            "None" => Ok(Self::None),
39            "FullScreen" => Ok(Self::FullScreen),
40            "ShowOutline" => Ok(Self::ShowOutline),
41            "ShowAttachments" => Ok(Self::ShowAttachments),
42            _ => Err(format!("未知的窗口模式: {s}")),
43        }
44    }
45}
46
47impl std::fmt::Display for PageMode {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.write_str(self.as_str())
50    }
51}
52
53/// 页面布局模式。
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum PageLayout {
56    /// 单页显示(默认值)。
57    OneColumn,
58    /// 单页连续滚动。
59    OneColumnContinuous,
60    /// 双页对开。
61    TwoPageLeft,
62    /// 双页连续。
63    TwoPageRight,
64}
65
66impl PageLayout {
67    /// 获取枚举的字符串表示。
68    #[must_use]
69    pub fn as_str(&self) -> &'static str {
70        match self {
71            Self::OneColumn => "OneColumn",
72            Self::OneColumnContinuous => "OneColumnContinuous",
73            Self::TwoPageLeft => "TwoPageLeft",
74            Self::TwoPageRight => "TwoPageRight",
75        }
76    }
77
78    /// 从字符串解析。
79    #[allow(clippy::should_implement_trait)]
80    pub fn from_str(s: &str) -> Result<Self, String> {
81        match s {
82            "OneColumn" => Ok(Self::OneColumn),
83            "OneColumnContinuous" => Ok(Self::OneColumnContinuous),
84            "TwoPageLeft" => Ok(Self::TwoPageLeft),
85            "TwoPageRight" => Ok(Self::TwoPageRight),
86            _ => Err(format!("未知的页面布局: {s}")),
87        }
88    }
89}
90
91impl std::fmt::Display for PageLayout {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.write_str(self.as_str())
94    }
95}
96
97/// 标题栏显示模式。
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum TabDisplay {
100    /// 显示文件名(默认值)。
101    FileName,
102    /// 显示文档标题。
103    DocTitle,
104}
105
106impl TabDisplay {
107    /// 获取枚举的字符串表示。
108    #[must_use]
109    pub fn as_str(&self) -> &'static str {
110        match self {
111            Self::FileName => "FileName",
112            Self::DocTitle => "DocTitle",
113        }
114    }
115
116    /// 从字符串解析。
117    #[allow(clippy::should_implement_trait)]
118    pub fn from_str(s: &str) -> Result<Self, String> {
119        match s {
120            "FileName" => Ok(Self::FileName),
121            "DocTitle" => Ok(Self::DocTitle),
122            _ => Err(format!("未知的标题栏显示模式: {s}")),
123        }
124    }
125}
126
127impl std::fmt::Display for TabDisplay {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        f.write_str(self.as_str())
130    }
131}
132
133/// 缩放模式。
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum ZoomMode {
136    /// 适应页面。
137    FitPage,
138    /// 适应宽度。
139    FitWidth,
140    /// 适应高度。
141    FitHeight,
142}
143
144impl ZoomMode {
145    /// 获取枚举的字符串表示。
146    #[must_use]
147    pub fn as_str(&self) -> &'static str {
148        match self {
149            Self::FitPage => "FitPage",
150            Self::FitWidth => "FitWidth",
151            Self::FitHeight => "FitHeight",
152        }
153    }
154}
155
156impl std::fmt::Display for ZoomMode {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        f.write_str(self.as_str())
159    }
160}
161
162/// 缩放设置(模式或具体数值)。
163#[derive(Debug, Clone, Copy)]
164pub enum ZoomScale {
165    /// 按模式缩放。
166    Mode(ZoomMode),
167    /// 按具体比例缩放(百分比,如 100.0 表示 100%)。
168    Value(f64),
169}
170
171/// 视图首选项。
172///
173/// 定义文档初始化视图的窗口模式、页面布局、
174/// 工具栏可见性、缩放等设置。
175#[derive(Debug, Clone)]
176pub struct CtVPreferences {
177    /// 窗口模式(可选),默认 None。
178    pub page_mode: PageMode,
179    /// 页面布局模式(可选),默认 OneColumn。
180    pub page_layout: PageLayout,
181    /// 标题栏显示模式(可选),默认 FileName。
182    pub tab_display: Option<TabDisplay>,
183    /// 是否隐藏工具栏(可选),默认 false。
184    pub hide_toolbar: bool,
185    /// 是否隐藏菜单栏(可选),默认 false。
186    pub hide_menubar: bool,
187    /// 是否隐藏主窗口之外的其他窗口组件(可选),默认 false。
188    pub hide_window_ui: bool,
189    /// 缩放设置(可选)。
190    pub zoom: Option<ZoomScale>,
191}
192
193impl CtVPreferences {
194    /// 创建默认的视图首选项。
195    #[must_use]
196    pub fn new() -> Self {
197        Self {
198            page_mode: PageMode::None,
199            page_layout: PageLayout::OneColumn,
200            tab_display: None,
201            hide_toolbar: false,
202            hide_menubar: false,
203            hide_window_ui: false,
204            zoom: None,
205        }
206    }
207
208    /// 设置窗口模式。
209    #[must_use]
210    pub fn page_mode(mut self, mode: PageMode) -> Self {
211        self.page_mode = mode;
212        self
213    }
214
215    /// 设置页面布局模式。
216    #[must_use]
217    pub fn page_layout(mut self, layout: PageLayout) -> Self {
218        self.page_layout = layout;
219        self
220    }
221
222    /// 设置标题栏显示模式。
223    #[must_use]
224    pub fn tab_display(mut self, display: TabDisplay) -> Self {
225        self.tab_display = Some(display);
226        self
227    }
228
229    /// 设置是否隐藏工具栏。
230    #[must_use]
231    pub fn hide_toolbar(mut self, hide: bool) -> Self {
232        self.hide_toolbar = hide;
233        self
234    }
235
236    /// 设置是否隐藏菜单栏。
237    #[must_use]
238    pub fn hide_menubar(mut self, hide: bool) -> Self {
239        self.hide_menubar = hide;
240        self
241    }
242
243    /// 设置是否隐藏窗口 UI。
244    #[must_use]
245    pub fn hide_window_ui(mut self, hide: bool) -> Self {
246        self.hide_window_ui = hide;
247        self
248    }
249
250    /// 设置缩放模式。
251    #[must_use]
252    pub fn zoom_mode(mut self, mode: ZoomMode) -> Self {
253        self.zoom = Some(ZoomScale::Mode(mode));
254        self
255    }
256
257    /// 设置缩放比例(百分比)。
258    #[must_use]
259    pub fn zoom_value(mut self, value: f64) -> Self {
260        self.zoom = Some(ZoomScale::Value(value));
261        self
262    }
263
264    /// 序列化为 XML 字符串。
265    #[must_use]
266    pub fn to_xml_string(&self) -> String {
267        use std::fmt::Write;
268
269        let mut xml = String::from("<ofd:VPreferences>");
270
271        let _ = write!(
272            xml,
273            "\n<ofd:PageMode>{}</ofd:PageMode>",
274            self.page_mode.as_str()
275        );
276        let _ = write!(
277            xml,
278            "\n<ofd:PageLayout>{}</ofd:PageLayout>",
279            self.page_layout.as_str()
280        );
281
282        if let Some(ref td) = self.tab_display {
283            let _ = write!(xml, "\n<ofd:TabDisplay>{}</ofd:TabDisplay>", td.as_str());
284        }
285
286        let _ = write!(
287            xml,
288            "\n<ofd:HideToolbar>{}</ofd:HideToolbar>",
289            self.hide_toolbar
290        );
291        let _ = write!(
292            xml,
293            "\n<ofd:HideMenubar>{}</ofd:HideMenubar>",
294            self.hide_menubar
295        );
296        let _ = write!(
297            xml,
298            "\n<ofd:HideWindowUI>{}</ofd:HideWindowUI>",
299            self.hide_window_ui
300        );
301
302        match &self.zoom {
303            Some(ZoomScale::Mode(m)) => {
304                let _ = write!(xml, "\n<ofd:ZoomMode>{}</ofd:ZoomMode>", m.as_str());
305            }
306            Some(ZoomScale::Value(v)) => {
307                let _ = write!(xml, "\n<ofd:Zoom>{v}</ofd:Zoom>");
308            }
309            None => {}
310        }
311
312        xml.push_str("\n</ofd:VPreferences>");
313        xml
314    }
315}
316
317impl Default for CtVPreferences {
318    fn default() -> Self {
319        Self::new()
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    #[test]
328    fn test_ct_v_preferences_new() {
329        let vp = CtVPreferences::new();
330        assert_eq!(vp.page_mode, PageMode::None);
331        assert_eq!(vp.page_layout, PageLayout::OneColumn);
332        assert!(vp.tab_display.is_none());
333        assert!(!vp.hide_toolbar);
334        assert!(!vp.hide_menubar);
335        assert!(!vp.hide_window_ui);
336        assert!(vp.zoom.is_none());
337    }
338
339    #[test]
340    fn test_ct_v_preferences_builder() {
341        let vp = CtVPreferences::new()
342            .page_mode(PageMode::FullScreen)
343            .page_layout(PageLayout::TwoPageLeft)
344            .tab_display(TabDisplay::DocTitle)
345            .hide_toolbar(true)
346            .hide_menubar(true)
347            .hide_window_ui(true)
348            .zoom_mode(ZoomMode::FitWidth);
349        assert_eq!(vp.page_mode, PageMode::FullScreen);
350        assert_eq!(vp.page_layout, PageLayout::TwoPageLeft);
351        assert_eq!(vp.tab_display, Some(TabDisplay::DocTitle));
352        assert!(vp.hide_toolbar);
353        assert!(vp.hide_menubar);
354        assert!(vp.hide_window_ui);
355        assert!(matches!(vp.zoom, Some(ZoomScale::Mode(ZoomMode::FitWidth))));
356    }
357
358    #[test]
359    fn test_ct_v_preferences_xml() {
360        let vp = CtVPreferences::new()
361            .page_mode(PageMode::ShowOutline)
362            .zoom_value(150.0);
363        let xml = vp.to_xml_string();
364        assert!(xml.contains("<ofd:PageMode>ShowOutline</ofd:PageMode>"));
365        assert!(xml.contains("<ofd:Zoom>150</ofd:Zoom>"));
366        assert!(xml.contains("</ofd:VPreferences>"));
367    }
368
369    #[test]
370    fn test_ct_v_preferences_default() {
371        let vp = CtVPreferences::default();
372        assert_eq!(vp.page_mode, PageMode::None);
373    }
374
375    #[test]
376    fn test_page_mode_from_str() {
377        assert_eq!(PageMode::from_str("None").unwrap(), PageMode::None);
378        assert_eq!(
379            PageMode::from_str("FullScreen").unwrap(),
380            PageMode::FullScreen
381        );
382        assert!(PageMode::from_str("Invalid").is_err());
383    }
384
385    #[test]
386    fn test_page_layout_display() {
387        assert_eq!(PageLayout::OneColumn.to_string(), "OneColumn");
388        assert_eq!(
389            PageLayout::from_str("TwoPageLeft").unwrap(),
390            PageLayout::TwoPageLeft
391        );
392    }
393}