Skip to content

fix(fonts): resolve declared custom and Google fonts in text rendering#100

Open
LeadcodeDev wants to merge 1 commit into
mainfrom
fix/custom-fonts-text
Open

fix(fonts): resolve declared custom and Google fonts in text rendering#100
LeadcodeDev wants to merge 1 commit into
mainfrom
fix/custom-fonts-text

Conversation

@LeadcodeDev

@LeadcodeDev LeadcodeDev commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Problem

Fonts declared in a scenario's fonts array (local or source: "google") never actually rendered. They were downloaded and passed to Skia via FontMgr::new_from_data, but that returns a detached Typeface that match_family_style can't find — Skia's default FontMgr only name-resolves installed system fonts. Every font-family pointing at a declared font silently fell back to Helvetica/Arial.

Concretely: a scenario declaring { "family": "Anton", "source": "google" } and using font-family: "Anton" rendered in a generic system sans-serif — the download succeeded, the glyphs were wrong.

Fix

  • On load, keep each custom font's raw bytes in a global family -> bytes registry (first registration wins).
  • typeface_with_fallback resolves custom families from the registry first, building the Typeface from the bytes and caching it per render thread. The FontMgr is thread_local, so registering only on the main thread never reached the rayon render workers — resolving from shared bytes does.

Before / after

Same scenario (a yellow block with font-family: "Anton"):

  • Before: generic sans-serif (fallback)
  • After: Anton condensed grotesque (the declared font)

Verification

  • cargo test -p rustmotion-core green; new tests: registry stores-first-wins + serves bytes; a registered family resolves to its own typeface over the system fallback (uses the cached Anton TTF when present, skips on a cold cache — the render is the visual counterpart).
  • cargo fmt clean, clippy 0 warnings.

Scope / follow-up

One weight per custom family via this path (sufficient for accent/display faces). Multi-weight custom families (e.g. Inter 400+700 both declared) resolve to the first registered weight — a follow-up if needed.

Custom/Google fonts declared in a scenario's `fonts` were downloaded and
handed to Skia via `FontMgr::new_from_data`, but that call returns a
detached Typeface and never exposes it to `match_family_style` — Skia's
default FontMgr only name-resolves installed system fonts. So every
`font-family` pointing at a declared font silently rendered in the
Helvetica/Arial fallback (e.g. Anton came out as a generic sans-serif).

Keep the raw bytes in a global family->bytes registry when loading, and
have `typeface_with_fallback` resolve custom families from it first —
building and caching the Typeface per render thread (the FontMgr is
thread-local, so system-thread registration alone never reached the
rayon render workers). One weight per custom family via this path;
multi-weight custom families are follow-up.

Tests: registry stores first-wins + serves bytes; a registered family
resolves to its own typeface over the system fallback (uses the cached
Anton TTF when present, skips on a cold cache).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant