Skip to content

fw/applib/graphics: fall back between base and extension fonts on glyph miss#1477

Open
phamson02 wants to merge 1 commit into
coredevices:mainfrom
phamson02:glyph-extension-fallback
Open

fw/applib/graphics: fall back between base and extension fonts on glyph miss#1477
phamson02 wants to merge 1 commit into
coredevices:mainfrom
phamson02:glyph-extension-fallback

Conversation

@phamson02

@phamson02 phamson02 commented Jun 10, 2026

Copy link
Copy Markdown

Problem

prv_font_res_for_codepoint() routes each codepoint to exactly one font: Latin-classified codepoints (<= U+02AF and the U+2000–U+2BFF symbols range) always go to the base font, while everything else goes to the language-pack extension font when one is loaded. If this routed font (e.g. base font) does not contain the glyph, the renderer falls straight back to the wildcard box (▯), even when the other font (e.g. extension font) contains a valid glyph.

This means that Latin-classified codepoints missing from the base fonts (e.g. Vietnamese ơ/ư at U+01A1/U+01B0) can never be supplied by a language pack. This has been a known issue in the Vietnamese Pebble community since at least 2016 (issue: MarSoft/pebble-firmware-utils#17) as language packs could not fix the missing ơ/ư.

I originally attempted to patch the base Gothic fonts to include these characters, but noticed that this fonts cover only about 360 of the roughly 3,650 codepoints this classification routes to them. With this fallback logic, any Latin-classified codepoint missing from the base font can instead be supplied by a community extension pack, which seems like a better long-term fix (Happy to discuss more on this if needed).

This also fixes the inverse issue: glyphs that exist only in the base font but are not Latin-classified (e.g. fi U+FB01 and π U+03C0, both present in GOTHIC_18) stop rendering as soon as any language pack is installed, because they are then routed to an extension font that does not contain them.

Fix

On a miss for the requested codepoint, try the other of base/extension before falling back to the wildcard.

Safety properties:

  • The extra lookup only runs on the miss path, and only when an extension is loaded (font_info->extended) — app fonts and pack-less systems see zero change.
  • The wildcard and space fallbacks still come exclusively from the base font, preserving the existing "extension pack may be deleted" guarantee.
  • Glyph cache entries are keyed by resource id, so base and extension lookups (including cached negative results) cannot collide.

Testing

  • New regression test test_text_resources__extension_miss_falls_back_to_base: fi (U+FB01) is present in GOTHIC_18 but absent from the Chinese language-pack fixture; with the extension installed, the lookup must return the base-font glyph. Verified the test fails without the fix (returns the 7 px wildcard instead of the 6 px ligature) and passes with it.
  • Existing suite passes, including the "missing in both fonts → wildcard" case (./pbl test -M ".*test_text_resources.*").
  • Firmware builds clean for qemu_emery.

🤖 Generated with Claude Code and manually edited by me

…ph miss

prv_font_res_for_codepoint() routes each codepoint to exactly one font:
latin-classified codepoints (<= U+02AF and the U+2000-U+2BFF symbols
range) always go to the base font, everything else to the language pack
extension when one is loaded. The routed font is final: if it lacks the
glyph, the renderer degrades straight to the wildcard box, even when the
other font has a perfectly good glyph.

This breaks in both directions:

- Latin-classified codepoints missing from the base fonts (e.g. the
  Vietnamese ơ/ư at U+01A1/U+01B0) can never be supplied by a language pack.
- Glyphs that exist only in the base font but are not latin-classified
  (e.g. fi U+FB01, π U+03C0) stop rendering as soon as any language pack
  is installed, because they are then routed to the extension font that
  does not contain them.

On a miss for the requested codepoint, try the other of base/extension
before falling back to the wildcard. The extra lookup only runs on the
miss path and only when an extension is loaded; the wildcard and space
fallbacks still come exclusively from the base font, in case the
extension pack has been deleted. Glyph cache entries are keyed by
resource id, so base and extension lookups (including cached negative
results) cannot collide.

Add a regression test: fi is present in GOTHIC_18 but not in the chinese
language pack fixture; with an extension installed the lookup must still
return the base font glyph instead of degrading to the wildcard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Son Pham <phamtienson02@gmail.com>
@ericmigi

Copy link
Copy Markdown
Collaborator

do you mind sharing some pictures of the watch rendering before and after your change? so we could visualize the change more easily (as non Vietnamese speakers 😉 )

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.

2 participants