rusttype

repository·master·Indexed 20 days ago

https://github.com/redox-os/rusttype

A pure Rust font rendering library providing OpenType support, analytical rasterization, and GPU-optimized glyph caching. It serves as an alternative to FreeType, supporting .ttf and .otf font loading, horizontal layout with kerning, and a progressive transformation pipeline (Glyph → ScaledGlyph → PositionedGlyph) for rendering.

Tokens
4.2K
Snippets
14
Records
19
Agent score
71%

What's inside rusttype

  1. Overview of WenQuanYi Micro Hei font

    master

    WenQuanYi Micro Hei is a high-quality Sans-Serif (Hei, Gothic, or Dotum) CJK outline font family. It is designed to be extremely compact (~5M), making it suitable for handheld devices, embedded systems, or desktop use with a minimal memory footprint.

    Key features include:

    • CJK Coverage: Covers all unified CJK Han glyphs (GBK Hanzi) in the Unicode Standard 5.1 range (U+4E00-U+9FC3).
    • International Support: Includes support for Latin, Extended Latin, Hanguls, and Kanas.
    • Font Faces: The package contains two faces, Micro Hei and Micro Hei Mono, provided in a True-Type Collection (ttc) file.
    • Advanced Typesetting: Both faces include hinting and kerning instructions for Latin glyphs.
  2. Overview of RustType capabilities

    master

    RustType is a pure Rust alternative to libraries like FreeType, designed primarily for easy-to-use font rendering in applications like games.

    Core Capabilities:

    • Font Loading: Reads OpenType formatted fonts and collections (*.ttf and *.otf).
    • Glyph Management: Retrieves glyph shapes and common properties.
    • Layout: Supports horizontal layout using horizontal/vertical metrics and glyph-pair-specific kerning.
    • Rasterization: Uses an accurate analytical algorithm (not sampling-based) with sub-pixel positioning.
    • GPU Caching: The gpu_cache module manages a dynamic font cache in GPU memory to minimize texture uploads and keep draw calls low by storing glyphs in a single texture.

    Current Limitations:

    • No font hinting.
    • No support for ligatures.
    • No support for certain less common TrueType sub-formats.
    • No support for right-to-left (RTL) or vertical text layout.
  3. Run tests and examples in RustType

    master

    Heavier tests, benchmarks, and examples are located in the ./dev directory to prevent dev-dependency feature bleed in the main crate.

    To run all tests:

    cargo test --all --all-features

    To run a specific example from the dev package:

    cargo run --example <NAME> -p dev
    # Run all tests
    cargo test --all --all-features
    
    # Run a specific example
    cargo run --example <NAME> -p dev
  4. Get started with RustType font loading and rasterization

    master

    To begin using RustType, the primary entry point is the Font struct. You use Font to load font files, access individual fonts within a collection, and subsequently access their glyphs for layout and rasterization.

    For a complete implementation walkthrough, refer to the ascii.rs example provided in the repository, which demonstrates loading a font, rasterizing a string, and displaying it.

  5. How Points and Vectors interact

    master

    In rusttype, Point and Vector are distinct geometric primitives with specific legal operations:

    • Point: Represents a location in 2D space.
      • Subtracting one Point from another results in a Vector representing the offset between them.
      • Adding or subtracting a Vector from a Point results in a new Point.
    • Vector: Represents a direction and magnitude.
      • Adding or subtracting two Vectors results in a new Vector.
      • Multiplying or dividing a Vector by a scalar (f32 or f64) results in a scaled Vector.
      • Adding a Vector to a Point (or vice versa) results in a Point.

    This distinction ensures that operations like p1 - p0 correctly yield a displacement, while p0 + v correctly yields a new position.

    # use rusttype::*;
    # let p0 = point(0.0, 0.0);
    # let p1 = point(10.0, 10.0);
    # let t = 0.5;
    let interpolated_point = p0 + (p1 - p0) * t;
  6. How glyphs are transformed using ScaledGlyph and PositionedGlyph

    master

    RustType uses a progressive transformation pattern for glyphs to ensure that methods are only available when the necessary context (scale or position) is provided. This prevents errors like trying to draw a glyph without knowing its size or position.

    1. Glyph: The base type. It represents a raw glyph from a font with no inherent scale or position. You can only access its id() or its parent font().
    2. ScaledGlyph: Created by calling .scaled(scale) on a Glyph. It adds scaling context, allowing you to access h_metrics() (horizontal metrics) and exact_bounding_box().
    3. PositionedGlyph: Created by calling .positioned(point) on a ScaledGlyph. It adds positioning context, allowing you to call .draw() to rasterize the glyph or access its pixel_bounding_box().

    This chain ensures that you cannot attempt to rasterize a glyph until you have explicitly defined its scale and position.

    # use rusttype::*;
    # let glyph: Glyph<'static> = unimplemented!();
    // 1. Start with a raw Glyph
    let id = glyph.id();
    
    // 2. Scale it to get a ScaledGlyph
    let glyph = glyph.scaled(Scale::uniform(10.0));
    let h_metrics = glyph.h_metrics();
    
    // 3. Position it to get a PositionedGlyph
    let glyph = glyph.positioned(point(5.0, 3.0));
    
    // 4. Now you can draw it
    glyph.draw(|x, y, v| {
        // x, y: pixel coordinates relative to bounding box min
        // v: coverage (0.0 to 1.0)
    });
  7. Licensing for WenQuanYi Micro Hei

    master

    The WenQuanYi Micro Hei font is dual-licensed under:

    • Apache2.0
    • GPLv3 with font embedding exceptions

    For details regarding the embedding exception, refer to the GPLv3 documentation which allows embedding the font or unaltered portions of it into a document without causing the resulting document to be covered by the GNU General Public License.

  8. Load a Font from bytes or owned data

    master

    The Font type is the primary entry point for font management in rusttype. It can either reference existing byte slices (e.g., from include_bytes!) or own its data (e.g., a Vec<u8>).

    Loading Methods

    • From byte slices: Use try_from_bytes(bytes: &[u8]) to create a Font that references the provided data. Use try_from_bytes_and_index(bytes: &[u8], index: u32) if the data is a font collection and you need a specific font index.
    • From owned data: Use try_from_vec(data: Vec<u8>) to create a Font<'static> that owns its data. Use try_from_vec_and_index(data: Vec<u8>, index: u32) for specific indices in a collection.

    All loading methods return Option<Font>, returning None if the data is invalid.

    # use rusttype::Font;
    // Loading from a static byte slice
    let font_data: &[u8] = include_bytes!("path/to/font.ttf");
    let font: Font = Font::try_from_bytes(font_data).expect("Failed to load font");
    
    // Loading from an owned Vec
    let owned_data: Vec<u8> = std::fs::read("path/to/font.ttf").unwrap();
    let font_owned: Font<'static> = Font::try_from_vec(owned_data).expect("Failed to load font");
  9. Retrieve glyphs from a Font

    master

    To interact with specific characters, you can retrieve Glyph objects from a Font using Unicode code points or glyph IDs.

    • glyph<C: IntoGlyphId>(&self, id: C) -> Glyph<'font>: Returns a Glyph for a given identifier. Note that if a code point has no corresponding glyph, it maps to the ".notdef" glyph (ID 0).
    • glyphs_for<'a, I: Iterator>(&'a self, itr: I) -> GlyphIter<'a, 'font, I>: A convenience method that returns an iterator of Glyph objects for a given iterator of identifiers (like chars()).

    Warning: If you provide a GlyphId directly, it must be valid for that specific font; otherwise, the function will panic. Always prefer looking up glyphs via Unicode code points.

    // Using glyphs_for with a string
    let glyphs = font.glyphs_for("Hello".chars());
    
    // Using glyph for a single character
    let glyph = font.glyph('A');
  10. Rasterize glyphs using the draw method

    master

    To render a glyph, you must first transform it into a PositionedGlyph. The .draw() method uses an analytical algorithm to calculate pixel coverage. It iterates through the pixels of the glyph's bounding box in horizontal scanline order.

    For each pixel, the provided closure is called with:

    • x: The x-coordinate of the pixel relative to the min coordinates of the bounding box.
    • y: The y-coordinate of the pixel relative to the min coordinates of the bounding box.
    • v: The analytically calculated coverage of the pixel by the glyph shape (a value from 0.0 to 1.0).
    // Assuming 'glyph' is a PositionedGlyph
    glyph.draw(|x, y, v| {
        // Use x, y, and v to fill a buffer or draw to a screen
        // x and y are u32
        // v is f32
    });
  11. Get font metrics and scaling

    master

    rusttype provides several ways to query font dimensions and calculate appropriate scales.

    Vertical Metrics

    • v_metrics_unscaled(&self) -> VMetrics: Returns the raw ascent, descent, and line_gap shared by all glyphs in the font.
    • v_metrics(&self, scale: Scale) -> VMetrics: Returns the vertical metrics scaled to the provided Scale.

    Scaling and Sizing

    • scale_for_pixel_height(&self, height: f32) -> f32: Computes a scale factor required to make the font's total height (ascent minus descent) equal to the specified number of pixels.
    • units_per_em(&self) -> u16: Returns the units per EM square defined in the font.
    • glyph_count(&self) -> usize: Returns the total number of glyphs available in the font.
    // Calculate scale to make font 24 pixels tall
    let scale_factor = font.scale_for_pixel_height(24.0);
    let scale = Scale::uniform(scale_factor);
    
    // Get scaled vertical metrics
    let metrics = font.v_metrics(scale);
    println!("Ascent: {}, Descent: {}", metrics.ascent, metrics.descent);
  12. Layout text horizontally with `layout`

    master

    The layout method provides a high-level way to perform horizontal text layout, including kerning. It returns a LayoutIter which produces positioned glyphs.

    Usage Notes

    • Unicode Normalization: layout does not perform Unicode normalization. For composite characters (like ö represented as o + ¨), you should normalize the input string using a crate like unicode-normalization before passing it to layout to ensure the correct glyph is selected.
    • Control Characters: This method does not handle line breaks or other control characters; these should be handled by your application logic.

    Signature

    pub fn layout<'a, 's>(&'a self, s: &'s str, scale: Scale, start: Point<f32>) -> LayoutIter<'a, 'font, 's>

    # use rusttype::*;
    let (scale, start) = (Scale::uniform(0.5), Point::new(0.0, 0.0));
    let font: Font = unimplemented!();
    
    // Returns an iterator of positioned glyphs
    for glyph in font.layout("Hello World!", scale, start) {
        // Use glyph for rendering
    }