Skip to main content

easyofd_core/action/
ct_dest.rs

1//! 目标位置定义。
2//!
3//! 对应 Java: org.ofdrw.core.action.CT_Dest
4
5use std::fmt::Write;
6
7use super::DestType;
8
9/// 目标位置。
10///
11/// 定义页面跳转的目标位置参数,包括目标页码、显示类型和坐标。
12///
13/// 对应 Java: org.ofdrw.core.action.CT_Dest
14#[derive(Debug, Clone, PartialEq)]
15pub struct CTDest {
16    /// 目标页码。
17    ///
18    /// 对应 Java: CT_Dest.page (int)
19    pub page: u32,
20
21    /// 目标显示类型。
22    ///
23    /// 对应 Java: CT_Dest.type (DestType)
24    pub dest_type: DestType,
25
26    /// 左上角 X 坐标(单位 mm)。
27    ///
28    /// 仅在 `dest_type` 为 `XYZ`、`FitH`、`FitBH` 时有效。
29    /// 对应 Java: CT_Dest.left (Double)
30    pub left: Option<f64>,
31
32    /// 左上角 Y 坐标(单位 mm)。
33    ///
34    /// 仅在 `dest_type` 为 `XYZ`、`FitV`、`FitBV` 时有效。
35    /// 对应 Java: CT_Dest.top (Double)
36    pub top: Option<f64>,
37
38    /// 缩放级别。
39    ///
40    /// 仅在 `dest_type` 为 `XYZ` 时有效。
41    /// 对应 Java: CT_Dest.zoom (Double)
42    pub zoom: Option<f64>,
43}
44
45impl CTDest {
46    /// 创建一个新的目标位置。
47    ///
48    /// 对应 Java: new CT_Dest(int page)
49    #[must_use]
50    pub fn new(page: u32) -> Self {
51        Self {
52            page,
53            dest_type: DestType::Fit,
54            left: None,
55            top: None,
56            zoom: None,
57        }
58    }
59
60    /// 设置目标显示类型。
61    ///
62    /// 对应 Java: CT_Dest.setType(DestType)
63    #[must_use]
64    pub fn dest_type(mut self, dest_type: DestType) -> Self {
65        self.dest_type = dest_type;
66        self
67    }
68
69    /// 设置左上角 X 坐标。
70    ///
71    /// 对应 Java: CT_Dest.setLeft(Double)
72    #[must_use]
73    pub fn left(mut self, left: f64) -> Self {
74        self.left = Some(left);
75        self
76    }
77
78    /// 设置左上角 Y 坐标。
79    ///
80    /// 对应 Java: CT_Dest.setTop(Double)
81    #[must_use]
82    pub fn top(mut self, top: f64) -> Self {
83        self.top = Some(top);
84        self
85    }
86
87    /// 设置缩放级别。
88    ///
89    /// 对应 Java: CT_Dest.setZoom(Double)
90    #[must_use]
91    pub fn zoom(mut self, zoom: f64) -> Self {
92        self.zoom = Some(zoom);
93        self
94    }
95
96    /// 序列化为 OFD XML 字符串。
97    ///
98    /// 输出标准 OFD XML 格式。
99    #[must_use]
100    pub fn to_xml_string(&self) -> String {
101        let mut attrs = format!("PageID=\"{}\" Type=\"{}\"", self.page, self.dest_type);
102
103        if let Some(left) = self.left {
104            let _ = write!(attrs, " Left=\"{left}\"");
105        }
106        if let Some(top) = self.top {
107            let _ = write!(attrs, " Top=\"{top}\"");
108        }
109        if let Some(zoom) = self.zoom {
110            let _ = write!(attrs, " Zoom=\"{zoom}\"");
111        }
112
113        format!("<ofd:Dest {attrs}/>")
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_ct_dest_new() {
123        let dest = CTDest::new(3);
124        assert_eq!(dest.page, 3);
125        assert_eq!(dest.dest_type, DestType::Fit);
126        assert!(dest.left.is_none());
127        assert!(dest.top.is_none());
128        assert!(dest.zoom.is_none());
129    }
130
131    #[test]
132    fn test_ct_dest_builder() {
133        let dest = CTDest::new(1)
134            .dest_type(DestType::XYZ)
135            .left(10.0)
136            .top(20.0)
137            .zoom(1.5);
138        assert_eq!(dest.page, 1);
139        assert_eq!(dest.dest_type, DestType::XYZ);
140        assert_eq!(dest.left, Some(10.0));
141        assert_eq!(dest.top, Some(20.0));
142        assert_eq!(dest.zoom, Some(1.5));
143    }
144
145    #[test]
146    fn test_ct_dest_to_xml_fit() {
147        let dest = CTDest::new(5);
148        let xml = dest.to_xml_string();
149        assert!(xml.contains("PageID=\"5\""));
150        assert!(xml.contains("Type=\"Fit\""));
151        assert!(!xml.contains("Left"));
152    }
153
154    #[test]
155    fn test_ct_dest_to_xml_xyz() {
156        let dest = CTDest::new(2)
157            .dest_type(DestType::XYZ)
158            .left(10.5)
159            .top(20.5)
160            .zoom(2.0);
161        let xml = dest.to_xml_string();
162        assert!(xml.contains("PageID=\"2\""));
163        assert!(xml.contains("Type=\"XYZ\""));
164        assert!(xml.contains("Left=\"10.5\""));
165        assert!(xml.contains("Top=\"20.5\""));
166        assert!(xml.contains("Zoom=\"2\""));
167    }
168
169    #[test]
170    fn test_ct_dest_clone_debug() {
171        let dest = CTDest::new(1).dest_type(DestType::FitH).left(5.0);
172        let dest2 = dest.clone();
173        assert_eq!(dest2.page, 1);
174        assert_eq!(dest2.dest_type, DestType::FitH);
175        let dbg = format!("{dest:?}");
176        assert!(dbg.contains("CTDest"));
177    }
178}