Skip to main content

text_typeset/
lib.rs

1//! # text-typeset
2//!
3//! Turns rich text documents into GPU-ready glyph quads.
4//!
5//! Typesetting crate for the `text-document` ecosystem. Takes a rich
6//! text document model (styled paragraphs, tables, lists, frames)
7//! and produces positioned glyph quads, decoration rectangles, and a
8//! glyph atlas texture that any GPU framework can render in a few
9//! draw calls.
10//!
11//! ```text
12//! text-document (model) --> text-typeset (shaping + layout) --> framework adapter (rendering)
13//! ```
14//!
15//! # Architecture: shared service, owned flows
16//!
17//! text-typeset is split along the axis of "what is shareable":
18//!
19//! - [`TextFontService`] owns the font registry, the glyph atlas,
20//!   the glyph cache, the `swash` scale context, and the HiDPI
21//!   scale factor. It is the expensive-to-build, expensive-to-share
22//!   part. Construct one per process (or one per window) and share
23//!   it by reference across every widget that emits text.
24//!
25//! - [`DocumentFlow`] owns the per-widget view state: viewport,
26//!   zoom, scroll offset, content-width mode, flow layout, cursor,
27//!   and default colors. Every widget that shows a document holds
28//!   its own `DocumentFlow`. Layout and render methods take the
29//!   service by reference, so many flows can render into one
30//!   shared atlas — which means one GPU upload per frame, one
31//!   shaped glyph rasterized at most once, and no cross-widget
32//!   contamination of viewport / zoom / scroll state.
33//!
34//! # Quick start
35//!
36//! ```rust,no_run
37//! use text_typeset::{DocumentFlow, TextFontService};
38//!
39//! let mut service = TextFontService::new();
40//! let face = service.register_font(include_bytes!("../test-fonts/NotoSans-Variable.ttf"));
41//! service.set_default_font(face, 16.0);
42//!
43//! let mut flow = DocumentFlow::new();
44//! flow.set_viewport(800.0, 600.0);
45//!
46//! # #[cfg(feature = "text-document")]
47//! # {
48//! let doc = text_document::TextDocument::new();
49//! doc.set_plain_text("Hello, world!").unwrap();
50//! flow.layout_full(&service, &doc.snapshot_flow());
51//! # }
52//!
53//! let frame = flow.render(&mut service);
54//! // frame.glyphs       -> glyph quads (textured rects from the shared atlas)
55//! // frame.atlas_pixels -> RGBA texture to upload (or skip via service.atlas_pixels())
56//! // frame.decorations  -> cursor, selection, underlines, borders
57//! ```
58//!
59//! # Sharing between widgets
60//!
61//! Put the service behind whatever smart pointer the host framework
62//! uses — an `Rc<RefCell<TextFontService>>` for single-threaded UIs,
63//! a plain `&mut` in render loops that already have exclusive
64//! access. Every widget owns its own `DocumentFlow` and calls
65//! `flow.render(&mut *service.borrow_mut())` when it paints.
66//!
67//! Because the service does not store any per-widget state,
68//! "widget A rendered last" cannot break widget B. A changes to
69//! `set_viewport`, `set_zoom`, `set_scroll_offset`, or `set_cursor`
70//! live on A's flow and never touch B's.
71//!
72//! # Features
73//!
74//! - `text-document` (default): enables [`bridge`] module and
75//!   [`DocumentFlow::layout_full`] for direct integration with
76//!   text-document's `FlowSnapshot`.
77
78mod types;
79
80pub mod atlas;
81pub mod font;
82pub mod layout;
83mod render;
84pub mod shaping;
85
86#[cfg(feature = "text-document")]
87pub mod bridge;
88
89mod document_flow;
90mod font_service;
91
92// Public API
93pub use layout::inline_markup::{InlineAttrs, InlineMarkup, InlineSpan};
94pub use types::{
95    BlockVisualInfo, CharacterGeometry, CursorDisplay, DecorationKind, DecorationRect, FontFaceId,
96    GlyphQuad, HitRegion, HitTestResult, ImageQuad, LaidOutSpan, LaidOutSpanKind, ParagraphResult,
97    RenderFrame, SingleLineResult, TextFormat, UnderlineStyle, VerticalAlignment,
98};
99
100pub use document_flow::{ContentWidthMode, DocumentFlow, RelayoutError};
101pub use font_service::{AtlasSnapshot, TextFontService};