easyofd_core/page_description/
ct_region.rs1use crate::basic_type::ST_Array;
6use crate::xml_element::{XmlElement, XmlElementError, XmlNode};
7
8#[allow(non_camel_case_types)]
12#[derive(Debug, Clone, PartialEq)]
13pub struct CT_Region {
14 path: Vec<RegionPath>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
20pub struct RegionPath {
21 data: String,
23 transform: Option<ST_Array>,
25 draw_param: Option<u64>,
27}
28
29impl CT_Region {
30 pub fn new() -> Self {
32 Self { path: Vec::new() }
33 }
34
35 pub fn add_path(&mut self, path: RegionPath) -> &mut Self {
37 self.path.push(path);
38 self
39 }
40
41 pub fn paths(&self) -> &[RegionPath] {
43 &self.path
44 }
45
46 pub fn to_xml_string(&self) -> String {
48 format!("<Region />")
49 }
50
51 pub fn from_str(s: &str) -> Result<Self, String> {
53 let s = s.trim();
54 if s.is_empty() {
55 return Err("CT_Region 不能为空".to_string());
56 }
57 Ok(Self::new())
58 }
59}
60
61impl RegionPath {
62 pub fn new(data: &str) -> Self {
64 Self {
65 data: data.to_string(),
66 transform: None,
67 draw_param: None,
68 }
69 }
70
71 pub fn data(&self) -> &str {
73 &self.data
74 }
75
76 pub fn set_transform(&mut self, transform: ST_Array) -> &mut Self {
78 self.transform = Some(transform);
79 self
80 }
81
82 pub fn transform(&self) -> Option<&ST_Array> {
84 self.transform.as_ref()
85 }
86
87 pub fn set_draw_param(&mut self, draw_param: u64) -> &mut Self {
89 self.draw_param = Some(draw_param);
90 self
91 }
92
93 pub fn draw_param(&self) -> Option<u64> {
95 self.draw_param
96 }
97}
98
99impl Default for CT_Region {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105impl XmlElement for CT_Region {
106 fn element_name(&self) -> &'static str {
108 "Region"
109 }
110
111 fn attributes(&self) -> Vec<(String, String)> {
112 Vec::new()
113 }
114
115 fn child_nodes(&self) -> Vec<XmlNode> {
116 self.path
117 .iter()
118 .map(|p| {
119 let mut node = XmlNode::element("Path");
120 node.attrs.push(("Data".to_string(), p.data.clone()));
121 if let Some(ref tf) = p.transform {
122 node.attrs
123 .push(("Transform".to_string(), tf.to_xml_string()));
124 }
125 if let Some(dp) = p.draw_param {
126 node.attrs.push(("DrawParam".to_string(), dp.to_string()));
127 }
128 node
129 })
130 .collect()
131 }
132
133 fn from_xml(node: &XmlNode) -> Result<Self, XmlElementError> {
134 let path = node
135 .children_named("Path")
136 .map(|child| {
137 let data = child
138 .get_attr("Data")
139 .ok_or_else(|| XmlElementError("Path 缺少 Data 属性".to_string()))?
140 .to_string();
141 let transform = child
142 .get_attr("Transform")
143 .map(|s| {
144 ST_Array::from_str(s)
145 .map_err(|e| XmlElementError(format!("解析 Path.Transform 失败: {e}")))
146 })
147 .transpose()?;
148 let draw_param = child
149 .get_attr("DrawParam")
150 .map(|s| {
151 s.parse::<u64>()
152 .map_err(|e| XmlElementError(format!("解析 Path.DrawParam 失败: {e}")))
153 })
154 .transpose()?;
155 Ok(RegionPath {
156 data,
157 transform,
158 draw_param,
159 })
160 })
161 .collect::<Result<Vec<_>, XmlElementError>>()?;
162 Ok(Self { path })
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::xml_parse::parse_xml_to_nodes;
170
171 #[test]
172 fn test_basic_creation() {
173 let region = CT_Region::new();
174 assert!(region.paths().is_empty());
175 }
176
177 #[test]
178 fn test_add_path() {
179 let mut region = CT_Region::new();
180 region.add_path(RegionPath::new("M 0 0 L 100 0 L 100 100 Z"));
181 assert_eq!(region.paths().len(), 1);
182 assert_eq!(region.paths()[0].data(), "M 0 0 L 100 0 L 100 100 Z");
183 }
184
185 #[test]
186 fn test_region_path_transform() {
187 let mut path = RegionPath::new("M 0 0 L 100 0");
188 let transform = ST_Array::transform(1.0, 0.0, 0.0, 1.0, 10.0, 20.0);
189 path.set_transform(transform);
190 assert!(path.transform().is_some());
191 }
192
193 #[test]
194 fn test_to_xml_string() {
195 let region = CT_Region::new();
196 let xml = region.to_xml_string();
197 assert!(xml.contains("Region"));
198 }
199
200 #[test]
201 fn test_from_str() {
202 let region = CT_Region::from_str("dummy").unwrap();
203 assert!(region.paths().is_empty());
204 }
205
206 #[test]
207 fn test_from_str_empty() {
208 assert!(CT_Region::from_str("").is_err());
209 }
210
211 #[test]
212 fn test_xml_element_name() {
213 let region = CT_Region::new();
214 assert_eq!(region.element_name(), "Region");
215 }
216
217 #[test]
218 fn test_xml_element_roundtrip_empty() {
219 let region = CT_Region::new();
220 let xml = region.to_xml();
221 assert_eq!(xml, "<Region/>");
222 let node = parse_xml_to_nodes(&xml).unwrap();
223 let region2 = CT_Region::from_xml(&node).unwrap();
224 assert_eq!(region, region2);
225 }
226
227 #[test]
228 fn test_xml_element_roundtrip_with_paths() {
229 let mut region = CT_Region::new();
230 let mut p1 = RegionPath::new("M 0 0 L 100 0 L 100 100 Z");
231 p1.set_draw_param(5);
232 region.add_path(p1);
233 let mut p2 = RegionPath::new("M 0 0 L 50 50");
234 p2.set_transform(ST_Array::transform(1.0, 0.0, 0.0, 1.0, 10.0, 20.0));
235 region.add_path(p2);
236
237 let xml = region.to_xml();
238 assert!(xml.contains("Path"));
239 assert!(xml.contains("Data=\"M 0 0 L 100 0 L 100 100 Z\""));
240 assert!(xml.contains("DrawParam=\"5\""));
241
242 let node = parse_xml_to_nodes(&xml).unwrap();
243 let region2 = CT_Region::from_xml(&node).unwrap();
244 assert_eq!(region, region2);
245 }
246}