1use super::Area;
6use std::fmt;
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
10pub struct AreaId(usize);
11
12impl AreaId {
13 #[inline]
15 pub fn index(self) -> usize {
16 self.0
17 }
18
19 #[inline]
21 pub const fn from_index(index: usize) -> Self {
22 Self(index)
23 }
24}
25
26impl fmt::Display for AreaId {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(f, "Area({})", self.0)
29 }
30}
31
32pub struct AreaNode {
34 pub area: Area,
36
37 pub parent: Option<AreaId>,
39
40 pub first_child: Option<AreaId>,
42
43 pub next_sibling: Option<AreaId>,
45}
46
47impl AreaNode {
48 pub fn new(area: Area) -> Self {
50 Self {
51 area,
52 parent: None,
53 first_child: None,
54 next_sibling: None,
55 }
56 }
57
58 #[inline]
60 pub fn has_children(&self) -> bool {
61 self.first_child.is_some()
62 }
63}
64
65pub struct AreaTree {
67 nodes: Vec<AreaNode>,
68 footnotes: std::collections::HashMap<AreaId, Vec<AreaId>>,
70 pub document_lang: Option<String>,
72}
73
74impl AreaTree {
75 pub fn new() -> Self {
77 Self {
78 nodes: Vec::new(),
79 footnotes: std::collections::HashMap::new(),
80 document_lang: None,
81 }
82 }
83
84 pub fn with_capacity(capacity: usize) -> Self {
86 Self {
87 nodes: Vec::with_capacity(capacity),
88 footnotes: std::collections::HashMap::new(),
89 document_lang: None,
90 }
91 }
92
93 pub fn add_area(&mut self, area: Area) -> AreaId {
95 let id = AreaId(self.nodes.len());
96 self.nodes.push(AreaNode::new(area));
97 id
98 }
99
100 pub fn get(&self, id: AreaId) -> Option<&AreaNode> {
102 self.nodes.get(id.0)
103 }
104
105 pub fn get_mut(&mut self, id: AreaId) -> Option<&mut AreaNode> {
107 self.nodes.get_mut(id.0)
108 }
109
110 #[inline]
112 pub fn len(&self) -> usize {
113 self.nodes.len()
114 }
115
116 #[inline]
118 pub fn is_empty(&self) -> bool {
119 self.nodes.is_empty()
120 }
121
122 pub fn append_child(&mut self, parent: AreaId, child: AreaId) -> Result<(), String> {
124 if child.0 >= self.nodes.len() {
125 return Err(format!("Child area {} does not exist", child.0));
126 }
127 if parent.0 >= self.nodes.len() {
128 return Err(format!("Parent area {} does not exist", parent.0));
129 }
130
131 self.nodes[child.0].parent = Some(parent);
133
134 let parent_node = &mut self.nodes[parent.0];
136 if let Some(first_child) = parent_node.first_child {
137 let mut last_sibling = first_child;
139 while let Some(next) = self.nodes[last_sibling.0].next_sibling {
140 last_sibling = next;
141 }
142 self.nodes[last_sibling.0].next_sibling = Some(child);
143 } else {
144 parent_node.first_child = Some(child);
146 }
147
148 Ok(())
149 }
150
151 pub fn children(&self, parent: AreaId) -> Vec<AreaId> {
153 let mut children = Vec::new();
154 if let Some(node) = self.get(parent) {
155 let mut current = node.first_child;
156 while let Some(child_id) = current {
157 children.push(child_id);
158 current = self.get(child_id).and_then(|n| n.next_sibling);
159 }
160 }
161 children
162 }
163
164 pub fn iter(&self) -> impl Iterator<Item = (AreaId, &AreaNode)> {
166 self.nodes
167 .iter()
168 .enumerate()
169 .map(|(i, node)| (AreaId(i), node))
170 }
171
172 pub fn root(&self) -> Option<(AreaId, &AreaNode)> {
174 if self.nodes.is_empty() {
175 None
176 } else {
177 Some((AreaId(0), &self.nodes[0]))
178 }
179 }
180
181 pub fn add_footnote(&mut self, page_id: AreaId, footnote_id: AreaId) {
183 self.footnotes.entry(page_id).or_default().push(footnote_id);
184 }
185
186 pub fn get_footnotes(&self, page_id: AreaId) -> Option<&Vec<AreaId>> {
188 self.footnotes.get(&page_id)
189 }
190
191 pub fn find_page_ancestor(&self, area_id: AreaId) -> Option<AreaId> {
193 let mut current = area_id;
194 loop {
195 if let Some(node) = self.get(current) {
196 if node.area.area_type == crate::area::AreaType::Page {
197 return Some(current);
198 }
199 match node.parent {
200 Some(parent_id) => current = parent_id,
201 None => return None,
202 }
203 } else {
204 return None;
205 }
206 }
207 }
208
209 pub fn serialize(&self) -> String {
211 let mut output = String::new();
212 for (id, node) in self.iter() {
215 if node.parent.is_none() {
216 self.serialize_node(id, 0, &mut output);
217 }
218 }
219 output
220 }
221
222 fn serialize_node(&self, id: AreaId, depth: usize, output: &mut String) {
224 if let Some(node) = self.get(id) {
225 let indent = " ".repeat(depth);
226 let area = &node.area;
227 let type_name = format!("{:?}", area.area_type);
228 let geom = &area.geometry;
229 output.push_str(&format!(
230 "{}{} ({:.1}pt,{:.1}pt {:.1}pt x {:.1}pt)\n",
231 indent,
232 type_name,
233 geom.x.to_pt(),
234 geom.y.to_pt(),
235 geom.width.to_pt(),
236 geom.height.to_pt()
237 ));
238 if let Some(text) = area.text_content() {
240 let preview: String = text.chars().take(40).collect();
241 output.push_str(&format!("{} Text {:?}\n", indent, preview));
242 }
243 for child_id in self.children(id) {
244 self.serialize_node(child_id, depth + 1, output);
245 }
246 }
247 }
248
249 pub fn footnote_height(&self, page_id: AreaId) -> fop_types::Length {
251 use fop_types::Length;
252
253 let mut total_height = Length::ZERO;
254
255 if let Some(footnotes) = self.get_footnotes(page_id) {
256 if !footnotes.is_empty() {
258 total_height += Length::from_pt(7.0);
259 }
260
261 for &footnote_id in footnotes {
263 if let Some(node) = self.get(footnote_id) {
264 total_height += node.area.height();
265 }
266 }
267 }
268
269 total_height
270 }
271
272 pub fn diff(&self, other: &AreaTree) -> Vec<String> {
277 let mut diffs = Vec::new();
278 match (self.root(), other.root()) {
279 (None, None) => {}
280 (Some(_), None) => diffs.push("self has root, other has no root".to_string()),
281 (None, Some(_)) => diffs.push("self has no root, other has root".to_string()),
282 (Some((id1, _)), Some((id2, _))) => {
283 self.diff_nodes(id1, other, id2, "", &mut diffs);
284 }
285 }
286 diffs
287 }
288
289 fn diff_nodes(
290 &self,
291 id1: AreaId,
292 other: &AreaTree,
293 id2: AreaId,
294 path: &str,
295 diffs: &mut Vec<String>,
296 ) {
297 let (node1, node2) = match (self.get(id1), other.get(id2)) {
298 (Some(n1), Some(n2)) => (n1, n2),
299 _ => {
300 diffs.push(format!("Node missing at path: {}", path));
301 return;
302 }
303 };
304
305 if std::mem::discriminant(&node1.area.area_type)
307 != std::mem::discriminant(&node2.area.area_type)
308 {
309 diffs.push(format!(
310 "Area type mismatch at {}: {:?} vs {:?}",
311 path, node1.area.area_type, node2.area.area_type
312 ));
313 }
314
315 if node1.area.text_content() != node2.area.text_content() {
317 diffs.push(format!(
318 "Text content mismatch at {}: {:?} vs {:?}",
319 path,
320 node1.area.text_content(),
321 node2.area.text_content()
322 ));
323 }
324
325 let children1 = self.children(id1);
327 let children2 = other.children(id2);
328 if children1.len() != children2.len() {
329 diffs.push(format!(
330 "Children count mismatch at {}: {} vs {}",
331 path,
332 children1.len(),
333 children2.len()
334 ));
335 }
336
337 let min_len = children1.len().min(children2.len());
339 for i in 0..min_len {
340 let child_path = format!("{}/{}", path, i);
341 self.diff_nodes(children1[i], other, children2[i], &child_path, diffs);
342 }
343 }
344}
345
346impl Default for AreaTree {
347 fn default() -> Self {
348 Self::new()
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355 use crate::area::AreaType;
356 use fop_types::{Length, Point, Rect, Size};
357
358 fn make_rect(w: f64, h: f64) -> Rect {
359 Rect::from_point_size(
360 Point::ZERO,
361 Size::new(Length::from_pt(w), Length::from_pt(h)),
362 )
363 }
364
365 #[test]
366 fn test_area_tree_creation() {
367 let tree = AreaTree::new();
368 assert_eq!(tree.len(), 0);
369 assert!(tree.is_empty());
370 }
371
372 #[test]
373 fn test_add_area() {
374 let mut tree = AreaTree::new();
375 let area = Area::new(AreaType::Page, make_rect(210.0, 297.0));
376 let id = tree.add_area(area);
377
378 assert_eq!(tree.len(), 1);
379 assert_eq!(id.index(), 0);
380 assert!(tree.get(id).is_some());
381 }
382
383 #[test]
384 fn test_parent_child() {
385 let mut tree = AreaTree::new();
386
387 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
388 let block = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
389
390 tree.append_child(page, block)
391 .expect("test: should succeed");
392
393 assert_eq!(
394 tree.get(block).expect("test: should succeed").parent,
395 Some(page)
396 );
397 assert_eq!(
398 tree.get(page).expect("test: should succeed").first_child,
399 Some(block)
400 );
401 }
402
403 #[test]
404 fn test_multiple_children() {
405 let mut tree = AreaTree::new();
406
407 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
408 let block1 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
409 let block2 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
410 let block3 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
411
412 tree.append_child(page, block1)
413 .expect("test: should succeed");
414 tree.append_child(page, block2)
415 .expect("test: should succeed");
416 tree.append_child(page, block3)
417 .expect("test: should succeed");
418
419 let children = tree.children(page);
420 assert_eq!(children.len(), 3);
421 assert_eq!(children[0], block1);
422 assert_eq!(children[1], block2);
423 assert_eq!(children[2], block3);
424 }
425
426 #[test]
427 fn test_find_page_ancestor() {
428 let mut tree = AreaTree::new();
429
430 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
431 let region = tree.add_area(Area::new(AreaType::Region, make_rect(160.0, 247.0)));
432 let block = tree.add_area(Area::new(AreaType::Block, make_rect(160.0, 20.0)));
433
434 tree.append_child(page, region)
435 .expect("test: should succeed");
436 tree.append_child(region, block)
437 .expect("test: should succeed");
438
439 assert_eq!(tree.find_page_ancestor(block), Some(page));
441
442 assert_eq!(tree.find_page_ancestor(region), Some(page));
444
445 assert_eq!(tree.find_page_ancestor(page), Some(page));
447 }
448
449 #[test]
450 fn test_footnote_tracking() {
451 let mut tree = AreaTree::new();
452
453 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
454 let footnote = tree.add_area(Area::new(AreaType::Footnote, make_rect(160.0, 12.0)));
455
456 assert!(tree.get_footnotes(page).is_none());
458
459 tree.add_footnote(page, footnote);
461 let footnotes = tree.get_footnotes(page).expect("test: should succeed");
462 assert_eq!(footnotes.len(), 1);
463 assert_eq!(footnotes[0], footnote);
464
465 let height = tree.footnote_height(page);
467 assert!(height > fop_types::Length::ZERO);
468 }
469 #[test]
470 fn test_area_tree_serialize_empty() {
471 let tree = AreaTree::new();
472 let s = tree.serialize();
473 let _ = s;
475 }
476
477 #[test]
478 fn test_area_tree_serialize_single_page() {
479 let mut tree = AreaTree::new();
480 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
481 let _ = page;
482
483 let s = tree.serialize();
484 assert!(
486 !s.is_empty(),
487 "Serialize should produce non-empty output for a page"
488 );
489 assert!(
490 s.contains("Page"),
491 "Serialize should mention AreaType::Page"
492 );
493 }
494
495 #[test]
496 fn test_area_tree_serialize_nested_areas() {
497 let mut tree = AreaTree::new();
498 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
499 let region = tree.add_area(Area::new(AreaType::Region, make_rect(160.0, 247.0)));
500 let block = tree.add_area(Area::new(AreaType::Block, make_rect(160.0, 20.0)));
501 tree.append_child(page, region)
502 .expect("test: should succeed");
503 tree.append_child(region, block)
504 .expect("test: should succeed");
505
506 let s = tree.serialize();
507 assert!(s.contains("Page"), "Should contain Page");
508 assert!(s.contains("Region"), "Should contain Region");
509 assert!(s.contains("Block"), "Should contain Block");
510 }
511
512 #[test]
513 fn test_area_tree_diff_two_empty_trees() {
514 let tree1 = AreaTree::new();
515 let tree2 = AreaTree::new();
516 let diffs = tree1.diff(&tree2);
517 assert!(
518 diffs.is_empty(),
519 "Two empty trees should have no differences"
520 );
521 }
522
523 #[test]
524 fn test_area_tree_diff_self_has_root_other_does_not() {
525 let mut tree1 = AreaTree::new();
526 let tree2 = AreaTree::new();
527 tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
528
529 let diffs = tree1.diff(&tree2);
530 assert!(
531 !diffs.is_empty(),
532 "Should detect that self has root but other does not"
533 );
534 assert!(
535 diffs[0].contains("no root"),
536 "Diff message should mention missing root"
537 );
538 }
539
540 #[test]
541 fn test_area_tree_diff_other_has_root_self_does_not() {
542 let tree1 = AreaTree::new();
543 let mut tree2 = AreaTree::new();
544 tree2.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
545
546 let diffs = tree1.diff(&tree2);
547 assert!(
548 !diffs.is_empty(),
549 "Should detect that other has root but self does not"
550 );
551 }
552
553 #[test]
554 fn test_area_tree_diff_identical_single_page() {
555 let mut tree1 = AreaTree::new();
556 let mut tree2 = AreaTree::new();
557 tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
558 tree2.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
559
560 let diffs = tree1.diff(&tree2);
561 assert!(
562 diffs.is_empty(),
563 "Identical single-page trees should have no diffs"
564 );
565 }
566
567 #[test]
568 fn test_area_tree_diff_different_root_type() {
569 let mut tree1 = AreaTree::new();
570 let mut tree2 = AreaTree::new();
571 tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
572 tree2.add_area(Area::new(AreaType::Region, make_rect(210.0, 297.0)));
573
574 let diffs = tree1.diff(&tree2);
575 assert!(
576 !diffs.is_empty(),
577 "Different area types should produce a diff"
578 );
579 assert!(
580 diffs
581 .iter()
582 .any(|d| d.contains("type") || d.contains("Area")),
583 "Diff should describe type mismatch"
584 );
585 }
586
587 #[test]
588 fn test_area_tree_diff_different_child_count() {
589 let mut tree1 = AreaTree::new();
590 let mut tree2 = AreaTree::new();
591
592 let page1 = tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
593 let child1 = tree1.add_area(Area::new(AreaType::Block, make_rect(100.0, 20.0)));
594 tree1
595 .append_child(page1, child1)
596 .expect("test: should succeed");
597
598 let _page2 = tree2.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
599 let diffs = tree1.diff(&tree2);
602 assert!(
603 !diffs.is_empty(),
604 "Different child counts should produce a diff"
605 );
606 }
607
608 #[test]
609 fn test_area_tree_diff_identical_with_text_content() {
610 let mut tree1 = AreaTree::new();
611 let mut tree2 = AreaTree::new();
612
613 let mut area1 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
614 area1.content = Some(crate::area::types::AreaContent::Text("Hello".to_string()));
615 let mut area2 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
616 area2.content = Some(crate::area::types::AreaContent::Text("Hello".to_string()));
617
618 tree1.add_area(area1);
619 tree2.add_area(area2);
620
621 let diffs = tree1.diff(&tree2);
622 assert!(
623 diffs.is_empty(),
624 "Trees with identical text content should have no diffs"
625 );
626 }
627
628 #[test]
629 fn test_area_tree_diff_different_text_content() {
630 let mut tree1 = AreaTree::new();
631 let mut tree2 = AreaTree::new();
632
633 let mut area1 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
634 area1.content = Some(crate::area::types::AreaContent::Text("Hello".to_string()));
635 let mut area2 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
636 area2.content = Some(crate::area::types::AreaContent::Text("World".to_string()));
637
638 tree1.add_area(area1);
639 tree2.add_area(area2);
640
641 let diffs = tree1.diff(&tree2);
642 assert!(
643 !diffs.is_empty(),
644 "Different text content should produce a diff"
645 );
646 assert!(
647 diffs.iter().any(|d| d.contains("Text")),
648 "Diff should mention text content"
649 );
650 }
651}
652
653#[cfg(test)]
654mod extended_tests {
655 use super::*;
656 use crate::area::AreaType;
657 use fop_types::{Length, Point, Rect, Size};
658
659 fn make_rect(w: f64, h: f64) -> Rect {
660 Rect::from_point_size(
661 Point::ZERO,
662 Size::new(Length::from_pt(w), Length::from_pt(h)),
663 )
664 }
665
666 #[test]
669 fn test_area_id_index() {
670 let mut tree = AreaTree::new();
671 let a0 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
672 let a1 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
673 assert_eq!(a0.index(), 0);
674 assert_eq!(a1.index(), 1);
675 }
676
677 #[test]
678 fn test_area_id_from_index() {
679 let id = AreaId::from_index(42);
680 assert_eq!(id.index(), 42);
681 }
682
683 #[test]
684 fn test_area_id_display() {
685 let id = AreaId::from_index(7);
686 assert_eq!(format!("{}", id), "Area(7)");
687 }
688
689 #[test]
692 fn test_area_node_has_children_initially_false() {
693 let node = AreaNode::new(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
694 assert!(!node.has_children());
695 }
696
697 #[test]
698 fn test_area_node_has_children_after_append() {
699 let mut tree = AreaTree::new();
700 let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
701 let child = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
702 tree.append_child(parent, child)
703 .expect("test: should succeed");
704
705 let parent_node = tree.get(parent).expect("test: should succeed");
706 assert!(parent_node.has_children());
707 }
708
709 #[test]
712 fn test_area_tree_with_capacity() {
713 let tree = AreaTree::with_capacity(100);
714 assert!(tree.is_empty());
715 }
716
717 #[test]
718 fn test_area_tree_len_tracks_additions() {
719 let mut tree = AreaTree::new();
720 assert_eq!(tree.len(), 0);
721 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
722 assert_eq!(tree.len(), 1);
723 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
724 assert_eq!(tree.len(), 2);
725 }
726
727 #[test]
730 fn test_get_mut_can_modify_area() {
731 let mut tree = AreaTree::new();
732 let id = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
733
734 {
735 let node = tree.get_mut(id).expect("test: should succeed");
736 node.area.geometry.width = Length::from_pt(200.0);
737 }
738
739 let node = tree.get(id).expect("test: should succeed");
740 assert_eq!(node.area.geometry.width, Length::from_pt(200.0));
741 }
742
743 #[test]
744 fn test_get_nonexistent_returns_none() {
745 let tree = AreaTree::new();
746 let bogus_id = AreaId::from_index(999);
747 assert!(tree.get(bogus_id).is_none());
748 }
750
751 #[test]
754 fn test_children_ordering_preserved() {
755 let mut tree = AreaTree::new();
756 let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
757 let c1 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 20.0)));
758 let c2 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 30.0)));
759 let c3 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 40.0)));
760
761 tree.append_child(parent, c1).expect("test: should succeed");
762 tree.append_child(parent, c2).expect("test: should succeed");
763 tree.append_child(parent, c3).expect("test: should succeed");
764
765 let children = tree.children(parent);
766 assert_eq!(children.len(), 3);
767 assert_eq!(children[0], c1);
768 assert_eq!(children[1], c2);
769 assert_eq!(children[2], c3);
770 }
771
772 #[test]
773 fn test_children_returns_empty_for_leaf() {
774 let mut tree = AreaTree::new();
775 let leaf = tree.add_area(Area::new(AreaType::Text, make_rect(100.0, 12.0)));
776 let children = tree.children(leaf);
777 assert!(children.is_empty());
778 }
779
780 #[test]
783 fn test_iter_over_all_areas() {
784 let mut tree = AreaTree::new();
785 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
786 tree.add_area(Area::new(AreaType::Block, make_rect(200.0, 50.0)));
787 tree.add_area(Area::new(AreaType::Block, make_rect(300.0, 50.0)));
788
789 let count = tree.iter().count();
790 assert_eq!(count, 3);
791 }
792
793 #[test]
794 fn test_root_returns_first_area() {
795 let mut tree = AreaTree::new();
796 let first_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
797 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
798
799 let (root_id, root_node) = tree.root().expect("test: should succeed");
800 assert_eq!(root_id, first_id);
801 assert_eq!(root_node.area.area_type, AreaType::Page);
802 }
803
804 #[test]
805 fn test_root_empty_tree_returns_none() {
806 let tree = AreaTree::new();
807 assert!(tree.root().is_none());
808 }
809
810 #[test]
813 fn test_footnote_height_zero_with_no_footnotes() {
814 let mut tree = AreaTree::new();
815 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
816 let height = tree.footnote_height(page_id);
817 assert_eq!(height, Length::ZERO);
818 }
819
820 #[test]
821 fn test_footnote_tracking_multiple_footnotes() {
822 let mut tree = AreaTree::new();
823 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
824
825 let fn1 = tree.add_area(Area::new(AreaType::Footnote, make_rect(400.0, 30.0)));
826 let fn2 = tree.add_area(Area::new(AreaType::Footnote, make_rect(400.0, 20.0)));
827
828 tree.add_footnote(page_id, fn1);
829 tree.add_footnote(page_id, fn2);
830
831 let footnotes = tree.get_footnotes(page_id).expect("test: should succeed");
832 assert_eq!(footnotes.len(), 2);
833
834 let total_height = tree.footnote_height(page_id);
835 assert_eq!(total_height, Length::from_pt(57.0));
838 }
839
840 #[test]
841 fn test_get_footnotes_returns_none_for_page_without_footnotes() {
842 let mut tree = AreaTree::new();
843 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
844 assert!(tree.get_footnotes(page_id).is_none());
845 }
846
847 #[test]
850 fn test_find_page_ancestor_direct_child() {
851 let mut tree = AreaTree::new();
852 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
853 let block_id = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
854 tree.append_child(page_id, block_id)
855 .expect("test: should succeed");
856
857 let ancestor = tree.find_page_ancestor(block_id);
858 assert_eq!(ancestor, Some(page_id));
859 }
860
861 #[test]
862 fn test_find_page_ancestor_deep_nesting() {
863 let mut tree = AreaTree::new();
864 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
865 let region_id = tree.add_area(Area::new(AreaType::Region, make_rect(450.0, 700.0)));
866 let block_id = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
867 let text_id = tree.add_area(Area::new(AreaType::Text, make_rect(300.0, 12.0)));
868
869 tree.append_child(page_id, region_id)
870 .expect("test: should succeed");
871 tree.append_child(region_id, block_id)
872 .expect("test: should succeed");
873 tree.append_child(block_id, text_id)
874 .expect("test: should succeed");
875
876 let ancestor = tree.find_page_ancestor(text_id);
877 assert_eq!(ancestor, Some(page_id));
878 }
879
880 #[test]
881 fn test_find_page_ancestor_returns_none_for_page_itself() {
882 let mut tree = AreaTree::new();
883 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
884 let _result = tree.find_page_ancestor(page_id);
888 }
889
890 #[test]
893 fn test_serialize_produces_non_empty_string_for_non_empty_tree() {
894 let mut tree = AreaTree::new();
895 tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
896 let serialized = tree.serialize();
897 assert!(!serialized.is_empty());
898 }
899
900 #[test]
901 fn test_serialize_empty_tree_is_empty_string() {
902 let tree = AreaTree::new();
903 let serialized = tree.serialize();
904 assert!(serialized.is_empty());
905 }
906
907 #[test]
908 fn test_serialize_contains_area_type() {
909 let mut tree = AreaTree::new();
910 tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
911 let serialized = tree.serialize();
912 assert!(
913 serialized.contains("Page"),
914 "Serialized tree should mention 'Page'"
915 );
916 }
917
918 #[test]
921 fn test_diff_same_single_area_no_diffs() {
922 let mut tree1 = AreaTree::new();
923 let mut tree2 = AreaTree::new();
924 tree1.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
925 tree2.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
926
927 let diffs = tree1.diff(&tree2);
928 assert!(
929 diffs.is_empty(),
930 "Identical trees should have no diffs, got: {:?}",
931 diffs
932 );
933 }
934
935 #[test]
936 fn test_diff_different_area_types_produces_diff() {
937 let mut tree1 = AreaTree::new();
938 let mut tree2 = AreaTree::new();
939 tree1.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
940 tree2.add_area(Area::new(AreaType::Line, make_rect(100.0, 50.0)));
941
942 let diffs = tree1.diff(&tree2);
943 assert!(
944 !diffs.is_empty(),
945 "Different area types should produce a diff"
946 );
947 }
948
949 #[test]
950 fn test_diff_is_reflexive() {
951 let mut tree = AreaTree::new();
952 let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
953 let child = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
954 tree.append_child(parent, child)
955 .expect("test: should succeed");
956
957 let mut tree2 = AreaTree::new();
960 let parent2 = tree2.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
961 let child2 = tree2.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
962 tree2
963 .append_child(parent2, child2)
964 .expect("test: should succeed");
965
966 let diffs = tree.diff(&tree2);
967 assert!(diffs.is_empty(), "Identical trees should have no diffs");
968 }
969
970 #[test]
973 fn test_append_child_to_nonexistent_parent_returns_error() {
974 let mut tree = AreaTree::new();
975 let child = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
976 let bogus_parent = AreaId::from_index(999);
977 let result = tree.append_child(bogus_parent, child);
978 assert!(
979 result.is_err(),
980 "Appending to nonexistent parent should fail"
981 );
982 }
983}