1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PageMode {
12 None,
14 FullScreen,
16 ShowOutline,
18 ShowAttachments,
20}
21
22impl PageMode {
23 #[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 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum PageLayout {
56 OneColumn,
58 OneColumnContinuous,
60 TwoPageLeft,
62 TwoPageRight,
64}
65
66impl PageLayout {
67 #[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 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum TabDisplay {
100 FileName,
102 DocTitle,
104}
105
106impl TabDisplay {
107 #[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 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum ZoomMode {
136 FitPage,
138 FitWidth,
140 FitHeight,
142}
143
144impl ZoomMode {
145 #[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#[derive(Debug, Clone, Copy)]
164pub enum ZoomScale {
165 Mode(ZoomMode),
167 Value(f64),
169}
170
171#[derive(Debug, Clone)]
176pub struct CtVPreferences {
177 pub page_mode: PageMode,
179 pub page_layout: PageLayout,
181 pub tab_display: Option<TabDisplay>,
183 pub hide_toolbar: bool,
185 pub hide_menubar: bool,
187 pub hide_window_ui: bool,
189 pub zoom: Option<ZoomScale>,
191}
192
193impl CtVPreferences {
194 #[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 #[must_use]
210 pub fn page_mode(mut self, mode: PageMode) -> Self {
211 self.page_mode = mode;
212 self
213 }
214
215 #[must_use]
217 pub fn page_layout(mut self, layout: PageLayout) -> Self {
218 self.page_layout = layout;
219 self
220 }
221
222 #[must_use]
224 pub fn tab_display(mut self, display: TabDisplay) -> Self {
225 self.tab_display = Some(display);
226 self
227 }
228
229 #[must_use]
231 pub fn hide_toolbar(mut self, hide: bool) -> Self {
232 self.hide_toolbar = hide;
233 self
234 }
235
236 #[must_use]
238 pub fn hide_menubar(mut self, hide: bool) -> Self {
239 self.hide_menubar = hide;
240 self
241 }
242
243 #[must_use]
245 pub fn hide_window_ui(mut self, hide: bool) -> Self {
246 self.hide_window_ui = hide;
247 self
248 }
249
250 #[must_use]
252 pub fn zoom_mode(mut self, mode: ZoomMode) -> Self {
253 self.zoom = Some(ZoomScale::Mode(mode));
254 self
255 }
256
257 #[must_use]
259 pub fn zoom_value(mut self, value: f64) -> Self {
260 self.zoom = Some(ZoomScale::Value(value));
261 self
262 }
263
264 #[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}