Skip to content

juice094/pretext-rust

pretext-rust

CI License: MIT Rust

Rust port of Pretext, a fast, accurate, and multilingual text measurement and line-breaking library.

  • Two-phase API: analyze text with prepare(), then wrap it with layout().
  • Backend agnostic: plug in your own FontMetrics, WordSegmenter, and GraphemeSegmenter implementations.
  • No browser required: replaces CanvasRenderingContext2D, Intl.Segmenter, and user-agent sniffing with native Rust backends.
  • Slint integration: drop-in PretextLabel component rendered on the CPU.

Table of contents

Installation

Add the crates you need to your Cargo.toml:

[dependencies]
pretext-core = { git = "https://github.com/juice094/pretext-rust.git" }
pretext-fontdb = { git = "https://github.com/juice094/pretext-rust.git" }
pretext-slint = { git = "https://github.com/juice094/pretext-rust.git" }

When the project is published to crates.io you will be able to use the version = "..." form instead.

Quick start

Plain Rust text layout

use pretext_core::{EngineProfile, Font, FontMetrics, PrepareOptions, prepare};
use pretext_core::layout::layout_with_lines;

struct FixedWidthMetrics { width_per_char: f32 }

impl FontMetrics for FixedWidthMetrics {
    fn measure(&self, text: &str, _font: &Font) -> f32 {
        text.chars().count() as f32 * self.width_per_char
    }
    fn supports_char(&self, _c: char, _font: &Font) -> bool { true }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let metrics = FixedWidthMetrics { width_per_char: 10.0 };
    let font = Font::new("16px Inter");
    let prepared = prepare("hello world", &font, &metrics, &PrepareOptions::default())?;
    let result = layout_with_lines(&prepared, 50.0, &EngineProfile::chromium())?;

    for line in &result.lines {
        println!("{:?} width={}", line.text, line.width);
    }
    Ok(())
}

Rich inline flow (chips, mentions, code spans)

use pretext_core::{EngineProfile, Font, FontMetrics, PrepareOptions, prepare};
use pretext_core::rich_inline::{
    prepare_rich_inline, walk_rich_inline_line_ranges, RichInlineItem,
};

struct FixedWidthMetrics { width_per_char: f32 }

impl FontMetrics for FixedWidthMetrics {
    fn measure(&self, text: &str, _font: &Font) -> f32 {
        text.chars().count() as f32 * self.width_per_char
    }
    fn supports_char(&self, _c: char, _font: &Font) -> bool { true }
}

fn main() {
    let metrics = FixedWidthMetrics { width_per_char: 10.0 };
    let font = Font::new("16px Inter");
    let items = vec![
        RichInlineItem::new("hello ", font.clone()),
        RichInlineItem::new("@mention", font),
    ];
    let prepared = prepare_rich_inline(&items, &metrics, &EngineProfile::chromium());
    walk_rich_inline_line_ranges(&prepared, 80.0, &EngineProfile::chromium(), |line| {
        for fragment in &line.fragments {
            println!("item {} width={}", fragment.item_index, fragment.occupied_width);
        }
    });
}

Slint component

Add pretext-slint to your dependencies, then in Rust:

use slint::{Color, ComponentHandle};

fn main() -> Result<(), slint::PlatformError> {
    let app = pretext_slint::PretextLabel::new()?;
    pretext_slint::install_render_callback(&app);

    app.set_text("The quick brown fox jumps over the lazy dog.".into());
    app.set_font("22px sans-serif".into());
    app.set_requested_width(240.0);
    app.set_text_color(Color::from_rgb_u8(220, 220, 220));

    app.run()
}

Run the included demo:

cargo run -p pretext-slint-demo

Workspace layout

pretext-rust/
├── crates/pretext-core         # Core algorithm crate (no Slint dependency)
├── crates/pretext-fontdb       # Default font backend (fontdb + rustybuzz + fontdue)
├── crates/pretext-slint        # Slint UI bridge
└── crates/pretext-slint-demo   # Demo application

Architecture

The library is split into layers so callers can plug in their own backends:

  1. Backends (pretext_core::measurement traits)
    • FontMetrics — measure text widths and glyph support.
    • WordSegmenter / GraphemeSegmenter — language-aware segmentation.
  2. Analysis (pretext_core::analysis)
    • Normalize whitespace, segment text, merge punctuation, handle CJK and Arabic shaping rules.
  3. Measurement (pretext_core::measurement)
    • Cache segment widths and pre-compute grapheme-level fit advances for overflow wrapping.
  4. Line breaking (pretext_core::line_break)
    • Pure-arithmetic greedy walker over the prepared segment stream.
  5. High-level layout (pretext_core::layout, pretext_core::line_text)
    • Materialize line ranges into text/width pairs.
  6. Rich inline (pretext_core::rich_inline)
    • Multi-item inline flow with collapsed boundary whitespace and atomic items.
  7. Font backend (pretext-fontdb)
    • System font resolution via fontdb, shaping via rustybuzz, rasterization via fontdue.
  8. Slint bridge (pretext-slint)
    • CPU-rendered Image component backed by the font backend.

See ARCHITECTURE.md for a deeper design record.

Performance

  • pretext-fontdb caches raw font data, parsed fontdue::Font objects, and rasterized glyphs per (font_id, glyph_index, size).
  • pretext-slint caches the final rendered SharedPixelBuffer keyed by (text, font, width, color).
  • For repeated rendering of the same label, the hot path is now essentially a cache lookup plus an Image construction.

Current limitations

  • Single font / color per label: pretext-slint does not yet support mixed fonts, spans, or inline images.
  • CPU rasterization: rendering is done on the CPU with manual alpha blending. It is good for labels and static text, but not optimized for high-frequency animation.
  • No text selection / cursor / IME: these require extra state machines on top of the layout results.
  • Color change still re-renders: only the final pixel buffer is cached; a color change currently rebuilds the image. Future work can cache glyph coverage and re-tint.

Development

git clone https://github.com/juice094/pretext-rust.git
cd pretext-rust
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo fmt --check
cargo doc --workspace --no-deps

See CONTRIBUTING.md for guidelines.

License

This project is licensed under the MIT License.

About

Rust port of Pretext: fast, accurate, multilingual text measurement, line-breaking, and CPU-rendered Slint label component.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages