#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageMode {
None,
FullScreen,
ShowOutline,
ShowAttachments,
}
impl PageMode {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::None => "None",
Self::FullScreen => "FullScreen",
Self::ShowOutline => "ShowOutline",
Self::ShowAttachments => "ShowAttachments",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Result<Self, String> {
match s {
"None" => Ok(Self::None),
"FullScreen" => Ok(Self::FullScreen),
"ShowOutline" => Ok(Self::ShowOutline),
"ShowAttachments" => Ok(Self::ShowAttachments),
_ => Err(format!("未知的窗口模式: {s}")),
}
}
}
impl std::fmt::Display for PageMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageLayout {
OneColumn,
OneColumnContinuous,
TwoPageLeft,
TwoPageRight,
}
impl PageLayout {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::OneColumn => "OneColumn",
Self::OneColumnContinuous => "OneColumnContinuous",
Self::TwoPageLeft => "TwoPageLeft",
Self::TwoPageRight => "TwoPageRight",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Result<Self, String> {
match s {
"OneColumn" => Ok(Self::OneColumn),
"OneColumnContinuous" => Ok(Self::OneColumnContinuous),
"TwoPageLeft" => Ok(Self::TwoPageLeft),
"TwoPageRight" => Ok(Self::TwoPageRight),
_ => Err(format!("未知的页面布局: {s}")),
}
}
}
impl std::fmt::Display for PageLayout {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TabDisplay {
FileName,
DocTitle,
}
impl TabDisplay {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::FileName => "FileName",
Self::DocTitle => "DocTitle",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Result<Self, String> {
match s {
"FileName" => Ok(Self::FileName),
"DocTitle" => Ok(Self::DocTitle),
_ => Err(format!("未知的标题栏显示模式: {s}")),
}
}
}
impl std::fmt::Display for TabDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ZoomMode {
FitPage,
FitWidth,
FitHeight,
}
impl ZoomMode {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::FitPage => "FitPage",
Self::FitWidth => "FitWidth",
Self::FitHeight => "FitHeight",
}
}
}
impl std::fmt::Display for ZoomMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy)]
pub enum ZoomScale {
Mode(ZoomMode),
Value(f64),
}
#[derive(Debug, Clone)]
pub struct CtVPreferences {
pub page_mode: PageMode,
pub page_layout: PageLayout,
pub tab_display: Option<TabDisplay>,
pub hide_toolbar: bool,
pub hide_menubar: bool,
pub hide_window_ui: bool,
pub zoom: Option<ZoomScale>,
}
impl CtVPreferences {
#[must_use]
pub fn new() -> Self {
Self {
page_mode: PageMode::None,
page_layout: PageLayout::OneColumn,
tab_display: None,
hide_toolbar: false,
hide_menubar: false,
hide_window_ui: false,
zoom: None,
}
}
#[must_use]
pub fn page_mode(mut self, mode: PageMode) -> Self {
self.page_mode = mode;
self
}
#[must_use]
pub fn page_layout(mut self, layout: PageLayout) -> Self {
self.page_layout = layout;
self
}
#[must_use]
pub fn tab_display(mut self, display: TabDisplay) -> Self {
self.tab_display = Some(display);
self
}
#[must_use]
pub fn hide_toolbar(mut self, hide: bool) -> Self {
self.hide_toolbar = hide;
self
}
#[must_use]
pub fn hide_menubar(mut self, hide: bool) -> Self {
self.hide_menubar = hide;
self
}
#[must_use]
pub fn hide_window_ui(mut self, hide: bool) -> Self {
self.hide_window_ui = hide;
self
}
#[must_use]
pub fn zoom_mode(mut self, mode: ZoomMode) -> Self {
self.zoom = Some(ZoomScale::Mode(mode));
self
}
#[must_use]
pub fn zoom_value(mut self, value: f64) -> Self {
self.zoom = Some(ZoomScale::Value(value));
self
}
#[must_use]
pub fn to_xml_string(&self) -> String {
use std::fmt::Write;
let mut xml = String::from("<ofd:VPreferences>");
let _ = write!(
xml,
"\n<ofd:PageMode>{}</ofd:PageMode>",
self.page_mode.as_str()
);
let _ = write!(
xml,
"\n<ofd:PageLayout>{}</ofd:PageLayout>",
self.page_layout.as_str()
);
if let Some(ref td) = self.tab_display {
let _ = write!(xml, "\n<ofd:TabDisplay>{}</ofd:TabDisplay>", td.as_str());
}
let _ = write!(
xml,
"\n<ofd:HideToolbar>{}</ofd:HideToolbar>",
self.hide_toolbar
);
let _ = write!(
xml,
"\n<ofd:HideMenubar>{}</ofd:HideMenubar>",
self.hide_menubar
);
let _ = write!(
xml,
"\n<ofd:HideWindowUI>{}</ofd:HideWindowUI>",
self.hide_window_ui
);
match &self.zoom {
Some(ZoomScale::Mode(m)) => {
let _ = write!(xml, "\n<ofd:ZoomMode>{}</ofd:ZoomMode>", m.as_str());
}
Some(ZoomScale::Value(v)) => {
let _ = write!(xml, "\n<ofd:Zoom>{v}</ofd:Zoom>");
}
None => {}
}
xml.push_str("\n</ofd:VPreferences>");
xml
}
}
impl Default for CtVPreferences {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ct_v_preferences_new() {
let vp = CtVPreferences::new();
assert_eq!(vp.page_mode, PageMode::None);
assert_eq!(vp.page_layout, PageLayout::OneColumn);
assert!(vp.tab_display.is_none());
assert!(!vp.hide_toolbar);
assert!(!vp.hide_menubar);
assert!(!vp.hide_window_ui);
assert!(vp.zoom.is_none());
}
#[test]
fn test_ct_v_preferences_builder() {
let vp = CtVPreferences::new()
.page_mode(PageMode::FullScreen)
.page_layout(PageLayout::TwoPageLeft)
.tab_display(TabDisplay::DocTitle)
.hide_toolbar(true)
.hide_menubar(true)
.hide_window_ui(true)
.zoom_mode(ZoomMode::FitWidth);
assert_eq!(vp.page_mode, PageMode::FullScreen);
assert_eq!(vp.page_layout, PageLayout::TwoPageLeft);
assert_eq!(vp.tab_display, Some(TabDisplay::DocTitle));
assert!(vp.hide_toolbar);
assert!(vp.hide_menubar);
assert!(vp.hide_window_ui);
assert!(matches!(vp.zoom, Some(ZoomScale::Mode(ZoomMode::FitWidth))));
}
#[test]
fn test_ct_v_preferences_xml() {
let vp = CtVPreferences::new()
.page_mode(PageMode::ShowOutline)
.zoom_value(150.0);
let xml = vp.to_xml_string();
assert!(xml.contains("<ofd:PageMode>ShowOutline</ofd:PageMode>"));
assert!(xml.contains("<ofd:Zoom>150</ofd:Zoom>"));
assert!(xml.contains("</ofd:VPreferences>"));
}
#[test]
fn test_ct_v_preferences_default() {
let vp = CtVPreferences::default();
assert_eq!(vp.page_mode, PageMode::None);
}
#[test]
fn test_page_mode_from_str() {
assert_eq!(PageMode::from_str("None").unwrap(), PageMode::None);
assert_eq!(
PageMode::from_str("FullScreen").unwrap(),
PageMode::FullScreen
);
assert!(PageMode::from_str("Invalid").is_err());
}
#[test]
fn test_page_layout_display() {
assert_eq!(PageLayout::OneColumn.to_string(), "OneColumn");
assert_eq!(
PageLayout::from_str("TwoPageLeft").unwrap(),
PageLayout::TwoPageLeft
);
}
}