Skip to main content

text_typeset/layout/
frame.rs

1use crate::font::registry::FontRegistry;
2use crate::layout::block::layout_block;
3use crate::layout::block::{BlockLayout, BlockLayoutParams};
4use crate::layout::table::{TableLayout, TableLayoutParams, layout_table};
5
6/// Frame position type (from text-document's FramePosition).
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub enum FramePosition {
9    /// Inline: rendered in normal flow order.
10    #[default]
11    Inline,
12    /// Float left (content wraps around -not yet implemented, treated as Inline).
13    FloatLeft,
14    /// Float right (content wraps around -not yet implemented, treated as Inline).
15    FloatRight,
16    /// Absolute positioning (not yet implemented, treated as Inline).
17    Absolute,
18}
19
20/// Parameters for a frame, extracted from text-document's FrameSnapshot.
21pub struct FrameLayoutParams {
22    pub frame_id: usize,
23    pub position: FramePosition,
24    /// Frame width constraint (None = use available width).
25    pub width: Option<f32>,
26    /// Frame height constraint (None = auto from content).
27    pub height: Option<f32>,
28    pub margin_top: f32,
29    pub margin_bottom: f32,
30    pub margin_left: f32,
31    pub margin_right: f32,
32    pub padding: f32,
33    pub border_width: f32,
34    /// Nested flow elements: blocks and tables within the frame.
35    pub blocks: Vec<BlockLayoutParams>,
36    pub tables: Vec<(usize, TableLayoutParams)>, // (flow_index, params) for ordering
37}
38
39/// Computed layout for a frame.
40pub struct FrameLayout {
41    pub frame_id: usize,
42    pub y: f32,
43    pub x: f32,
44    pub total_width: f32,
45    pub total_height: f32,
46    pub content_x: f32,
47    pub content_y: f32,
48    pub content_width: f32,
49    pub content_height: f32,
50    pub blocks: Vec<BlockLayout>,
51    pub tables: Vec<TableLayout>,
52    pub border_width: f32,
53}
54
55/// Lay out a frame: compute dimensions, lay out nested content.
56pub fn layout_frame(
57    registry: &FontRegistry,
58    params: &FrameLayoutParams,
59    available_width: f32,
60) -> FrameLayout {
61    let border = params.border_width;
62    let pad = params.padding;
63    let frame_width = params.width.unwrap_or(available_width);
64    let content_width =
65        (frame_width - border * 2.0 - pad * 2.0 - params.margin_left - params.margin_right)
66            .max(0.0);
67
68    // Lay out nested blocks
69    let mut blocks = Vec::new();
70    let mut content_y = 0.0f32;
71
72    for block_params in &params.blocks {
73        let mut block = layout_block(registry, block_params, content_width);
74        block.y = content_y + block.top_margin;
75        let block_content = block.height - block.top_margin - block.bottom_margin;
76        content_y = block.y + block_content + block.bottom_margin;
77        blocks.push(block);
78    }
79
80    // Lay out nested tables
81    let mut tables = Vec::new();
82    for (_flow_idx, table_params) in &params.tables {
83        let mut table = layout_table(registry, table_params, content_width);
84        table.y = content_y;
85        content_y += table.total_height;
86        tables.push(table);
87    }
88
89    let auto_content_height = content_y;
90    let content_height = params
91        .height
92        .map(|h| (h - border * 2.0 - pad * 2.0).max(0.0))
93        .unwrap_or(auto_content_height);
94
95    let total_height =
96        params.margin_top + border + pad + content_height + pad + border + params.margin_bottom;
97    let total_width =
98        params.margin_left + border + pad + content_width + pad + border + params.margin_right;
99
100    let content_x = params.margin_left + border + pad;
101    let content_y_offset = params.margin_top + border + pad;
102
103    FrameLayout {
104        frame_id: params.frame_id,
105        y: 0.0, // set by flow
106        x: 0.0,
107        total_width,
108        total_height,
109        content_x,
110        content_y: content_y_offset,
111        content_width,
112        content_height,
113        blocks,
114        tables,
115        border_width: border,
116    }
117}