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 if let Some((root_id, _)) = self.root() {
213 self.serialize_node(root_id, 0, &mut output);
214 }
215 output
216 }
217
218 fn serialize_node(&self, id: AreaId, depth: usize, output: &mut String) {
220 if let Some(node) = self.get(id) {
221 let indent = " ".repeat(depth);
222 let area = &node.area;
223 let type_name = format!("{:?}", area.area_type);
224 let geom = &area.geometry;
225 output.push_str(&format!(
226 "{}{} ({:.1}pt,{:.1}pt {:.1}pt x {:.1}pt)\n",
227 indent,
228 type_name,
229 geom.x.to_pt(),
230 geom.y.to_pt(),
231 geom.width.to_pt(),
232 geom.height.to_pt()
233 ));
234 if let Some(text) = area.text_content() {
236 let preview: String = text.chars().take(40).collect();
237 output.push_str(&format!("{} Text {:?}\n", indent, preview));
238 }
239 for child_id in self.children(id) {
240 self.serialize_node(child_id, depth + 1, output);
241 }
242 }
243 }
244
245 pub fn footnote_height(&self, page_id: AreaId) -> fop_types::Length {
247 use fop_types::Length;
248
249 let mut total_height = Length::ZERO;
250
251 if let Some(footnotes) = self.get_footnotes(page_id) {
252 if !footnotes.is_empty() {
254 total_height += Length::from_pt(7.0);
255 }
256
257 for &footnote_id in footnotes {
259 if let Some(node) = self.get(footnote_id) {
260 total_height += node.area.height();
261 }
262 }
263 }
264
265 total_height
266 }
267
268 pub fn diff(&self, other: &AreaTree) -> Vec<String> {
273 let mut diffs = Vec::new();
274 match (self.root(), other.root()) {
275 (None, None) => {}
276 (Some(_), None) => diffs.push("self has root, other has no root".to_string()),
277 (None, Some(_)) => diffs.push("self has no root, other has root".to_string()),
278 (Some((id1, _)), Some((id2, _))) => {
279 self.diff_nodes(id1, other, id2, "", &mut diffs);
280 }
281 }
282 diffs
283 }
284
285 fn diff_nodes(
286 &self,
287 id1: AreaId,
288 other: &AreaTree,
289 id2: AreaId,
290 path: &str,
291 diffs: &mut Vec<String>,
292 ) {
293 let (node1, node2) = match (self.get(id1), other.get(id2)) {
294 (Some(n1), Some(n2)) => (n1, n2),
295 _ => {
296 diffs.push(format!("Node missing at path: {}", path));
297 return;
298 }
299 };
300
301 if std::mem::discriminant(&node1.area.area_type)
303 != std::mem::discriminant(&node2.area.area_type)
304 {
305 diffs.push(format!(
306 "Area type mismatch at {}: {:?} vs {:?}",
307 path, node1.area.area_type, node2.area.area_type
308 ));
309 }
310
311 if node1.area.text_content() != node2.area.text_content() {
313 diffs.push(format!(
314 "Text content mismatch at {}: {:?} vs {:?}",
315 path,
316 node1.area.text_content(),
317 node2.area.text_content()
318 ));
319 }
320
321 let children1 = self.children(id1);
323 let children2 = other.children(id2);
324 if children1.len() != children2.len() {
325 diffs.push(format!(
326 "Children count mismatch at {}: {} vs {}",
327 path,
328 children1.len(),
329 children2.len()
330 ));
331 }
332
333 let min_len = children1.len().min(children2.len());
335 for i in 0..min_len {
336 let child_path = format!("{}/{}", path, i);
337 self.diff_nodes(children1[i], other, children2[i], &child_path, diffs);
338 }
339 }
340}
341
342impl Default for AreaTree {
343 fn default() -> Self {
344 Self::new()
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351 use crate::area::AreaType;
352 use fop_types::{Length, Point, Rect, Size};
353
354 fn make_rect(w: f64, h: f64) -> Rect {
355 Rect::from_point_size(
356 Point::ZERO,
357 Size::new(Length::from_pt(w), Length::from_pt(h)),
358 )
359 }
360
361 #[test]
362 fn test_area_tree_creation() {
363 let tree = AreaTree::new();
364 assert_eq!(tree.len(), 0);
365 assert!(tree.is_empty());
366 }
367
368 #[test]
369 fn test_add_area() {
370 let mut tree = AreaTree::new();
371 let area = Area::new(AreaType::Page, make_rect(210.0, 297.0));
372 let id = tree.add_area(area);
373
374 assert_eq!(tree.len(), 1);
375 assert_eq!(id.index(), 0);
376 assert!(tree.get(id).is_some());
377 }
378
379 #[test]
380 fn test_parent_child() {
381 let mut tree = AreaTree::new();
382
383 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
384 let block = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
385
386 tree.append_child(page, block)
387 .expect("test: should succeed");
388
389 assert_eq!(
390 tree.get(block).expect("test: should succeed").parent,
391 Some(page)
392 );
393 assert_eq!(
394 tree.get(page).expect("test: should succeed").first_child,
395 Some(block)
396 );
397 }
398
399 #[test]
400 fn test_multiple_children() {
401 let mut tree = AreaTree::new();
402
403 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
404 let block1 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
405 let block2 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
406 let block3 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
407
408 tree.append_child(page, block1)
409 .expect("test: should succeed");
410 tree.append_child(page, block2)
411 .expect("test: should succeed");
412 tree.append_child(page, block3)
413 .expect("test: should succeed");
414
415 let children = tree.children(page);
416 assert_eq!(children.len(), 3);
417 assert_eq!(children[0], block1);
418 assert_eq!(children[1], block2);
419 assert_eq!(children[2], block3);
420 }
421
422 #[test]
423 fn test_find_page_ancestor() {
424 let mut tree = AreaTree::new();
425
426 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
427 let region = tree.add_area(Area::new(AreaType::Region, make_rect(160.0, 247.0)));
428 let block = tree.add_area(Area::new(AreaType::Block, make_rect(160.0, 20.0)));
429
430 tree.append_child(page, region)
431 .expect("test: should succeed");
432 tree.append_child(region, block)
433 .expect("test: should succeed");
434
435 assert_eq!(tree.find_page_ancestor(block), Some(page));
437
438 assert_eq!(tree.find_page_ancestor(region), Some(page));
440
441 assert_eq!(tree.find_page_ancestor(page), Some(page));
443 }
444
445 #[test]
446 fn test_footnote_tracking() {
447 let mut tree = AreaTree::new();
448
449 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
450 let footnote = tree.add_area(Area::new(AreaType::Footnote, make_rect(160.0, 12.0)));
451
452 assert!(tree.get_footnotes(page).is_none());
454
455 tree.add_footnote(page, footnote);
457 let footnotes = tree.get_footnotes(page).expect("test: should succeed");
458 assert_eq!(footnotes.len(), 1);
459 assert_eq!(footnotes[0], footnote);
460
461 let height = tree.footnote_height(page);
463 assert!(height > fop_types::Length::ZERO);
464 }
465 #[test]
466 fn test_area_tree_serialize_empty() {
467 let tree = AreaTree::new();
468 let s = tree.serialize();
469 let _ = s;
471 }
472
473 #[test]
474 fn test_area_tree_serialize_single_page() {
475 let mut tree = AreaTree::new();
476 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
477 let _ = page;
478
479 let s = tree.serialize();
480 assert!(
482 !s.is_empty(),
483 "Serialize should produce non-empty output for a page"
484 );
485 assert!(
486 s.contains("Page"),
487 "Serialize should mention AreaType::Page"
488 );
489 }
490
491 #[test]
492 fn test_area_tree_serialize_nested_areas() {
493 let mut tree = AreaTree::new();
494 let page = tree.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
495 let region = tree.add_area(Area::new(AreaType::Region, make_rect(160.0, 247.0)));
496 let block = tree.add_area(Area::new(AreaType::Block, make_rect(160.0, 20.0)));
497 tree.append_child(page, region)
498 .expect("test: should succeed");
499 tree.append_child(region, block)
500 .expect("test: should succeed");
501
502 let s = tree.serialize();
503 assert!(s.contains("Page"), "Should contain Page");
504 assert!(s.contains("Region"), "Should contain Region");
505 assert!(s.contains("Block"), "Should contain Block");
506 }
507
508 #[test]
509 fn test_area_tree_diff_two_empty_trees() {
510 let tree1 = AreaTree::new();
511 let tree2 = AreaTree::new();
512 let diffs = tree1.diff(&tree2);
513 assert!(
514 diffs.is_empty(),
515 "Two empty trees should have no differences"
516 );
517 }
518
519 #[test]
520 fn test_area_tree_diff_self_has_root_other_does_not() {
521 let mut tree1 = AreaTree::new();
522 let tree2 = AreaTree::new();
523 tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
524
525 let diffs = tree1.diff(&tree2);
526 assert!(
527 !diffs.is_empty(),
528 "Should detect that self has root but other does not"
529 );
530 assert!(
531 diffs[0].contains("no root"),
532 "Diff message should mention missing root"
533 );
534 }
535
536 #[test]
537 fn test_area_tree_diff_other_has_root_self_does_not() {
538 let tree1 = AreaTree::new();
539 let mut tree2 = AreaTree::new();
540 tree2.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
541
542 let diffs = tree1.diff(&tree2);
543 assert!(
544 !diffs.is_empty(),
545 "Should detect that other has root but self does not"
546 );
547 }
548
549 #[test]
550 fn test_area_tree_diff_identical_single_page() {
551 let mut tree1 = AreaTree::new();
552 let mut tree2 = AreaTree::new();
553 tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
554 tree2.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
555
556 let diffs = tree1.diff(&tree2);
557 assert!(
558 diffs.is_empty(),
559 "Identical single-page trees should have no diffs"
560 );
561 }
562
563 #[test]
564 fn test_area_tree_diff_different_root_type() {
565 let mut tree1 = AreaTree::new();
566 let mut tree2 = AreaTree::new();
567 tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
568 tree2.add_area(Area::new(AreaType::Region, make_rect(210.0, 297.0)));
569
570 let diffs = tree1.diff(&tree2);
571 assert!(
572 !diffs.is_empty(),
573 "Different area types should produce a diff"
574 );
575 assert!(
576 diffs
577 .iter()
578 .any(|d| d.contains("type") || d.contains("Area")),
579 "Diff should describe type mismatch"
580 );
581 }
582
583 #[test]
584 fn test_area_tree_diff_different_child_count() {
585 let mut tree1 = AreaTree::new();
586 let mut tree2 = AreaTree::new();
587
588 let page1 = tree1.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
589 let child1 = tree1.add_area(Area::new(AreaType::Block, make_rect(100.0, 20.0)));
590 tree1
591 .append_child(page1, child1)
592 .expect("test: should succeed");
593
594 let _page2 = tree2.add_area(Area::new(AreaType::Page, make_rect(210.0, 297.0)));
595 let diffs = tree1.diff(&tree2);
598 assert!(
599 !diffs.is_empty(),
600 "Different child counts should produce a diff"
601 );
602 }
603
604 #[test]
605 fn test_area_tree_diff_identical_with_text_content() {
606 let mut tree1 = AreaTree::new();
607 let mut tree2 = AreaTree::new();
608
609 let mut area1 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
610 area1.content = Some(crate::area::types::AreaContent::Text("Hello".to_string()));
611 let mut area2 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
612 area2.content = Some(crate::area::types::AreaContent::Text("Hello".to_string()));
613
614 tree1.add_area(area1);
615 tree2.add_area(area2);
616
617 let diffs = tree1.diff(&tree2);
618 assert!(
619 diffs.is_empty(),
620 "Trees with identical text content should have no diffs"
621 );
622 }
623
624 #[test]
625 fn test_area_tree_diff_different_text_content() {
626 let mut tree1 = AreaTree::new();
627 let mut tree2 = AreaTree::new();
628
629 let mut area1 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
630 area1.content = Some(crate::area::types::AreaContent::Text("Hello".to_string()));
631 let mut area2 = Area::new(AreaType::Text, make_rect(100.0, 12.0));
632 area2.content = Some(crate::area::types::AreaContent::Text("World".to_string()));
633
634 tree1.add_area(area1);
635 tree2.add_area(area2);
636
637 let diffs = tree1.diff(&tree2);
638 assert!(
639 !diffs.is_empty(),
640 "Different text content should produce a diff"
641 );
642 assert!(
643 diffs.iter().any(|d| d.contains("Text")),
644 "Diff should mention text content"
645 );
646 }
647}
648
649#[cfg(test)]
650mod extended_tests {
651 use super::*;
652 use crate::area::AreaType;
653 use fop_types::{Length, Point, Rect, Size};
654
655 fn make_rect(w: f64, h: f64) -> Rect {
656 Rect::from_point_size(
657 Point::ZERO,
658 Size::new(Length::from_pt(w), Length::from_pt(h)),
659 )
660 }
661
662 #[test]
665 fn test_area_id_index() {
666 let mut tree = AreaTree::new();
667 let a0 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
668 let a1 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
669 assert_eq!(a0.index(), 0);
670 assert_eq!(a1.index(), 1);
671 }
672
673 #[test]
674 fn test_area_id_from_index() {
675 let id = AreaId::from_index(42);
676 assert_eq!(id.index(), 42);
677 }
678
679 #[test]
680 fn test_area_id_display() {
681 let id = AreaId::from_index(7);
682 assert_eq!(format!("{}", id), "Area(7)");
683 }
684
685 #[test]
688 fn test_area_node_has_children_initially_false() {
689 let node = AreaNode::new(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
690 assert!(!node.has_children());
691 }
692
693 #[test]
694 fn test_area_node_has_children_after_append() {
695 let mut tree = AreaTree::new();
696 let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
697 let child = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
698 tree.append_child(parent, child)
699 .expect("test: should succeed");
700
701 let parent_node = tree.get(parent).expect("test: should succeed");
702 assert!(parent_node.has_children());
703 }
704
705 #[test]
708 fn test_area_tree_with_capacity() {
709 let tree = AreaTree::with_capacity(100);
710 assert!(tree.is_empty());
711 }
712
713 #[test]
714 fn test_area_tree_len_tracks_additions() {
715 let mut tree = AreaTree::new();
716 assert_eq!(tree.len(), 0);
717 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
718 assert_eq!(tree.len(), 1);
719 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
720 assert_eq!(tree.len(), 2);
721 }
722
723 #[test]
726 fn test_get_mut_can_modify_area() {
727 let mut tree = AreaTree::new();
728 let id = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
729
730 {
731 let node = tree.get_mut(id).expect("test: should succeed");
732 node.area.geometry.width = Length::from_pt(200.0);
733 }
734
735 let node = tree.get(id).expect("test: should succeed");
736 assert_eq!(node.area.geometry.width, Length::from_pt(200.0));
737 }
738
739 #[test]
740 fn test_get_nonexistent_returns_none() {
741 let tree = AreaTree::new();
742 let bogus_id = AreaId::from_index(999);
743 assert!(tree.get(bogus_id).is_none());
744 }
746
747 #[test]
750 fn test_children_ordering_preserved() {
751 let mut tree = AreaTree::new();
752 let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
753 let c1 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 20.0)));
754 let c2 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 30.0)));
755 let c3 = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 40.0)));
756
757 tree.append_child(parent, c1).expect("test: should succeed");
758 tree.append_child(parent, c2).expect("test: should succeed");
759 tree.append_child(parent, c3).expect("test: should succeed");
760
761 let children = tree.children(parent);
762 assert_eq!(children.len(), 3);
763 assert_eq!(children[0], c1);
764 assert_eq!(children[1], c2);
765 assert_eq!(children[2], c3);
766 }
767
768 #[test]
769 fn test_children_returns_empty_for_leaf() {
770 let mut tree = AreaTree::new();
771 let leaf = tree.add_area(Area::new(AreaType::Text, make_rect(100.0, 12.0)));
772 let children = tree.children(leaf);
773 assert!(children.is_empty());
774 }
775
776 #[test]
779 fn test_iter_over_all_areas() {
780 let mut tree = AreaTree::new();
781 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
782 tree.add_area(Area::new(AreaType::Block, make_rect(200.0, 50.0)));
783 tree.add_area(Area::new(AreaType::Block, make_rect(300.0, 50.0)));
784
785 let count = tree.iter().count();
786 assert_eq!(count, 3);
787 }
788
789 #[test]
790 fn test_root_returns_first_area() {
791 let mut tree = AreaTree::new();
792 let first_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
793 tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
794
795 let (root_id, root_node) = tree.root().expect("test: should succeed");
796 assert_eq!(root_id, first_id);
797 assert_eq!(root_node.area.area_type, AreaType::Page);
798 }
799
800 #[test]
801 fn test_root_empty_tree_returns_none() {
802 let tree = AreaTree::new();
803 assert!(tree.root().is_none());
804 }
805
806 #[test]
809 fn test_footnote_height_zero_with_no_footnotes() {
810 let mut tree = AreaTree::new();
811 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
812 let height = tree.footnote_height(page_id);
813 assert_eq!(height, Length::ZERO);
814 }
815
816 #[test]
817 fn test_footnote_tracking_multiple_footnotes() {
818 let mut tree = AreaTree::new();
819 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
820
821 let fn1 = tree.add_area(Area::new(AreaType::Footnote, make_rect(400.0, 30.0)));
822 let fn2 = tree.add_area(Area::new(AreaType::Footnote, make_rect(400.0, 20.0)));
823
824 tree.add_footnote(page_id, fn1);
825 tree.add_footnote(page_id, fn2);
826
827 let footnotes = tree.get_footnotes(page_id).expect("test: should succeed");
828 assert_eq!(footnotes.len(), 2);
829
830 let total_height = tree.footnote_height(page_id);
831 assert_eq!(total_height, Length::from_pt(57.0));
834 }
835
836 #[test]
837 fn test_get_footnotes_returns_none_for_page_without_footnotes() {
838 let mut tree = AreaTree::new();
839 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
840 assert!(tree.get_footnotes(page_id).is_none());
841 }
842
843 #[test]
846 fn test_find_page_ancestor_direct_child() {
847 let mut tree = AreaTree::new();
848 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
849 let block_id = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
850 tree.append_child(page_id, block_id)
851 .expect("test: should succeed");
852
853 let ancestor = tree.find_page_ancestor(block_id);
854 assert_eq!(ancestor, Some(page_id));
855 }
856
857 #[test]
858 fn test_find_page_ancestor_deep_nesting() {
859 let mut tree = AreaTree::new();
860 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
861 let region_id = tree.add_area(Area::new(AreaType::Region, make_rect(450.0, 700.0)));
862 let block_id = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
863 let text_id = tree.add_area(Area::new(AreaType::Text, make_rect(300.0, 12.0)));
864
865 tree.append_child(page_id, region_id)
866 .expect("test: should succeed");
867 tree.append_child(region_id, block_id)
868 .expect("test: should succeed");
869 tree.append_child(block_id, text_id)
870 .expect("test: should succeed");
871
872 let ancestor = tree.find_page_ancestor(text_id);
873 assert_eq!(ancestor, Some(page_id));
874 }
875
876 #[test]
877 fn test_find_page_ancestor_returns_none_for_page_itself() {
878 let mut tree = AreaTree::new();
879 let page_id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
880 let _result = tree.find_page_ancestor(page_id);
884 }
885
886 #[test]
889 fn test_serialize_produces_non_empty_string_for_non_empty_tree() {
890 let mut tree = AreaTree::new();
891 tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
892 let serialized = tree.serialize();
893 assert!(!serialized.is_empty());
894 }
895
896 #[test]
897 fn test_serialize_empty_tree_is_empty_string() {
898 let tree = AreaTree::new();
899 let serialized = tree.serialize();
900 assert!(serialized.is_empty());
901 }
902
903 #[test]
904 fn test_serialize_contains_area_type() {
905 let mut tree = AreaTree::new();
906 tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
907 let serialized = tree.serialize();
908 assert!(
909 serialized.contains("Page"),
910 "Serialized tree should mention 'Page'"
911 );
912 }
913
914 #[test]
917 fn test_diff_same_single_area_no_diffs() {
918 let mut tree1 = AreaTree::new();
919 let mut tree2 = AreaTree::new();
920 tree1.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
921 tree2.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
922
923 let diffs = tree1.diff(&tree2);
924 assert!(
925 diffs.is_empty(),
926 "Identical trees should have no diffs, got: {:?}",
927 diffs
928 );
929 }
930
931 #[test]
932 fn test_diff_different_area_types_produces_diff() {
933 let mut tree1 = AreaTree::new();
934 let mut tree2 = AreaTree::new();
935 tree1.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
936 tree2.add_area(Area::new(AreaType::Line, make_rect(100.0, 50.0)));
937
938 let diffs = tree1.diff(&tree2);
939 assert!(
940 !diffs.is_empty(),
941 "Different area types should produce a diff"
942 );
943 }
944
945 #[test]
946 fn test_diff_is_reflexive() {
947 let mut tree = AreaTree::new();
948 let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
949 let child = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
950 tree.append_child(parent, child)
951 .expect("test: should succeed");
952
953 let mut tree2 = AreaTree::new();
956 let parent2 = tree2.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
957 let child2 = tree2.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
958 tree2
959 .append_child(parent2, child2)
960 .expect("test: should succeed");
961
962 let diffs = tree.diff(&tree2);
963 assert!(diffs.is_empty(), "Identical trees should have no diffs");
964 }
965
966 #[test]
969 fn test_append_child_to_nonexistent_parent_returns_error() {
970 let mut tree = AreaTree::new();
971 let child = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
972 let bogus_parent = AreaId::from_index(999);
973 let result = tree.append_child(bogus_parent, child);
974 assert!(
975 result.is_err(),
976 "Appending to nonexistent parent should fail"
977 );
978 }
979}