Font

:= {
    handle : Handle,
    base_size_value : F32,
    line_spacing_value : F32,
    fallback_index : U64,
    glyph_values : List(GlyphMetrics),
}

A font's handle and its metric snapshot, and raylib-compatible text measurement over them.

measure is the whole point: it answers the size a string will occupy at a given font size and letter spacing, following raylib's own rules, without calling the host. A layout pass can therefore run in a pure function, in a test, or in a package that does not depend on the platform.

A Font pairs an opaque native handle with the scalar metrics taken when it loaded: the atlas's base size, the extra line spacing, and one entry per rasterized glyph, sorted by codepoint. The metrics are ordinary data, so a package can read them and a test can write them down; the handle is opaque, so only the host can mint one. Loading a font is an effect and lives in the platform's Draw, which re-exports this type as Draw.Font.

base_size : Font -> F32

The pixel size this font's glyph atlas was rasterized at.

Drawing at this size is one atlas texel per screen pixel. Drawing much larger scales the atlas up rather than re-rasterizing, so load the font again at the size wanted instead.

line_spacing : Font -> F32

Extra vertical space between lines, on top of the drawn size. Adding it to the text size is the distance from one baseline to the next.

glyphs : Font -> List(GlyphMetrics)

Every glyph the font rasterized, with its advance and its box. This is the table measure walks; an app rarely needs it directly.

get_glyph_index : Font, U32 -> U64

Where a codepoint sits in glyphs, or the fallback glyph's index when the font has no glyph for it. Answers an index rather than a Try, so measurement stays total.

measure : Font, Measure -> Size

Measure a valid Roc string against this font's metric snapshot.

Pure: it reads the snapshot taken when the font loaded and never calls the host, so a layout pass can run in update!, in a helper, or in an expect. The platform's Text is the fuller interface built on it.

This follows raylib 6 MeasureTextEx: embedded NUL ends the input, newline advances by font size plus line spacing, missing codepoints use the fallback glyph, and spacing counts Unicode scalars.

stub : Font

Resource-free font value for pure tests.

The handle never resolves to a host resource, so every host path it reaches treats it as an invalid one: drawing falls back to raylib's built-in font, and the platform's Text.prepare! refuses it. Its metric snapshot is a fiction rather than a measurement -- no glyphs, no line spacing, and a base_size of 1 so that measure stays finite instead of dividing by zero. Put it in a model to reach the app's real update! from an expect. Do not use it to test drawing, layout, or resource lifetime.

Resource

Font.Resource :: # (opaque)

The native resource a loaded font holds.

Opaque: only the host mints one, so a font value can be copied and measured but a handle cannot be forged from an integer.

stub : Resource

The invalid token, as a resource-free handle. See Font.stub.

Handle : [DefaultFont, LoadedFont(Resource)]

Which font a value names: raylib's built-in one, or a loaded resource.

GlyphMetrics : {
    codepoint : U32,
    advance_x : F32,
    offset_x : F32,
    offset_y : F32,
    width : F32,
    height : F32,
}

Scalar metrics for one glyph, in the atlas's own units.

Measure : {
    text : Str,
    size : F32,
    spacing : F32,
}

What to measure: the string, the size to draw it at, and the extra space between scalars.

Size : {
    width : F32,
    height : F32,
}

Text measurement result, in the units the size was given in.