Title text: fontconfig fallback for codepoints outside the primary font#97
Open
nikicat wants to merge 1 commit into
Open
Title text: fontconfig fallback for codepoints outside the primary font#97nikicat wants to merge 1 commit into
nikicat wants to merge 1 commit into
Conversation
The ab_glyph and skrifa title renderers each load a single primary
font (whatever fc-match returns for the GNOME titlebar-font setting,
defaulting to Cantarell). Any codepoint not in that one font -- emoji,
CJK, most symbol ranges -- silently rendered as the same .notdef
glyph. On GNOME/Mutter Wayland (which forces CSD), every titlebar was
affected: a title like 'hello 😀 你好 ★' rendered with three identical
hollow boxes.
This adds a shared font_fallback module that, for each title char not
covered by the primary or any already-loaded face, asks fc-match for
the smallest font with that codepoint in its charset, mmaps the file
once, and caches it. Both backends now parse the primary + fallbacks
each render and pick the first face that has a glyph for each char.
Line metrics still come from the primary so fallback faces with wild
ascent/descent don't grow the bar; per-glyph advance comes from the
covering face. Kerning is only applied within the same face.
The bug was verified by rendering two distinct missing codepoints
('★' U+2605 and '█' U+2588, both absent from Cantarell) on the
pre-fix code path -- the resulting pixmaps were byte-identical (both
.notdef). The regression tests assert that a fallback face is loaded
for a known-missing codepoint, which is the structural invariant.
crossfont has the same single-face limitation but a different code
shape (it owns the rasterizer); leaving as a follow-up.
Refs raphamorim/rio#1622
Refs PolyMeilex#96
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #96.
Problem
Both the
ab_glyphand the newskrifatitle renderers load a single primary font (whateverfc-matchreturns for the GNOMEtitlebar-fontpreference, defaulting to Cantarell) and lay out every codepoint with that one font. Any codepoint not in the primary's charset — emoji, CJK, most symbol ranges — silently becomes.notdef, so its advance is consumed but no real glyph is drawn.Most visible on GNOME/Mutter Wayland, which forces CSD on clients, so every titlebar Rio / Alacritty / winit-based app paints is affected. A title like
hello 😀 你好 ★renders with three identical hollow boxes.Reproduction (verified empirically): on the pre-fix code,
'★'(U+2605) and'█'(U+2588) — both absent from Cantarell — produce byte-identical pixmaps, because both are drawn as the same.notdefglyph.Fix
New
src/title/font_fallback.rs: for each title char not covered by the primary or any already-loaded face, runfc-match -f '%{file}' :charset=HEX, mmap the file once, and cache it (deduped by path; seen chars not re-queried). Discovery happens inupdate_title, not in render.Both backends now parse the primary + fallback faces each render and pick the first face that has a glyph for each char (
select/select_face). Layout rules:crossfonthas the same single-face limitation but a different code shape (it owns the rasterizer); leaving it as a follow-up.The previously-
warn!-level "Ignoring out of range pixel" log in theab_glyphrenderer is nowdebug!— it fires on the fallback path whenever a fallback glyph's outline extends past the primary's height bound, which is expected.Tests
Two regression tests per backend (in
ab_glyph_renderer.rsandskrifa_renderer.rs):loads_fallback_for_missing_codepoint— bypasses the system font, sets title'★', asserts a fallback face was loaded. This is the structural invariant; the bug was thatfallbackswould always be empty.no_fallback_for_ascii_title— sets title"hello", asserts no fallback lookups happened.Verified on the pre-fix code (separately) that two distinct missing codepoints produce byte-identical
.notdefpixmaps — the smoking gun. The committed tests use the structural assertion rather than a pixmap comparison because the latter depends on font specifics (a.notdefthat renders the hex codepoint inside the box would mask the bug).All four feature configurations build clean (
ab_glyph,skrifa,crossfont, default) and tests pass on the touched backends:(Pre-existing clippy lints in
crossfont_renderer.rsand a fewmismatched_lifetime_syntaxeswarnings inparts.rs/theme.rsare unrelated and untouched here.)Downstream
Downstream user-visible report: raphamorim/rio#1622