fontdue

repository·master·Indexed 23 days ago

https://github.com/mooman219/fontdue

A high-performance, pure Rust font rasterizer and layout tool supporting TrueType (.ttf/.ttc) and OpenType (.otf) formats. Designed for low-latency applications and compatible with no_std environments (requiring the alloc crate), it provides APIs for rasterizing glyphs with subpixel anti-aliasing, calculating layout metrics, and performing naïve text layout. It focuses on rasterization and layout and does not handle text shaping.

Tokens
4.1K
Snippets
4
Records
23
Agent score
83%

What's inside fontdue

  1. Overview of Fontdue

    master

    Fontdue is a pure Rust, no_std font rasterizer and layout tool that supports TrueType (.ttf/.ttc) and OpenType (.otf) formats. It is designed for high performance and low end-to-end latency.

    Key Characteristics:

    • Portability: Uses no_std (requires the alloc crate) for portability across environments.
    • Memory Model: Unlike some other font libraries, font structures in Fontdue have no lifetime dependencies because they allocate their own space during parsing.
    • Scope: It is a rasterizer and layout tool that does not handle text shaping. If you require full text shaping, consider using Cosmic Text.
    • Performance: Optimized for fast rasterization and layout tasks.
  2. Perform text layout with Fontdue

    master

    Fontdue provides a naïve layout API designed for simplicity. Layout operations require a Layout context, which manages heap allocations. Reusing a Layout instance via .reset() is recommended to reduce allocation overhead.

    Workflow:

    1. Create a Layout instance, specifying the CoordinateSystem (e.g., CoordinateSystem::PositiveYUp).
    2. Provide a slice of Font instances to be used during layout.
    3. Use .append() to add text segments using TextStyle. Each segment specifies the text string, font size, and the index of the font in your provided font list.
    4. Access the resulting glyphs via .glyphs().

    Note: The layout API is currently immature and may undergo breaking changes.

    // Read the font data.
    let font = include_bytes!("../resources/fonts/Roboto-Regular.ttf") as &[u8];
    // Parse it into the font type.
    let roboto_regular = Font::from_bytes(font, fontdue::FontSettings::default()).unwrap();
    // The list of fonts that will be used during layout.
    let fonts = &[roboto_regular];
    // Create a layout context. Laying out text needs some heap allocations; reusing this context
    // reduces the need to reallocate space. We inform layout of which way the Y axis points here.
    let mut layout = Layout::new(CoordinateSystem::PositiveYUp);
    // By default, layout is initialized with the default layout settings. This call is redundant,
    // but demonstrates setting the value with your custom settings.
    layout.reset(&LayoutSettings {
        ..LayoutSettings::default()
    });
    // The text that will be laid out, its size, and the index of the font in the font list to use for
    // that section of text.
    layout.append(fonts, &TextStyle::new("Hello ", 35.0, 0));
    layout.append(fonts, &TextStyle::new("world!", 40.0, 0));
    // Prints the layout for "Hello world!"
    println!("{:?}", layout.glyphs());
  3. Use Fontdue for font parsing, rasterization, and layout

    master
    Fontdue is a no_std crate (requiring the alloc crate) designed for font parsing, rasterization, and text layout. It provides high-performance tools for handling font data and rendering text. The primary entry points for font data structures are exported via the font module.
  4. Manage linebreaks with Linebreaker and LinebreakData

    master

    The Linebreaker struct implements a state machine to determine how characters affect line breaks. By feeding characters into Linebreaker::next, you receive LinebreakData which indicates if a break is possible.

    LinebreakData represents three states:

    • LINEBREAK_HARD: A mandatory break point.
    • LINEBREAK_SOFT: An optional break point.
    • LINEBREAK_NONE: No break possible.

    Ordering of LinebreakData follows priority: HARD > SOFT > NONE.

  5. Initialize a Font from bytes

    master

    Use Font::from_bytes to create a Font instance from a byte array. This requires a FontSettings object to configure parsing behavior.

    FontSettings Configuration:

    • collection_index: The index of the font to use if parsing a font collection (default: 0).
    • scale: The scale in pixels the font geometry is optimized for. Fonts rendered at this scale provide optimal looks and performance (default: 40.0). The units are pixels per Em unit.
    • load_substitutions: If true, loads glyphs for substitutions (like ligatures) from the gsub table (default: true). This is only effective when using indexed operations like rasterize_indexed.
    let settings = FontSettings {
        collection_index: 0,
        scale: 40.0,
        load_substitutions: true,
    };
    let font = Font::from_bytes(font_data, settings).expect("Failed to load font");
  6. Initialize and use the Layout struct

    master

    The Layout struct manages the state required for text layout. It is designed to be reused between layout calls to minimize heap allocations and improve performance.

    1. Create: Use Layout::new(coordinate_system) where coordinate_system is either CoordinateSystem::PositiveYUp or CoordinateSystem::PositiveYDown. This informs how Y coordinates are calculated.
    2. Configure: Call reset(&settings) to apply new LayoutSettings and clear previous text.
    3. Append: Use append(fonts, style) to add text segments. This performs the actual layout logic.
    4. Retrieve: Access the resulting GlyphPositions via .glyphs() or line metrics via .lines().

    Note: Reusing a Layout instance by calling reset or clear is highly recommended for performance.

  7. Attach custom metadata to glyphs during layout

    master

    You can attach user-defined metadata to glyphs by using TextStyle::with_user_data. When using this method, the Layout type becomes parameterized by your metadata type (e.g., Layout<u8>). All TextStyle segments appended to the layout must share the same metadata type.

    // If you wanted to attached metadata based on the TextStyle to the glyphs returned by the
    // glyphs() function, you can use the TextStyle::with_metadata function. In this example, the
    // Layout type is now parameterized with u8 (Layout<u8>). All styles need to share the same
    // metadata type.
    let mut layout = Layout::new(CoordinateSystem::PositiveYUp);
    layout.append(fonts, &TextStyle::with_user_data("Hello ", 35.0, 0, 10u8));
    layout.append(fonts, &TextStyle::with_user_data("world!", 40.0, 0, 20u8));
    println!("{:?}", layout.glyphs());
  8. Rasterize glyphs with Fontdue

    master

    To rasterize a character, you must first load the font bytes and parse them into a fontdue::Font instance using Font::from_bytes. You can then call .rasterize() on the font instance to obtain both the glyph metrics and the bitmap for a specific character at a given pixel size.

    Note: The rasterization API is considered stable and is unlikely to undergo major changes.

    // Read the font data.
    let font = include_bytes!("../resources/Roboto-Regular.ttf") as &[u8];
    // Parse it into the font type.
    let font = fontdue::Font::from_bytes(font, fontdue::FontSettings::default()).unwrap();
    // Rasterize and get the layout metrics for the letter 'g' at 17px.
    let (metrics, bitmap) = font.rasterize('g', 17.0);
  9. Configure text layout with LayoutSettings

    master

    Use LayoutSettings to define the constraints and alignment for text layout. Text layout is a best-effort process; if constraints prevent layout, text may overflow.

    Key configuration fields:

    • x, y: The starting coordinates of the text region.
    • max_width: (Optional) The rightmost boundary. If exceeded, text wraps to the next line. If a single glyph is wider than max_width, it will overflow.
    • max_height: (Optional) The bottom boundary. Used for vertical alignment. Text exceeding this will overflow.
    • horizontal_align: Controls alignment within max_width (Left, Center, Right).
    • vertical_align: Controls alignment within max_height (Top, Middle, Bottom).
    • line_height: A multiplier for the default line height.
    • wrap_style: Determines how text wraps (Word preserves words using Unicode rules; Letter breaks at the nearest letter).
    • wrap_hard_breaks: If true, newline characters trigger immediate line wraps.
  10. Define text segments with TextStyle

    master

    TextStyle describes a segment of text to be laid out, including its scale, font, and optional user data.

    • text: The string slice to layout.
    • px: The scale of the text in pixels (pixels per Em unit).
    • font_index: The index of the font in the slice passed to the Layout::append method.
    • user_data: An optional piece of user-defined data (must implement Copy + Clone) associated with the glyphs produced by this style. Use TextStyle::with_user_data to include it.
  11. Classify characters with CharacterData

    master

    The CharacterData struct provides metadata about a character to assist in layout and rasterization decisions. You can generate this data using CharacterData::classify(c, index), where index is the glyph index in the font.

    CharacterData tracks:

    • is_whitespace(): True if the character is ASCII whitespace ( , `

    , , `, etc.).

    • is_control(): True if the character is a control character.
    • is_missing(): True if the character is missing from the associated font (index is 0).
    • rasterize(): A heuristic that returns true if the character should be rasterized. It returns false for whitespace, control characters, or missing glyphs.