1use crate::area::{Area, AreaTree, AreaType, TraitSet};
7use crate::layout::{
8 extract_space_after, extract_space_before, extract_traits, BlockLayoutContext,
9 PageNumberResolver, TextAlign,
10};
11use fop_core::{FoArena, FoNodeData, NodeId, PropertyId};
12use fop_types::{FontRegistry, Length, Point, Rect, Result, Size};
13
14#[derive(Debug, Clone)]
16pub struct StreamingConfig {
17 pub max_memory_pages: usize,
20
21 pub page_width: Length,
23
24 pub page_height: Length,
26}
27
28impl Default for StreamingConfig {
29 fn default() -> Self {
30 Self {
31 max_memory_pages: 10,
32 page_width: Length::from_mm(210.0),
33 page_height: Length::from_mm(297.0),
34 }
35 }
36}
37
38pub struct StreamingLayoutEngine {
40 config: StreamingConfig,
41 #[allow(dead_code)]
42 font_registry: FontRegistry,
43}
44
45impl StreamingLayoutEngine {
46 pub fn new() -> Self {
48 Self::with_config(StreamingConfig::default())
49 }
50
51 pub fn with_config(config: StreamingConfig) -> Self {
53 Self {
54 config,
55 font_registry: FontRegistry::new(),
56 }
57 }
58
59 pub fn layout_streaming<'a, 'b>(
65 &'a self,
66 fo_tree: &'b FoArena<'b>,
67 ) -> StreamingLayoutIterator<'a, 'b> {
68 StreamingLayoutIterator::new(self, fo_tree)
69 }
70
71 fn layout_page_sequence<'b>(
73 &self,
74 fo_tree: &FoArena<'b>,
75 page_seq_id: NodeId,
76 resolver: &mut PageNumberResolver,
77 ) -> Result<Vec<AreaTree>> {
78 let mut pages = Vec::new();
79
80 let page_seq_node = fo_tree
82 .get(page_seq_id)
83 .ok_or_else(|| fop_types::FopError::Generic("Page sequence not found".to_string()))?;
84
85 if let FoNodeData::PageSequence { properties, .. } = &page_seq_node.data {
86 let mut page_tree = AreaTree::new();
91
92 let page_rect = Rect::from_point_size(
94 Point::ZERO,
95 Size::new(self.config.page_width, self.config.page_height),
96 );
97
98 let mut traits = TraitSet::default();
99 if let Ok(color) = properties.get(PropertyId::BackgroundColor) {
100 traits.background_color = color.as_color();
101 }
102
103 let area = Area::new(AreaType::Page, page_rect).with_traits(traits);
104 let page_id = page_tree.add_area(area);
105
106 if let Some(node) = fo_tree.get(page_seq_id) {
108 if let Some(id) = &node.id {
109 resolver.register_element(id.clone(), page_id);
110 }
111 }
112
113 let children = fo_tree.children(page_seq_id);
115 for child_id in children {
116 self.layout_node(fo_tree, child_id, &mut page_tree, Some(page_id), resolver)?;
117 }
118
119 resolver.set_current_page(resolver.current_page() + 1);
121
122 pages.push(page_tree);
123 }
124
125 Ok(pages)
126 }
127
128 fn layout_node<'b>(
130 &self,
131 fo_tree: &FoArena<'b>,
132 node_id: NodeId,
133 area_tree: &mut AreaTree,
134 parent_area: Option<crate::area::AreaId>,
135 resolver: &mut PageNumberResolver,
136 ) -> Result<Option<crate::area::AreaId>> {
137 let node = fo_tree
138 .get(node_id)
139 .ok_or_else(|| fop_types::FopError::Generic(format!("Node {} not found", node_id)))?;
140
141 let area_id = match &node.data {
142 FoNodeData::Flow { properties, .. } => {
143 let flow_rect = Rect::from_point_size(
145 Point::new(Length::from_pt(72.0), Length::from_pt(72.0)), Size::new(
147 self.config.page_width - Length::from_pt(144.0),
148 self.config.page_height - Length::from_pt(144.0),
149 ),
150 );
151
152 let mut traits = TraitSet::default();
153 if let Ok(color) = properties.get(PropertyId::Color) {
154 traits.color = color.as_color();
155 }
156
157 let area = Area::new(AreaType::Region, flow_rect).with_traits(traits);
158 let area_id = area_tree.add_area(area);
159
160 if let Some(parent) = parent_area {
161 area_tree
162 .append_child(parent, area_id)
163 .map_err(fop_types::FopError::Generic)?;
164 }
165
166 let children = fo_tree.children(node_id);
168 let mut block_ctx = BlockLayoutContext::new(flow_rect.width);
169
170 for child_id in children {
171 if let Some(child_area_id) = self.layout_block(
172 fo_tree,
173 child_id,
174 area_tree,
175 area_id,
176 block_ctx.current_y,
177 flow_rect.width,
178 resolver,
179 )? {
180 if let Some(child_area) = area_tree.get(child_area_id) {
181 block_ctx.current_y =
182 child_area.area.geometry.y + child_area.area.height();
183 }
184 }
185 }
186
187 Some(area_id)
188 }
189
190 _ => None,
191 };
192
193 Ok(area_id)
194 }
195
196 #[allow(clippy::too_many_arguments)]
198 fn layout_block<'b>(
199 &self,
200 fo_tree: &FoArena<'b>,
201 node_id: NodeId,
202 area_tree: &mut AreaTree,
203 parent_area: crate::area::AreaId,
204 y_offset: Length,
205 available_width: Length,
206 resolver: &mut PageNumberResolver,
207 ) -> Result<Option<crate::area::AreaId>> {
208 let node = fo_tree
209 .get(node_id)
210 .ok_or_else(|| fop_types::FopError::Generic(format!("Node {} not found", node_id)))?;
211
212 match &node.data {
213 FoNodeData::Block { properties } => {
214 let traits = extract_traits(properties);
215 let space_before = extract_space_before(properties);
216 let space_after = extract_space_after(properties);
217 let line_height = traits.font_size.unwrap_or(Length::from_pt(12.0));
218
219 let mut block_ctx = BlockLayoutContext::new(available_width);
220 block_ctx.current_y = y_offset;
221 let block_rect = block_ctx.allocate_with_spacing(
222 available_width,
223 line_height,
224 space_before,
225 space_after,
226 );
227
228 let area = Area::new(AreaType::Block, block_rect).with_traits(traits.clone());
229 let area_id = area_tree.add_area(area);
230 area_tree
231 .append_child(parent_area, area_id)
232 .map_err(fop_types::FopError::Generic)?;
233
234 let text_align = traits.text_align.unwrap_or(TextAlign::Left);
235
236 let children = fo_tree.children(node_id);
238 for child_id in children {
239 if let Some(child_node) = fo_tree.get(child_id) {
240 if let FoNodeData::Text(text) = &child_node.data {
241 let mut text_traits = traits.clone();
242 text_traits.text_align = Some(text_align);
243
244 let text_rect =
245 Rect::new(Length::ZERO, Length::ZERO, available_width, line_height);
246
247 let text_area =
248 Area::text(text_rect, text.clone()).with_traits(text_traits);
249 let text_id = area_tree.add_area(text_area);
250 area_tree
251 .append_child(area_id, text_id)
252 .map_err(fop_types::FopError::Generic)?;
253 }
254 }
255 }
256
257 if let Some(id) = &node.id {
259 resolver.register_element(id.clone(), area_id);
260 }
261
262 Ok(Some(area_id))
263 }
264 _ => Ok(None),
265 }
266 }
267}
268
269impl Default for StreamingLayoutEngine {
270 fn default() -> Self {
271 Self::new()
272 }
273}
274
275pub struct StreamingLayoutIterator<'a, 'b> {
277 engine: &'a StreamingLayoutEngine,
278 fo_tree: &'b FoArena<'b>,
279 resolver: PageNumberResolver,
280 page_sequences: Vec<NodeId>,
281 current_seq_index: usize,
282 current_page_buffer: Vec<AreaTree>,
283 current_page_index: usize,
284}
285
286impl<'a, 'b> StreamingLayoutIterator<'a, 'b> {
287 fn new(engine: &'a StreamingLayoutEngine, fo_tree: &'b FoArena<'b>) -> Self {
288 let mut page_sequences = Vec::new();
290 if let Some((root_id, _)) = fo_tree.root() {
291 for child_id in fo_tree.children(root_id) {
292 if let Some(child) = fo_tree.get(child_id) {
293 if matches!(child.data, FoNodeData::PageSequence { .. }) {
294 page_sequences.push(child_id);
295 }
296 }
297 }
298 }
299
300 Self {
301 engine,
302 fo_tree,
303 resolver: PageNumberResolver::new(),
304 page_sequences,
305 current_seq_index: 0,
306 current_page_buffer: Vec::new(),
307 current_page_index: 0,
308 }
309 }
310
311 fn load_next_batch(&mut self) -> Result<bool> {
312 if self.current_seq_index >= self.page_sequences.len() {
313 return Ok(false);
314 }
315
316 let seq_id = self.page_sequences[self.current_seq_index];
317 self.current_page_buffer =
318 self.engine
319 .layout_page_sequence(self.fo_tree, seq_id, &mut self.resolver)?;
320 self.current_page_index = 0;
321 self.current_seq_index += 1;
322
323 Ok(!self.current_page_buffer.is_empty())
324 }
325}
326
327impl<'a, 'b> Iterator for StreamingLayoutIterator<'a, 'b> {
328 type Item = Result<AreaTree>;
329
330 fn next(&mut self) -> Option<Self::Item> {
331 if self.current_page_index < self.current_page_buffer.len() {
333 let page = self.current_page_buffer.remove(0);
334 return Some(Ok(page));
335 }
336
337 match self.load_next_batch() {
339 Ok(true) => {
340 if !self.current_page_buffer.is_empty() {
341 let page = self.current_page_buffer.remove(0);
342 Some(Ok(page))
343 } else {
344 None
345 }
346 }
347 Ok(false) => None,
348 Err(e) => Some(Err(e)),
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use fop_core::{FoNode, FoNodeData, PropertyList};
357
358 #[test]
359 fn test_streaming_engine_creation() {
360 let engine = StreamingLayoutEngine::new();
361 assert_eq!(engine.config.page_width, Length::from_mm(210.0));
362 assert_eq!(engine.config.page_height, Length::from_mm(297.0));
363 assert_eq!(engine.config.max_memory_pages, 10);
364 }
365
366 #[test]
367 fn test_custom_config() {
368 let config = StreamingConfig {
369 max_memory_pages: 5,
370 page_width: Length::from_mm(215.9), page_height: Length::from_mm(279.4), };
373 let engine = StreamingLayoutEngine::with_config(config);
374 assert_eq!(engine.config.max_memory_pages, 5);
375 assert_eq!(engine.config.page_width, Length::from_mm(215.9));
376 }
377
378 #[test]
379 fn test_streaming_layout_empty_tree() {
380 let engine = StreamingLayoutEngine::new();
381 let fo_tree = FoArena::new();
382
383 let mut iter = engine.layout_streaming(&fo_tree);
384 assert!(iter.next().is_none());
385 }
386
387 #[test]
388 fn test_streaming_layout_single_page() {
389 let engine = StreamingLayoutEngine::new();
390 let mut fo_tree = FoArena::new();
391
392 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
394
395 let page_seq = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
396 master_reference: "A4".to_string(),
397 format: "1".to_string(),
398 grouping_separator: None,
399 grouping_size: None,
400 properties: PropertyList::new(),
401 }));
402 fo_tree
403 .append_child(root, page_seq)
404 .expect("test: should succeed");
405
406 let flow = fo_tree.add_node(FoNode::new(FoNodeData::Flow {
407 flow_name: "xsl-region-body".to_string(),
408 properties: PropertyList::new(),
409 }));
410 fo_tree
411 .append_child(page_seq, flow)
412 .expect("test: should succeed");
413
414 let block = fo_tree.add_node(FoNode::new(FoNodeData::Block {
415 properties: PropertyList::new(),
416 }));
417 fo_tree
418 .append_child(flow, block)
419 .expect("test: should succeed");
420
421 let mut page_count = 0;
423 for page_result in engine.layout_streaming(&fo_tree) {
424 let page = page_result.expect("test: should succeed");
425 assert!(!page.is_empty());
426 page_count += 1;
427 }
428
429 assert_eq!(page_count, 1);
430 }
431
432 #[test]
433 fn test_streaming_layout_multiple_pages() {
434 let engine = StreamingLayoutEngine::new();
435 let mut fo_tree = FoArena::new();
436
437 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
438
439 for i in 0..3 {
441 let page_seq = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
442 master_reference: format!("page-{}", i),
443 format: "1".to_string(),
444 grouping_separator: None,
445 grouping_size: None,
446 properties: PropertyList::new(),
447 }));
448 fo_tree
449 .append_child(root, page_seq)
450 .expect("test: should succeed");
451
452 let flow = fo_tree.add_node(FoNode::new(FoNodeData::Flow {
453 flow_name: "xsl-region-body".to_string(),
454 properties: PropertyList::new(),
455 }));
456 fo_tree
457 .append_child(page_seq, flow)
458 .expect("test: should succeed");
459 }
460
461 let mut page_count = 0;
463 for page_result in engine.layout_streaming(&fo_tree) {
464 let page = page_result.expect("test: should succeed");
465 assert!(!page.is_empty());
466 page_count += 1;
467 }
469
470 assert_eq!(page_count, 3);
471 }
472
473 #[test]
474 fn test_memory_bounded_processing() {
475 let config = StreamingConfig {
476 max_memory_pages: 2, ..Default::default()
478 };
479 let engine = StreamingLayoutEngine::with_config(config);
480 let mut fo_tree = FoArena::new();
481
482 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
483
484 for i in 0..10 {
486 let page_seq = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
487 master_reference: format!("page-{}", i),
488 format: "1".to_string(),
489 grouping_separator: None,
490 grouping_size: None,
491 properties: PropertyList::new(),
492 }));
493 fo_tree
494 .append_child(root, page_seq)
495 .expect("test: should succeed");
496
497 let flow = fo_tree.add_node(FoNode::new(FoNodeData::Flow {
498 flow_name: "xsl-region-body".to_string(),
499 properties: PropertyList::new(),
500 }));
501 fo_tree
502 .append_child(page_seq, flow)
503 .expect("test: should succeed");
504 }
505
506 let mut page_count = 0;
508 for page_result in engine.layout_streaming(&fo_tree) {
509 let _page = page_result.expect("test: should succeed");
510 page_count += 1;
511 }
512
513 assert_eq!(page_count, 10);
514 }
515}
516
517#[cfg(test)]
518mod extended_tests {
519 use super::*;
520 use fop_core::{FoArena, FoNode, FoNodeData, PropertyList};
521
522 fn make_fo_tree_with_n_page_sequences(n: usize) -> FoArena<'static> {
525 let mut fo_tree = FoArena::new();
526 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
527 for i in 0..n {
528 let page_seq = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
529 master_reference: format!("master-{}", i),
530 format: "1".to_string(),
531 grouping_separator: None,
532 grouping_size: None,
533 properties: PropertyList::new(),
534 }));
535 fo_tree
536 .append_child(root, page_seq)
537 .expect("test: should succeed");
538 let flow = fo_tree.add_node(FoNode::new(FoNodeData::Flow {
539 flow_name: "xsl-region-body".to_string(),
540 properties: PropertyList::new(),
541 }));
542 fo_tree
543 .append_child(page_seq, flow)
544 .expect("test: should succeed");
545 }
546 fo_tree
547 }
548
549 #[test]
552 fn test_streaming_config_default_page_width_is_a4() {
553 let config = StreamingConfig::default();
554 assert_eq!(config.page_width, Length::from_mm(210.0));
555 }
556
557 #[test]
558 fn test_streaming_config_default_page_height_is_a4() {
559 let config = StreamingConfig::default();
560 assert_eq!(config.page_height, Length::from_mm(297.0));
561 }
562
563 #[test]
564 fn test_streaming_config_default_max_memory_pages_is_ten() {
565 let config = StreamingConfig::default();
566 assert_eq!(config.max_memory_pages, 10);
567 }
568
569 #[test]
570 fn test_streaming_config_clone() {
571 let config = StreamingConfig::default();
572 let cloned = config.clone();
573 assert_eq!(cloned.max_memory_pages, config.max_memory_pages);
574 assert_eq!(cloned.page_width, config.page_width);
575 assert_eq!(cloned.page_height, config.page_height);
576 }
577
578 #[test]
579 fn test_streaming_config_debug() {
580 let config = StreamingConfig::default();
581 let dbg = format!("{:?}", config);
582 assert!(dbg.contains("StreamingConfig"));
583 }
584
585 #[test]
588 fn test_engine_default_equals_new() {
589 let a = StreamingLayoutEngine::new();
590 let b = StreamingLayoutEngine::default();
591 assert_eq!(a.config.max_memory_pages, b.config.max_memory_pages);
592 assert_eq!(a.config.page_width, b.config.page_width);
593 }
594
595 #[test]
596 fn test_engine_with_custom_page_size_letter() {
597 let config = StreamingConfig {
598 page_width: Length::from_mm(215.9),
599 page_height: Length::from_mm(279.4),
600 max_memory_pages: 5,
601 };
602 let engine = StreamingLayoutEngine::with_config(config);
603 assert_eq!(engine.config.page_width, Length::from_mm(215.9));
604 assert_eq!(engine.config.page_height, Length::from_mm(279.4));
605 assert_eq!(engine.config.max_memory_pages, 5);
606 }
607
608 #[test]
609 fn test_engine_with_large_memory_limit() {
610 let config = StreamingConfig {
611 max_memory_pages: 1000,
612 ..Default::default()
613 };
614 let engine = StreamingLayoutEngine::with_config(config);
615 assert_eq!(engine.config.max_memory_pages, 1000);
616 }
617
618 #[test]
619 fn test_engine_with_single_page_memory_limit() {
620 let config = StreamingConfig {
621 max_memory_pages: 1,
622 ..Default::default()
623 };
624 let engine = StreamingLayoutEngine::with_config(config);
625 assert_eq!(engine.config.max_memory_pages, 1);
626 }
627
628 #[test]
631 fn test_layout_page_has_root_area() {
632 let engine = StreamingLayoutEngine::new();
633 let mut fo_tree = FoArena::new();
634 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
635 let ps = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
636 master_reference: "A4".to_string(),
637 format: "1".to_string(),
638 grouping_separator: None,
639 grouping_size: None,
640 properties: PropertyList::new(),
641 }));
642 fo_tree
643 .append_child(root, ps)
644 .expect("test: should succeed");
645
646 let pages: Vec<_> = engine
647 .layout_streaming(&fo_tree)
648 .collect::<Result<Vec<_>>>()
649 .expect("test: should succeed");
650 assert_eq!(pages.len(), 1);
651 assert!(!pages[0].is_empty());
653 }
654
655 #[test]
656 fn test_layout_page_area_count_at_least_one() {
657 let engine = StreamingLayoutEngine::new();
658 let fo_tree = make_fo_tree_with_n_page_sequences(1);
659 let pages: Vec<_> = engine
660 .layout_streaming(&fo_tree)
661 .collect::<Result<Vec<_>>>()
662 .expect("test: should succeed");
663 assert!(!pages[0].is_empty());
664 }
665
666 #[test]
667 fn test_layout_five_page_sequences() {
668 let engine = StreamingLayoutEngine::new();
669 let fo_tree = make_fo_tree_with_n_page_sequences(5);
670 let pages: Vec<_> = engine
671 .layout_streaming(&fo_tree)
672 .collect::<Result<Vec<_>>>()
673 .expect("test: should succeed");
674 assert_eq!(pages.len(), 5);
675 }
676
677 #[test]
678 fn test_layout_twenty_page_sequences() {
679 let engine = StreamingLayoutEngine::new();
680 let fo_tree = make_fo_tree_with_n_page_sequences(20);
681 let count = engine
682 .layout_streaming(&fo_tree)
683 .filter(|r| r.is_ok())
684 .count();
685 assert_eq!(count, 20);
686 }
687
688 #[test]
689 fn test_each_page_is_independent() {
690 let engine = StreamingLayoutEngine::new();
691 let fo_tree = make_fo_tree_with_n_page_sequences(3);
692
693 let pages: Vec<_> = engine
694 .layout_streaming(&fo_tree)
695 .collect::<Result<Vec<_>>>()
696 .expect("test: should succeed");
697
698 assert_eq!(pages.len(), 3);
700 for page in &pages {
701 assert!(!page.is_empty());
702 }
703 }
704
705 #[test]
706 fn test_streaming_with_block_text() {
707 let engine = StreamingLayoutEngine::new();
708 let mut fo_tree = FoArena::new();
709
710 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
711 let ps = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
712 master_reference: "A4".to_string(),
713 format: "1".to_string(),
714 grouping_separator: None,
715 grouping_size: None,
716 properties: PropertyList::new(),
717 }));
718 fo_tree
719 .append_child(root, ps)
720 .expect("test: should succeed");
721
722 let flow = fo_tree.add_node(FoNode::new(FoNodeData::Flow {
723 flow_name: "xsl-region-body".to_string(),
724 properties: PropertyList::new(),
725 }));
726 fo_tree
727 .append_child(ps, flow)
728 .expect("test: should succeed");
729
730 let block = fo_tree.add_node(FoNode::new(FoNodeData::Block {
731 properties: PropertyList::new(),
732 }));
733 fo_tree
734 .append_child(flow, block)
735 .expect("test: should succeed");
736
737 let text = fo_tree.add_node(FoNode::new(FoNodeData::Text("Hello, World!".to_string())));
738 fo_tree
739 .append_child(block, text)
740 .expect("test: should succeed");
741
742 let pages: Vec<_> = engine
743 .layout_streaming(&fo_tree)
744 .collect::<Result<Vec<_>>>()
745 .expect("test: should succeed");
746 assert_eq!(pages.len(), 1);
747 assert!(!pages[0].is_empty());
749 }
750
751 #[test]
752 fn test_streaming_no_page_sequences_in_root() {
753 let engine = StreamingLayoutEngine::new();
754 let mut fo_tree = FoArena::new();
755 fo_tree.add_node(FoNode::new(FoNodeData::Root));
757
758 let pages: Vec<_> = engine
759 .layout_streaming(&fo_tree)
760 .collect::<Result<Vec<_>>>()
761 .expect("test: should succeed");
762 assert!(pages.is_empty());
763 }
764
765 #[test]
766 fn test_streaming_page_sequence_without_flow() {
767 let engine = StreamingLayoutEngine::new();
768 let mut fo_tree = FoArena::new();
769 let root = fo_tree.add_node(FoNode::new(FoNodeData::Root));
770 let ps = fo_tree.add_node(FoNode::new(FoNodeData::PageSequence {
771 master_reference: "A4".to_string(),
772 format: "1".to_string(),
773 grouping_separator: None,
774 grouping_size: None,
775 properties: PropertyList::new(),
776 }));
777 fo_tree
778 .append_child(root, ps)
779 .expect("test: should succeed");
780 let pages: Vec<_> = engine
782 .layout_streaming(&fo_tree)
783 .collect::<Result<Vec<_>>>()
784 .expect("test: should succeed");
785 assert_eq!(pages.len(), 1);
786 }
787
788 #[test]
789 fn test_streaming_iterator_is_lazy() {
790 let engine = StreamingLayoutEngine::new();
793 let fo_tree = make_fo_tree_with_n_page_sequences(5);
794 let mut iter = engine.layout_streaming(&fo_tree);
795
796 let first = iter.next();
798 assert!(first.is_some());
799 assert!(first.expect("test: should succeed").is_ok());
800 }
801
802 #[test]
803 fn test_streaming_iterator_terminates() {
804 let engine = StreamingLayoutEngine::new();
805 let fo_tree = make_fo_tree_with_n_page_sequences(3);
806 let iter = engine.layout_streaming(&fo_tree);
807
808 let results: Vec<_> = iter.collect();
810 assert_eq!(results.len(), 3);
811 for r in results {
813 assert!(r.is_ok());
814 }
815 }
816
817 #[test]
818 fn test_streaming_zero_page_sequences() {
819 let engine = StreamingLayoutEngine::new();
820 let fo_tree = FoArena::new();
821 let count = engine.layout_streaming(&fo_tree).count();
822 assert_eq!(count, 0);
823 }
824
825 #[test]
826 fn test_streaming_produces_page_area_type() {
827 let engine = StreamingLayoutEngine::new();
828 let fo_tree = make_fo_tree_with_n_page_sequences(1);
829 let pages: Vec<_> = engine
830 .layout_streaming(&fo_tree)
831 .collect::<Result<Vec<_>>>()
832 .expect("test: should succeed");
833 let page_tree = &pages[0];
834 if let Some((_, root_node)) = page_tree.root() {
836 assert_eq!(root_node.area.area_type, crate::area::AreaType::Page);
837 } else {
838 panic!("Expected a root area in page tree");
839 }
840 }
841}