text_typeset/types.rs
1/// Opaque handle to a registered font face.
2///
3/// Obtained from [`crate::Typesetter::register_font`] or [`crate::Typesetter::register_font_as`].
4/// Pass to [`crate::Typesetter::set_default_font`] to make it the default.
5#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
6pub struct FontFaceId(pub u32);
7
8// ── Render output ───────────────────────────────────────────────
9
10/// Everything needed to draw one frame.
11///
12/// Produced by [`crate::Typesetter::render`]. Contains glyph quads (textured rectangles
13/// from the atlas), inline image placeholders, and decoration rectangles
14/// (selections, cursor, underlines, table borders, etc.).
15///
16/// The adapter draws the frame in three passes:
17/// 1. Upload `atlas_pixels` as a GPU texture (only when `atlas_dirty` is true).
18/// 2. Draw each [`GlyphQuad`] as a textured rectangle from the atlas.
19/// 3. Draw each [`DecorationRect`] as a colored rectangle.
20pub struct RenderFrame {
21 /// True if the atlas texture changed since the last frame (needs re-upload).
22 pub atlas_dirty: bool,
23 /// Atlas texture width in pixels.
24 pub atlas_width: u32,
25 /// Atlas texture height in pixels.
26 pub atlas_height: u32,
27 /// RGBA pixel data, row-major. Length = `atlas_width * atlas_height * 4`.
28 pub atlas_pixels: Vec<u8>,
29 /// One textured rectangle per visible glyph.
30 pub glyphs: Vec<GlyphQuad>,
31 /// Inline image placeholders. The adapter loads the actual image data
32 /// (e.g., via `TextDocument::resource(name)`) and draws it at the given
33 /// screen position.
34 pub images: Vec<ImageQuad>,
35 /// Decoration rectangles: selections, cursor, underlines, strikeouts,
36 /// overlines, backgrounds, table borders, and cell backgrounds.
37 pub decorations: Vec<DecorationRect>,
38 /// Per-block glyph data for incremental updates. Keyed by block_id.
39 pub(crate) block_glyphs: Vec<(usize, Vec<GlyphQuad>)>,
40 /// Per-block decoration data (underlines, etc. — NOT cursor/selection).
41 pub(crate) block_decorations: Vec<(usize, Vec<DecorationRect>)>,
42 /// Per-block image data for incremental updates.
43 pub(crate) block_images: Vec<(usize, Vec<ImageQuad>)>,
44 /// Per-block height snapshot for detecting height changes in incremental render.
45 pub(crate) block_heights: std::collections::HashMap<usize, f32>,
46}
47
48/// A positioned glyph to draw as a textured quad from the atlas.
49///
50/// The adapter draws the rectangle at `screen` position, sampling from
51/// the `atlas` rectangle in the atlas texture, tinted with `color`.
52#[derive(Clone)]
53pub struct GlyphQuad {
54 /// Screen position and size: `[x, y, width, height]` in pixels.
55 pub screen: [f32; 4],
56 /// Atlas source rectangle: `[x, y, width, height]` in atlas pixel coordinates.
57 pub atlas: [f32; 4],
58 /// Glyph color: `[r, g, b, a]`, 0.0-1.0.
59 /// For normal text glyphs, this is the text color (default black).
60 /// For color emoji, this is `[1, 1, 1, 1]` (color is baked into the atlas).
61 pub color: [f32; 4],
62 /// `true` if the atlas region for this glyph holds a pre-multiplied
63 /// RGBA color bitmap (color emoji via COLR/CBDT/sbix). The renderer
64 /// must sample `texture.rgb` directly instead of using the texture
65 /// as an alpha mask tinted by [`color`](Self::color).
66 pub is_color: bool,
67}
68
69/// An inline image placeholder.
70///
71/// text-typeset computes the position and size but does NOT load or rasterize
72/// the image. The adapter retrieves the image data (e.g., from
73/// `TextDocument::resource(name)`) and draws it as a separate texture.
74#[derive(Clone)]
75pub struct ImageQuad {
76 /// Screen position and size: `[x, y, width, height]` in pixels.
77 pub screen: [f32; 4],
78 /// Image resource name (matches `FragmentContent::Image::name` from text-document).
79 pub name: String,
80}
81
82/// A colored rectangle for decorations (underlines, selections, borders, etc.).
83#[derive(Clone)]
84pub struct DecorationRect {
85 /// Screen position and size: `[x, y, width, height]` in pixels.
86 pub rect: [f32; 4],
87 /// Color: `[r, g, b, a]`, 0.0-1.0.
88 pub color: [f32; 4],
89 /// What kind of decoration this rectangle represents.
90 pub kind: DecorationKind,
91}
92
93/// The type of a [`DecorationRect`].
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum DecorationKind {
96 /// Selection highlight (translucent background behind selected text).
97 Selection,
98 /// Cursor caret (thin vertical line at the insertion point).
99 Cursor,
100 /// Underline (below baseline, from font metrics).
101 Underline,
102 /// Strikethrough (at x-height, from font metrics).
103 Strikeout,
104 /// Overline (at ascent line).
105 Overline,
106 /// Generic background (e.g., frame borders).
107 Background,
108 /// Block-level background color.
109 BlockBackground,
110 /// Table border line.
111 TableBorder,
112 /// Table cell background color.
113 TableCellBackground,
114 /// Text-level background highlight (behind individual text runs).
115 /// Adapters should draw these before glyph quads so text appears on top.
116 TextBackground,
117 /// Cell-level selection highlight (entire cell background when cells are
118 /// selected as a rectangular region, as opposed to text within cells).
119 CellSelection,
120}
121
122/// Underline style for text decorations.
123#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
124pub enum UnderlineStyle {
125 /// No underline.
126 #[default]
127 None,
128 /// Solid single underline.
129 Single,
130 /// Dashed underline.
131 Dash,
132 /// Dotted underline.
133 Dot,
134 /// Alternating dash-dot pattern.
135 DashDot,
136 /// Alternating dash-dot-dot pattern.
137 DashDotDot,
138 /// Wavy underline.
139 Wave,
140 /// Spell-check underline (wavy, typically red).
141 SpellCheck,
142}
143
144/// Vertical alignment for characters (superscript, subscript, etc.).
145#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
146pub enum VerticalAlignment {
147 /// Normal baseline alignment.
148 #[default]
149 Normal,
150 /// Superscript: smaller size, shifted up.
151 SuperScript,
152 /// Subscript: smaller size, shifted down.
153 SubScript,
154}
155
156// ── Hit testing ─────────────────────────────────────────────────
157
158/// Result of [`crate::Typesetter::hit_test`] - maps a screen-space point to a
159/// document position.
160pub struct HitTestResult {
161 /// Absolute character position in the document.
162 pub position: usize,
163 /// Which block (paragraph) was hit, identified by stable block ID.
164 pub block_id: usize,
165 /// Character offset within the block (0 = start of block).
166 pub offset_in_block: usize,
167 /// What region of the layout was hit.
168 pub region: HitRegion,
169 /// Tooltip text if the hit position has a tooltip. None otherwise.
170 pub tooltip: Option<String>,
171 /// When non-None, the hit position is inside a table cell.
172 /// Identifies the table by its stable table ID.
173 /// None for hits on top-level blocks, frame blocks, or outside any table.
174 pub table_id: Option<usize>,
175}
176
177/// What region of the layout a hit test landed in.
178#[derive(Debug)]
179pub enum HitRegion {
180 /// Inside a text run (normal text content).
181 Text,
182 /// In the block's left margin area (before any text content).
183 LeftMargin,
184 /// In the block's indent area.
185 Indent,
186 /// On a table border line.
187 TableBorder,
188 /// Below all content in the document.
189 BelowContent,
190 /// Past the end of a line (to the right of the last character).
191 PastLineEnd,
192 /// On an inline image.
193 Image { name: String },
194 /// On a hyperlink.
195 Link { href: String },
196}
197
198// ── Cursor display ──────────────────────────────────────────────
199
200/// Cursor display state for rendering.
201///
202/// The adapter reads cursor position from text-document's `TextCursor`
203/// and creates this struct to feed to [`crate::Typesetter::set_cursor`].
204/// text-typeset uses it to generate caret and selection decorations
205/// in the next [`crate::Typesetter::render`] call.
206pub struct CursorDisplay {
207 /// Cursor position (character offset in the document).
208 pub position: usize,
209 /// Selection anchor. Equals `position` when there is no selection.
210 /// When different from `position`, the range `[min(anchor, position), max(anchor, position))`
211 /// is highlighted as a selection.
212 pub anchor: usize,
213 /// Whether the caret is visible (false during the blink-off phase).
214 /// The adapter manages the blink timer; text-typeset just respects this flag.
215 pub visible: bool,
216 /// When non-empty, render cell-level selection highlights instead of
217 /// text-level selection. Each tuple is `(table_id, row, col)` identifying
218 /// a selected cell. The adapter fills this from `TextCursor::selected_cells()`.
219 pub selected_cells: Vec<(usize, usize, usize)>,
220}
221
222// ── Scrolling ───────────────────────────────────────────────────
223
224/// Visual position and size of a laid-out block.
225///
226/// Returned by [`crate::Typesetter::block_visual_info`].
227pub struct BlockVisualInfo {
228 /// Block ID (matches `BlockSnapshot::block_id`).
229 pub block_id: usize,
230 /// Y position of the block's top edge relative to the document start, in pixels.
231 pub y: f32,
232 /// Total height of the block including margins, in pixels.
233 pub height: f32,
234}
235
236// ── Single-line API ────────────────────────────────────────────
237
238/// Text formatting parameters for the single-line layout API.
239///
240/// Controls font selection, size, and text color. All fields are optional
241/// and fall back to the typesetter's defaults (default font, default size,
242/// default text color).
243#[derive(Clone, Debug, Default)]
244pub struct TextFormat {
245 /// Font family name (e.g., "Noto Sans", "monospace").
246 /// None means use the default font.
247 pub font_family: Option<String>,
248 /// Font weight (100-900). Overrides `font_bold`.
249 pub font_weight: Option<u32>,
250 /// Shorthand for weight 700. Ignored if `font_weight` is set.
251 pub font_bold: Option<bool>,
252 /// Italic style.
253 pub font_italic: Option<bool>,
254 /// Font size in pixels. None means use the default size.
255 pub font_size: Option<f32>,
256 /// Text color (RGBA, 0.0-1.0). None means use the typesetter's text color.
257 pub color: Option<[f32; 4]>,
258}
259
260/// Result of [`crate::Typesetter::layout_single_line`].
261///
262/// Contains the measured dimensions and GPU-ready glyph quads for a
263/// single line of text. No flow layout, line breaking, or bidi analysis
264/// is performed.
265pub struct SingleLineResult {
266 /// Total advance width of the shaped text, in pixels.
267 pub width: f32,
268 /// Line height (ascent + descent + leading), in pixels.
269 pub height: f32,
270 /// Distance from the top of the line to the baseline, in pixels.
271 pub baseline: f32,
272 /// Distance from baseline to the top of the underline, in logical
273 /// pixels. Positive = below the baseline. Sourced from the primary
274 /// font's `post` table.
275 pub underline_offset: f32,
276 /// Underline line thickness in logical pixels. Sourced from the
277 /// primary font's stroke size.
278 pub underline_thickness: f32,
279 /// GPU-ready glyph quads, positioned at y=0 (no scroll offset).
280 pub glyphs: Vec<GlyphQuad>,
281 /// Per-glyph cache keys, parallel to `glyphs`. Callers that cache
282 /// glyph output externally should pass these back to
283 /// [`TextFontService::touch_glyphs`] each frame to prevent the
284 /// atlas from evicting still-visible glyphs.
285 pub glyph_keys: Vec<crate::atlas::cache::GlyphCacheKey>,
286 /// Per-span bounding rectangles for markup-aware layout
287 /// ([`crate::Typesetter::layout_single_line_markup`]). Empty for
288 /// the plain-text layout path.
289 pub spans: Vec<LaidOutSpan>,
290}
291
292/// A single laid-out span produced by the markup-aware layout path.
293///
294/// When a link wraps across two paragraph lines, it produces two
295/// `LaidOutSpan` entries sharing the same URL and byte_range but with
296/// distinct `line_index` / `rect`.
297#[derive(Debug, Clone)]
298pub struct LaidOutSpan {
299 pub kind: LaidOutSpanKind,
300 /// Which wrapped line this span piece lives on (0 for single-line).
301 pub line_index: usize,
302 /// Local-space rect: `[x, y, width, height]`, same space as glyph quads.
303 pub rect: [f32; 4],
304 /// Byte range into the original markup source.
305 pub byte_range: std::ops::Range<usize>,
306}
307
308/// Kind discriminator for [`LaidOutSpan`].
309#[derive(Debug, Clone)]
310pub enum LaidOutSpanKind {
311 Text,
312 Link { url: String },
313}
314
315/// Result of [`crate::Typesetter::layout_paragraph`].
316///
317/// Contains the measured dimensions and GPU-ready glyph quads for a
318/// multi-line paragraph wrapped at a fixed width. Glyphs are positioned
319/// in paragraph-local coordinates: `x = 0` is the left edge of the
320/// paragraph, `y = 0` is the top of the first line's line box. The
321/// adapter should offset all glyph quads by the paragraph's screen
322/// position before drawing.
323pub struct ParagraphResult {
324 /// Width of the widest laid-out line, in pixels. May be less than the
325 /// `max_width` passed to `layout_paragraph` if the content is narrower.
326 pub width: f32,
327 /// Total stacked paragraph height in pixels — sum of line heights for
328 /// all emitted lines.
329 pub height: f32,
330 /// Distance from `y = 0` to the baseline of the first line, in pixels.
331 pub baseline_first: f32,
332 /// Number of lines actually emitted (respects `max_lines` when set).
333 pub line_count: usize,
334 /// Line height (single line's ascent + descent + leading), in pixels.
335 /// Useful for callers that need to reason about per-line geometry.
336 pub line_height: f32,
337 /// Distance from baseline to the top of the underline, in logical
338 /// pixels. Positive = below the baseline. Sourced from the primary
339 /// font's `post` table.
340 pub underline_offset: f32,
341 /// Underline line thickness in logical pixels. Sourced from the
342 /// primary font's stroke size.
343 pub underline_thickness: f32,
344 /// GPU-ready glyph quads in paragraph-local coordinates.
345 pub glyphs: Vec<GlyphQuad>,
346 /// Per-glyph cache keys, parallel to `glyphs`. See
347 /// [`SingleLineResult::glyph_keys`].
348 pub glyph_keys: Vec<crate::atlas::cache::GlyphCacheKey>,
349 /// Per-span bounding rectangles for markup-aware layout
350 /// ([`crate::Typesetter::layout_paragraph_markup`]). Empty for
351 /// the plain-text layout path.
352 pub spans: Vec<LaidOutSpan>,
353}
354
355impl RenderFrame {
356 pub(crate) fn new() -> Self {
357 Self {
358 atlas_dirty: false,
359 atlas_width: 0,
360 atlas_height: 0,
361 atlas_pixels: Vec::new(),
362 glyphs: Vec::new(),
363 images: Vec::new(),
364 decorations: Vec::new(),
365 block_glyphs: Vec::new(),
366 block_decorations: Vec::new(),
367 block_images: Vec::new(),
368 block_heights: std::collections::HashMap::new(),
369 }
370 }
371}
372
373// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
374// CharacterGeometry — accessibility per-character advance data
375// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
376
377/// Per-character advance geometry for a laid-out text run.
378///
379/// Consumed by accessibility layers that need to populate AccessKit's
380/// `character_positions` and `character_widths` on a `Role::TextRun`
381/// node so screen reader highlight cursors and screen magnifiers can
382/// track the caret at character granularity.
383///
384/// `position` is measured in run-local coordinates: the first
385/// character of the requested range sits at `position == 0.0`, and
386/// subsequent characters accumulate their advance widths. `width` is
387/// the advance width of each character, in the same units.
388#[derive(Debug, Clone, Copy, PartialEq)]
389pub struct CharacterGeometry {
390 pub position: f32,
391 pub width: f32,
392}