text_document/flow.rs
1//! Flow types for document traversal and layout engine support.
2//!
3//! The layout engine processes [`FlowElement`]s in order to build its layout
4//! tree. Snapshot types capture consistent views for thread-safe reads.
5
6use crate::text_block::TextBlock;
7use crate::text_frame::TextFrame;
8use crate::text_table::TextTable;
9use crate::{Alignment, BlockFormat, FrameFormat, ListStyle, TextFormat};
10
11// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12// FlowElement
13// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
14
15/// An element in the document's visual flow.
16///
17/// The layout engine processes these in order to build its layout tree.
18/// Obtained from [`TextDocument::flow()`](crate::TextDocument::flow) or
19/// [`TextFrame::flow()`].
20#[derive(Clone)]
21pub enum FlowElement {
22 /// A paragraph or heading. Layout as a text block.
23 Block(TextBlock),
24
25 /// A table at this position in the flow. Layout as a grid.
26 /// The anchor frame's `table` field identifies the table entity.
27 Table(TextTable),
28
29 /// A non-table sub-frame (float, sidebar, blockquote).
30 /// Contains its own nested flow, accessible via
31 /// [`TextFrame::flow()`].
32 Frame(TextFrame),
33}
34
35impl FlowElement {
36 /// Snapshot this element into a thread-safe, plain-data representation.
37 ///
38 /// Dispatches to [`TextBlock::snapshot()`], [`TextTable::snapshot()`],
39 /// or [`TextFrame::snapshot()`] as appropriate.
40 pub fn snapshot(&self) -> FlowElementSnapshot {
41 match self {
42 FlowElement::Block(b) => FlowElementSnapshot::Block(b.snapshot()),
43 FlowElement::Table(t) => FlowElementSnapshot::Table(t.snapshot()),
44 FlowElement::Frame(f) => FlowElementSnapshot::Frame(f.snapshot()),
45 }
46 }
47}
48
49// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
50// FragmentContent
51// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
52
53/// A contiguous run of content with uniform formatting within a block.
54///
55/// Offsets are **block-relative**: `offset` is the character position
56/// within the block where this fragment starts (0 = block start).
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub enum FragmentContent {
59 /// A text run. The layout engine shapes these into glyphs.
60 Text {
61 text: String,
62 format: TextFormat,
63 /// Character offset within the block (block-relative).
64 offset: usize,
65 /// Character count.
66 length: usize,
67 },
68 /// An inline image. The layout engine reserves space for it.
69 ///
70 /// To retrieve the image pixel data, use the existing
71 /// [`TextDocument::resource(name)`](crate::TextDocument::resource) method.
72 Image {
73 name: String,
74 width: u32,
75 height: u32,
76 quality: u32,
77 format: TextFormat,
78 /// Character offset within the block (block-relative).
79 offset: usize,
80 },
81}
82
83// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
84// BlockSnapshot
85// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
86
87/// All layout-relevant data for one block, captured atomically.
88#[derive(Debug, Clone, PartialEq)]
89pub struct BlockSnapshot {
90 pub block_id: usize,
91 pub position: usize,
92 pub length: usize,
93 pub text: String,
94 pub fragments: Vec<FragmentContent>,
95 pub block_format: BlockFormat,
96 pub list_info: Option<ListInfo>,
97 /// Parent frame ID. Needed to know where this block lives in the
98 /// frame tree (e.g. main frame vs. a sub-frame or table cell frame).
99 pub parent_frame_id: Option<usize>,
100 /// If this block is inside a table cell, the cell coordinates.
101 /// Needed so the typesetter can propagate height changes to the
102 /// enclosing table row.
103 pub table_cell: Option<TableCellContext>,
104}
105
106/// Snapshot-friendly reference to a table cell (plain IDs, no live handles).
107#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct TableCellContext {
109 pub table_id: usize,
110 pub row: usize,
111 pub column: usize,
112}
113
114// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
115// ListInfo
116// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
117
118/// List membership and marker information for a block.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct ListInfo {
121 pub list_id: usize,
122 /// The list style (Disc, Decimal, LowerAlpha, etc.).
123 pub style: ListStyle,
124 /// Indentation level.
125 pub indent: u8,
126 /// Pre-formatted marker text: "•", "3.", "(c)", "IV.", etc.
127 pub marker: String,
128 /// 0-based index of this item within its list.
129 pub item_index: usize,
130}
131
132// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
133// TableCellRef
134// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
135
136/// Reference to a table cell that contains a block.
137#[derive(Clone)]
138pub struct TableCellRef {
139 pub table: TextTable,
140 pub row: usize,
141 pub column: usize,
142}
143
144// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
145// Table format types
146// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
147
148/// Table-level formatting.
149#[derive(Debug, Clone, Default, PartialEq, Eq)]
150pub struct TableFormat {
151 pub border: Option<i32>,
152 pub cell_spacing: Option<i32>,
153 pub cell_padding: Option<i32>,
154 pub width: Option<i32>,
155 pub alignment: Option<Alignment>,
156}
157
158/// Cell-level formatting.
159#[derive(Debug, Clone, Default, PartialEq, Eq)]
160pub struct CellFormat {
161 pub padding: Option<i32>,
162 pub border: Option<i32>,
163 pub vertical_alignment: Option<CellVerticalAlignment>,
164 pub background_color: Option<String>,
165}
166
167/// Vertical alignment within a table cell.
168#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
169pub enum CellVerticalAlignment {
170 #[default]
171 Top,
172 Middle,
173 Bottom,
174}
175
176// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
177// Table and Cell Snapshots
178// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
179
180/// Consistent snapshot of a table's structure and all cell content.
181#[derive(Debug, Clone, PartialEq)]
182pub struct TableSnapshot {
183 pub table_id: usize,
184 pub rows: usize,
185 pub columns: usize,
186 pub column_widths: Vec<i32>,
187 pub format: TableFormat,
188 pub cells: Vec<CellSnapshot>,
189}
190
191/// Snapshot of one table cell including its block content.
192#[derive(Debug, Clone, PartialEq)]
193pub struct CellSnapshot {
194 pub row: usize,
195 pub column: usize,
196 pub row_span: usize,
197 pub column_span: usize,
198 pub format: CellFormat,
199 pub blocks: Vec<BlockSnapshot>,
200}
201
202// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
203// Flow Snapshots
204// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
205
206/// Consistent snapshot of the entire document flow, captured in a
207/// single lock acquisition.
208#[derive(Debug, Clone, PartialEq)]
209pub struct FlowSnapshot {
210 pub elements: Vec<FlowElementSnapshot>,
211}
212
213/// Snapshot of one flow element.
214#[derive(Debug, Clone, PartialEq)]
215pub enum FlowElementSnapshot {
216 Block(BlockSnapshot),
217 Table(TableSnapshot),
218 Frame(FrameSnapshot),
219}
220
221/// Snapshot of a sub-frame and its contents.
222#[derive(Debug, Clone, PartialEq)]
223pub struct FrameSnapshot {
224 pub frame_id: usize,
225 pub format: FrameFormat,
226 pub elements: Vec<FlowElementSnapshot>,
227}
228
229// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
230// FormatChangeKind
231// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
232
233/// What kind of formatting changed.
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
235pub enum FormatChangeKind {
236 /// Block-level: alignment, margins, indent, heading level.
237 /// Requires paragraph relayout.
238 Block,
239 /// Character-level: font, bold, italic, underline, color.
240 /// Requires reshaping but not necessarily reflow.
241 Character,
242}