Skip to main content

fop_layout/area/
mod.rs

1//! Area tree data structures
2//!
3//! The area tree represents the geometric layout of content on pages.
4//! Each area has a position, dimensions, and may contain child areas.
5
6pub mod area_tree;
7pub mod types;
8
9pub use area_tree::{AreaId, AreaNode, AreaTree};
10pub use types::{
11    Area, AreaContent, AreaType, BorderStyle, Direction, DisplayAlign, FontStretch, FontStyle,
12    FontVariant, Span, TextDecoration, TextTransform, TraitSet, WritingMode,
13};
14
15#[cfg(test)]
16mod tests {
17    use super::*;
18    use fop_types::{Color, Length, Point, Rect, Size};
19
20    fn make_rect(w: f64, h: f64) -> Rect {
21        Rect::from_point_size(
22            Point::ZERO,
23            Size::new(Length::from_pt(w), Length::from_pt(h)),
24        )
25    }
26
27    fn make_rect_at(x: f64, y: f64, w: f64, h: f64) -> Rect {
28        Rect::from_point_size(
29            Point::new(Length::from_pt(x), Length::from_pt(y)),
30            Size::new(Length::from_pt(w), Length::from_pt(h)),
31        )
32    }
33
34    // ---- Area construction tests ----
35
36    #[test]
37    fn test_area_new_creates_area_with_correct_type() {
38        let area = Area::new(AreaType::Block, make_rect(100.0, 50.0));
39        assert_eq!(area.area_type, AreaType::Block);
40    }
41
42    #[test]
43    fn test_area_new_stores_geometry() {
44        let rect = make_rect(200.0, 100.0);
45        let area = Area::new(AreaType::Page, rect);
46        assert_eq!(area.geometry.width, Length::from_pt(200.0));
47        assert_eq!(area.geometry.height, Length::from_pt(100.0));
48    }
49
50    #[test]
51    fn test_area_new_has_no_content_by_default() {
52        let area = Area::new(AreaType::Block, make_rect(100.0, 50.0));
53        assert!(area.content.is_none());
54        assert!(!area.has_text());
55        assert!(!area.has_image_data());
56    }
57
58    #[test]
59    fn test_area_new_has_default_traits() {
60        let area = Area::new(AreaType::Block, make_rect(100.0, 50.0));
61        assert!(area.traits.color.is_none());
62        assert!(area.traits.background_color.is_none());
63        assert!(area.traits.font_size.is_none());
64    }
65
66    #[test]
67    fn test_area_width_returns_geometry_width() {
68        let area = Area::new(AreaType::Block, make_rect(300.0, 50.0));
69        assert_eq!(area.width(), Length::from_pt(300.0));
70    }
71
72    #[test]
73    fn test_area_height_returns_geometry_height() {
74        let area = Area::new(AreaType::Block, make_rect(100.0, 75.0));
75        assert_eq!(area.height(), Length::from_pt(75.0));
76    }
77
78    // ---- Text area tests ----
79
80    #[test]
81    fn test_area_text_constructor() {
82        let area = Area::text(make_rect(100.0, 12.0), "hello".to_string());
83        assert_eq!(area.area_type, AreaType::Text);
84        assert!(area.has_text());
85    }
86
87    #[test]
88    fn test_area_text_content_returns_string() {
89        let area = Area::text(make_rect(100.0, 12.0), "hello world".to_string());
90        assert_eq!(area.text_content(), Some("hello world"));
91    }
92
93    #[test]
94    fn test_area_text_content_none_for_block() {
95        let area = Area::new(AreaType::Block, make_rect(100.0, 50.0));
96        assert!(area.text_content().is_none());
97    }
98
99    // ---- Viewport / image tests ----
100
101    #[test]
102    fn test_area_viewport_with_image() {
103        let image_data = vec![0u8, 1u8, 2u8, 3u8];
104        let area = Area::viewport_with_image(make_rect(100.0, 100.0), image_data.clone());
105        assert_eq!(area.area_type, AreaType::Viewport);
106        assert!(area.has_image_data());
107        assert_eq!(area.image_data(), Some(image_data.as_slice()));
108    }
109
110    #[test]
111    fn test_area_image_data_none_for_text_area() {
112        let area = Area::text(make_rect(100.0, 12.0), "text".to_string());
113        assert!(area.image_data().is_none());
114    }
115
116    // ---- with_traits tests ----
117
118    #[test]
119    fn test_area_with_traits_sets_color() {
120        let traits = TraitSet {
121            color: Some(Color::rgb(255, 0, 0)),
122            ..Default::default()
123        };
124        let area = Area::new(AreaType::Block, make_rect(100.0, 50.0)).with_traits(traits);
125        assert!(area.traits.color.is_some());
126    }
127
128    #[test]
129    fn test_area_with_traits_sets_font_size() {
130        let traits = TraitSet {
131            font_size: Some(Length::from_pt(14.0)),
132            ..Default::default()
133        };
134        let area = Area::new(AreaType::Text, make_rect(100.0, 14.0)).with_traits(traits);
135        assert_eq!(area.traits.font_size, Some(Length::from_pt(14.0)));
136    }
137
138    // ---- AreaTree integration via public API ----
139
140    #[test]
141    fn test_area_tree_new_is_empty() {
142        let tree = AreaTree::new();
143        assert!(tree.is_empty());
144        assert_eq!(tree.len(), 0);
145    }
146
147    #[test]
148    fn test_area_tree_add_returns_valid_id() {
149        let mut tree = AreaTree::new();
150        let id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
151        assert!(tree.get(id).is_some());
152    }
153
154    #[test]
155    fn test_area_tree_root_is_first_added() {
156        let mut tree = AreaTree::new();
157        let id = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
158        let (root_id, _) = tree.root().expect("test: should succeed");
159        assert_eq!(root_id, id);
160    }
161
162    #[test]
163    fn test_area_tree_children_empty_for_new_area() {
164        let mut tree = AreaTree::new();
165        let id = tree.add_area(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
166        let children = tree.children(id);
167        assert!(children.is_empty());
168    }
169
170    #[test]
171    fn test_area_tree_append_child_updates_parent() {
172        let mut tree = AreaTree::new();
173        let parent = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
174        let child = tree.add_area(Area::new(AreaType::Block, make_rect(400.0, 50.0)));
175        tree.append_child(parent, child)
176            .expect("test: should succeed");
177        assert_eq!(
178            tree.get(child).expect("test: should succeed").parent,
179            Some(parent)
180        );
181    }
182
183    #[test]
184    fn test_area_tree_multiple_pages() {
185        let mut tree = AreaTree::new();
186        let p1 = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
187        let p2 = tree.add_area(Area::new(AreaType::Page, make_rect(595.0, 842.0)));
188        let _ = (p1, p2);
189        assert_eq!(tree.len(), 2);
190    }
191
192    #[test]
193    fn test_area_tree_geometry_preserved() {
194        let mut tree = AreaTree::new();
195        let rect = make_rect_at(10.0, 20.0, 200.0, 100.0);
196        let id = tree.add_area(Area::new(AreaType::Block, rect));
197        let node = tree.get(id).expect("test: should succeed");
198        assert_eq!(node.area.geometry.width, Length::from_pt(200.0));
199        assert_eq!(node.area.geometry.height, Length::from_pt(100.0));
200    }
201
202    // ---- AreaId tests ----
203
204    #[test]
205    fn test_area_id_equality() {
206        let id_a = AreaId::from_index(5);
207        let id_b = AreaId::from_index(5);
208        assert_eq!(id_a, id_b);
209    }
210
211    #[test]
212    fn test_area_id_inequality() {
213        let id_a = AreaId::from_index(5);
214        let id_b = AreaId::from_index(6);
215        assert_ne!(id_a, id_b);
216    }
217
218    #[test]
219    fn test_area_id_display_format() {
220        let id = AreaId::from_index(3);
221        assert_eq!(format!("{}", id), "Area(3)");
222    }
223
224    // ---- AreaType distinctness ----
225
226    #[test]
227    fn test_area_types_page_not_block() {
228        assert_ne!(AreaType::Page, AreaType::Block);
229    }
230
231    #[test]
232    fn test_area_types_region_not_line() {
233        assert_ne!(AreaType::Region, AreaType::Line);
234    }
235
236    #[test]
237    fn test_area_types_text_not_inline() {
238        assert_ne!(AreaType::Text, AreaType::Inline);
239    }
240
241    // ---- AreaNode tests ----
242
243    #[test]
244    fn test_area_node_new_has_no_parent() {
245        let node = AreaNode::new(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
246        assert!(node.parent.is_none());
247        assert!(node.first_child.is_none());
248        assert!(node.next_sibling.is_none());
249    }
250
251    #[test]
252    fn test_area_node_has_children_false_initially() {
253        let node = AreaNode::new(Area::new(AreaType::Block, make_rect(100.0, 50.0)));
254        assert!(!node.has_children());
255    }
256
257    // ---- document_lang field ----
258
259    #[test]
260    fn test_area_tree_document_lang_none_by_default() {
261        let tree = AreaTree::new();
262        assert!(tree.document_lang.is_none());
263    }
264
265    #[test]
266    fn test_area_tree_document_lang_can_be_set() {
267        let mut tree = AreaTree::new();
268        tree.document_lang = Some("en-US".to_string());
269        assert_eq!(tree.document_lang.as_deref(), Some("en-US"));
270    }
271}