easyofd_core/text/
text_code.rs1#[derive(Debug, Clone)]
9pub struct TextCode {
10 pub content: String,
12 pub x: Option<f64>,
14 pub y: Option<f64>,
16 pub delta_x: Vec<f64>,
18 pub delta_y: Vec<f64>,
20}
21
22impl TextCode {
23 #[must_use]
25 pub fn new() -> Self {
26 Self {
27 content: String::new(),
28 x: None,
29 y: None,
30 delta_x: Vec::new(),
31 delta_y: Vec::new(),
32 }
33 }
34
35 #[must_use]
37 pub fn with_content(content: impl Into<String>) -> Self {
38 Self {
39 content: content.into(),
40 ..Self::new()
41 }
42 }
43
44 #[must_use]
46 pub fn content(mut self, content: impl Into<String>) -> Self {
47 self.content = content.into();
48 self
49 }
50
51 #[must_use]
53 pub fn coordinate(mut self, x: f64, y: f64) -> Self {
54 self.x = Some(x);
55 self.y = Some(y);
56 self
57 }
58
59 #[must_use]
61 pub fn x(mut self, x: f64) -> Self {
62 self.x = Some(x);
63 self
64 }
65
66 #[must_use]
68 pub fn y(mut self, y: f64) -> Self {
69 self.y = Some(y);
70 self
71 }
72
73 #[must_use]
75 pub fn delta_x(mut self, deltas: Vec<f64>) -> Self {
76 self.delta_x = deltas;
77 self
78 }
79
80 #[must_use]
82 pub fn delta_y(mut self, deltas: Vec<f64>) -> Self {
83 self.delta_y = deltas;
84 self
85 }
86
87 #[must_use]
89 pub fn get_content(&self) -> &str {
90 &self.content
91 }
92
93 #[must_use]
95 pub fn get_x(&self) -> Option<f64> {
96 self.x
97 }
98
99 #[must_use]
101 pub fn get_y(&self) -> Option<f64> {
102 self.y
103 }
104
105 #[must_use]
107 pub fn to_xml_string(&self) -> String {
108 use std::fmt::Write;
109 let mut xml = String::from("<ofd:TextCode");
110 if let Some(x) = self.x {
111 write!(xml, " X=\"{x}\"").expect("写入内存缓冲区不会失败");
112 }
113 if let Some(y) = self.y {
114 write!(xml, " Y=\"{y}\"").expect("写入内存缓冲区不会失败");
115 }
116 if !self.delta_x.is_empty() {
117 xml.push_str(" DeltaX=\"");
118 for (i, d) in self.delta_x.iter().enumerate() {
119 if i > 0 {
120 xml.push(' ');
121 }
122 write!(xml, "{d}").expect("写入内存缓冲区不会失败");
123 }
124 xml.push('"');
125 }
126 if !self.delta_y.is_empty() {
127 xml.push_str(" DeltaY=\"");
128 for (i, d) in self.delta_y.iter().enumerate() {
129 if i > 0 {
130 xml.push(' ');
131 }
132 write!(xml, "{d}").expect("写入内存缓冲区不会失败");
133 }
134 xml.push('"');
135 }
136 xml.push('>');
137 xml.push_str(&self.content);
138 xml.push_str("</ofd:TextCode>");
139 xml
140 }
141}
142
143impl Default for TextCode {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_text_code_new() {
155 let tc = TextCode::new();
156 assert!(tc.content.is_empty());
157 assert!(tc.x.is_none());
158 assert!(tc.y.is_none());
159 assert!(tc.delta_x.is_empty());
160 assert!(tc.delta_y.is_empty());
161 }
162
163 #[test]
164 fn test_text_code_with_content() {
165 let tc = TextCode::with_content("Hello");
166 assert_eq!(tc.get_content(), "Hello");
167 assert!(tc.x.is_none());
168 }
169
170 #[test]
171 fn test_text_code_builder() {
172 let tc = TextCode::new()
173 .content("World")
174 .coordinate(10.0, 20.0)
175 .delta_x(vec![6.0, 6.0, 6.0]);
176 assert_eq!(tc.get_content(), "World");
177 assert!((tc.get_x().unwrap() - 10.0).abs() < f64::EPSILON);
178 assert!((tc.get_y().unwrap() - 20.0).abs() < f64::EPSILON);
179 assert_eq!(tc.delta_x.len(), 3);
180 }
181
182 #[test]
183 fn test_text_code_to_xml_minimal() {
184 let tc = TextCode::with_content("A");
185 let xml = tc.to_xml_string();
186 assert!(xml.contains("<ofd:TextCode"));
187 assert!(xml.contains(">A</ofd:TextCode>"));
188 }
189
190 #[test]
191 fn test_text_code_to_xml_with_position() {
192 let tc = TextCode::with_content("Hi").coordinate(5.0, 10.0);
193 let xml = tc.to_xml_string();
194 assert!(xml.contains("X=\"5\""));
195 assert!(xml.contains("Y=\"10\""));
196 assert!(xml.contains(">Hi</ofd:TextCode>"));
197 }
198
199 #[test]
200 fn test_text_code_to_xml_with_deltas() {
201 let tc = TextCode::with_content("ABC")
202 .x(0.0)
203 .delta_x(vec![6.0, 6.0, 6.0])
204 .delta_y(vec![0.0, 0.0]);
205 let xml = tc.to_xml_string();
206 assert!(xml.contains("DeltaX=\"6 6 6\""));
207 assert!(xml.contains("DeltaY=\"0 0\""));
208 }
209
210 #[test]
211 fn test_text_code_clone_debug() {
212 let tc = TextCode::with_content("x");
213 let tc2 = tc.clone();
214 assert_eq!(tc2.get_content(), "x");
215 assert!(format!("{tc:?}").contains("TextCode"));
216 }
217}