easyofd_core/page_obj/
ct_page_block.rs1#[allow(non_camel_case_types)]
9#[derive(Debug, Clone)]
10pub struct CT_PageBlock {
11 pub page_blocks: Vec<CT_PageBlock>,
13 pub text_objects: Vec<PageBlockTextObject>,
15 pub path_objects: Vec<PageBlockPathObject>,
17 pub image_objects: Vec<PageBlockImageObject>,
19}
20
21#[derive(Debug, Clone)]
23pub struct PageBlockTextObject {
24 pub id: u32,
26 pub boundary: String,
28 pub content: String,
30 pub font_size: f64,
32}
33
34#[derive(Debug, Clone)]
36pub struct PageBlockPathObject {
37 pub id: u32,
39 pub boundary: String,
41 pub abbreviated_data: String,
43}
44
45#[derive(Debug, Clone)]
47pub struct PageBlockImageObject {
48 pub id: u32,
50 pub boundary: String,
52 pub resource_id: u32,
54}
55
56impl CT_PageBlock {
57 #[must_use]
59 pub fn new() -> Self {
60 Self {
61 page_blocks: Vec::new(),
62 text_objects: Vec::new(),
63 path_objects: Vec::new(),
64 image_objects: Vec::new(),
65 }
66 }
67
68 pub fn add_page_block(&mut self, block: CT_PageBlock) {
70 self.page_blocks.push(block);
71 }
72
73 pub fn add_text_object(&mut self, obj: PageBlockTextObject) {
75 self.text_objects.push(obj);
76 }
77
78 pub fn add_path_object(&mut self, obj: PageBlockPathObject) {
80 self.path_objects.push(obj);
81 }
82
83 pub fn add_image_object(&mut self, obj: PageBlockImageObject) {
85 self.image_objects.push(obj);
86 }
87
88 #[must_use]
90 pub fn get_page_blocks(&self) -> &[CT_PageBlock] {
91 &self.page_blocks
92 }
93
94 #[must_use]
96 pub fn total_count(&self) -> usize {
97 let direct = self.text_objects.len() + self.path_objects.len() + self.image_objects.len();
98 let nested: usize = self.page_blocks.iter().map(|b| b.total_count()).sum();
99 direct + nested
100 }
101
102 #[must_use]
104 pub fn to_xml_string(&self) -> String {
105 use std::fmt::Write;
106 let mut xml = String::from("<ofd:PageBlock>\n");
107 for text_obj in &self.text_objects {
108 let _ = writeln!(
109 xml,
110 " <ofd:TextObject ID=\"{}\" Boundary=\"{}\">{}</ofd:TextObject>",
111 text_obj.id, text_obj.boundary, text_obj.content
112 );
113 }
114 for path_obj in &self.path_objects {
115 let _ = writeln!(
116 xml,
117 " <ofd:PathObject ID=\"{}\" Boundary=\"{}\">\
118 <ofd:AbbreviatedData>{}</ofd:AbbreviatedData>\
119 </ofd:PathObject>",
120 path_obj.id, path_obj.boundary, path_obj.abbreviated_data
121 );
122 }
123 for img_obj in &self.image_objects {
124 let _ = writeln!(
125 xml,
126 " <ofd:ImageObject ID=\"{}\" Boundary=\"{}\" ResourceID=\"{}\" />",
127 img_obj.id, img_obj.boundary, img_obj.resource_id
128 );
129 }
130 for block in &self.page_blocks {
131 xml.push_str(&block.to_xml_string());
132 }
133 xml.push_str("</ofd:PageBlock>\n");
134 xml
135 }
136}
137
138impl Default for CT_PageBlock {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144impl PageBlockTextObject {
145 #[must_use]
147 pub fn new(id: u32, boundary: impl Into<String>, content: impl Into<String>) -> Self {
148 Self {
149 id,
150 boundary: boundary.into(),
151 content: content.into(),
152 font_size: 12.0,
153 }
154 }
155
156 #[must_use]
158 pub fn font_size(mut self, size: f64) -> Self {
159 self.font_size = size;
160 self
161 }
162}
163
164impl PageBlockPathObject {
165 #[must_use]
167 pub fn new(id: u32, boundary: impl Into<String>, data: impl Into<String>) -> Self {
168 Self {
169 id,
170 boundary: boundary.into(),
171 abbreviated_data: data.into(),
172 }
173 }
174}
175
176impl PageBlockImageObject {
177 #[must_use]
179 pub fn new(id: u32, boundary: impl Into<String>, resource_id: u32) -> Self {
180 Self {
181 id,
182 boundary: boundary.into(),
183 resource_id,
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_ct_page_block_new() {
194 let block = CT_PageBlock::new();
195 assert!(block.page_blocks.is_empty());
196 assert!(block.text_objects.is_empty());
197 assert!(block.path_objects.is_empty());
198 assert!(block.image_objects.is_empty());
199 }
200
201 #[test]
202 fn test_ct_page_block_add_text() {
203 let mut block = CT_PageBlock::new();
204 block.add_text_object(PageBlockTextObject::new(1, "0 0 100 20", "hello"));
205 assert_eq!(block.text_objects.len(), 1);
206 assert_eq!(block.total_count(), 1);
207 }
208
209 #[test]
210 fn test_ct_page_block_add_path() {
211 let mut block = CT_PageBlock::new();
212 block.add_path_object(PageBlockPathObject::new(2, "0 0 50 50", "M0 0L10 10"));
213 assert_eq!(block.path_objects.len(), 1);
214 }
215
216 #[test]
217 fn test_ct_page_block_add_image() {
218 let mut block = CT_PageBlock::new();
219 block.add_image_object(PageBlockImageObject::new(3, "0 0 100 100", 10));
220 assert_eq!(block.image_objects.len(), 1);
221 }
222
223 #[test]
224 fn test_ct_page_block_nested() {
225 let inner = CT_PageBlock::new();
226 let mut outer = CT_PageBlock::new();
227 outer.add_page_block(inner);
228 assert_eq!(outer.get_page_blocks().len(), 1);
229 }
230
231 #[test]
232 fn test_ct_page_block_total_count_recursive() {
233 let mut inner = CT_PageBlock::new();
234 inner.add_text_object(PageBlockTextObject::new(1, "0 0 10 10", "x"));
235 inner.add_path_object(PageBlockPathObject::new(2, "0 0 10 10", "M0 0"));
236 let mut outer = CT_PageBlock::new();
237 outer.add_text_object(PageBlockTextObject::new(3, "0 0 10 10", "y"));
238 outer.add_page_block(inner);
239 assert_eq!(outer.total_count(), 3);
240 }
241
242 #[test]
243 fn test_ct_page_block_to_xml_basic() {
244 let block = CT_PageBlock::new();
245 let xml = block.to_xml_string();
246 assert!(xml.contains("<ofd:PageBlock>"));
247 assert!(xml.contains("</ofd:PageBlock>"));
248 }
249
250 #[test]
251 fn test_ct_page_block_to_xml_with_objects() {
252 let mut block = CT_PageBlock::new();
253 block.add_text_object(PageBlockTextObject::new(1, "10 20 50 15", "test").font_size(14.0));
254 block.add_image_object(PageBlockImageObject::new(2, "0 0 100 100", 5));
255 let xml = block.to_xml_string();
256 assert!(xml.contains("ofd:TextObject"));
257 assert!(xml.contains("test"));
258 assert!(xml.contains("ofd:ImageObject"));
259 assert!(xml.contains("ResourceID=\"5\""));
260 }
261
262 #[test]
263 fn test_ct_page_block_clone_debug() {
264 let block = CT_PageBlock::new();
265 let block2 = block.clone();
266 assert!(block2.text_objects.is_empty());
267 assert!(format!("{block:?}").contains("CT_PageBlock"));
268 }
269
270 #[test]
271 fn test_page_block_text_object_builder() {
272 let obj = PageBlockTextObject::new(1, "0 0 50 20", "hello").font_size(18.0);
273 assert_eq!(obj.id, 1);
274 assert_eq!(obj.content, "hello");
275 assert!((obj.font_size - 18.0).abs() < f64::EPSILON);
276 }
277}