Skip to content

Add font fallback support for missing glyphs - #677

Open
MingqiZhang0 wants to merge 2 commits into
focus-editor:mainfrom
MingqiZhang0:fix/cjk-font-fallback
Open

Add font fallback support for missing glyphs#677
MingqiZhang0 wants to merge 2 commits into
focus-editor:mainfrom
MingqiZhang0:fix/cjk-font-fallback

Conversation

@MingqiZhang0

@MingqiZhang0 MingqiZhang0 commented Aug 9, 2026

Copy link
Copy Markdown

Summary

Adds configurable font fallback support to Focus for glyphs that are not available in the selected primary font, covering both editor text and UI text.

Previously, when the configured font did not contain a glyph, Focus could display a replacement glyph such as instead. This was especially noticeable with CJK text in source files, filenames, and directory paths when using fonts without CJK coverage.

With this change, Focus keeps the configured primary font unchanged and searches an ordered fallback font stack only when a glyph is missing.

Fallbacks can be configured independently for editor and UI fonts through font_fallbacks and font_ui_fallbacks.

The implementation is generic to missing glyphs and is not limited to CJK text.

Implementation

Simp.Dynamic_Font now supports a fallback font stack.

When a glyph is requested:

  1. Focus first checks the primary font.
  2. If the glyph is missing, fallback font faces are searched in order.
  3. The glyph records the font face it came from so it is loaded and rendered using the correct face.
  4. If no configured font contains the glyph, Focus keeps its existing replacement-glyph behavior.

Kerning is only applied when two adjacent glyphs come from the same font face.

The fallback stack is also part of the dynamic-font cache identity, so fonts using different ordered fallback lists do not incorrectly reuse the same cached Dynamic_Font.

Configuration

Editor and UI fallback fonts can be configured independently:

[[style]]

[fonts]
font:              default
font_fallbacks:    <font1>, <font2>

font_ui:           default
font_ui_fallbacks: <font1>, <font2>
  • font_fallbacks controls fallback fonts for editor text.
  • font_ui_fallbacks controls fallback fonts for UI text.
  • Entries are comma-separated and searched in order.
  • The configured primary font always has priority.
  • If a fallback setting is omitted, Focus tries platform default fallback candidates.
  • An explicitly empty value disables fallback fonts for that font, including platform defaults.
  • Duplicate fallback paths are ignored.
  • Up to 8 fallback fonts can be configured.

Windows, macOS, and Linux provide platform-specific default fallback candidates.

The default configuration file includes commented examples for both fallback settings.

Since this PR adds user-facing configuration options, the config version is bumped from 25 to 26. Existing configs are not given an explicit empty fallback setting during migration, so existing users retain the platform-default behavior.

Motivation

The issue originally showed up with CJK filenames and directory names.

The underlying paths were valid, but the selected UI font did not contain the required glyphs. Focus uses its own FreeType-based text rendering rather than relying on the operating system's automatic font fallback, so missing characters followed Focus's replacement-glyph path.

The same problem applies to editor text when the selected code font does not contain a requested character.

Handling fallback in the font renderer fixes the problem at the glyph level instead of adding special handling for filenames, paths, editor buffers, or individual languages.

It also preserves the appearance of the selected primary font: fallback fonts are only used for glyphs that the primary font does not contain.

Validation

The fallback implementation was exercised against the original CJK filename/path cases and a broader generated filename stress test.

  • 5/5 GUI regression scenarios passed
  • 1511 generated filename/path stress cases completed without crashes or unexpected errors
  • tested CJK filenames and directory names rendered correctly
  • configuration loading produced no warnings in the tested scenarios
  • primary-font glyphs remained unchanged when a fallback font was available
  • git diff --check passes

The current source changes were also reviewed for editor/UI fallback independence, config override behavior, explicit-empty handling, duplicate detection, fallback limits, cache identity, and cross-face kerning.

I currently do not have access to the Jai compiler, so the latest branch has not been freshly rebuilt locally.

Known limitations

This change provides font fallback, not full text shaping or universal Unicode support.

Characters that are not present in either the primary font or any configured fallback font still use Focus's existing replacement-glyph behavior.

Complex text shaping, combining sequences, emoji rendering, and East Asian display-width handling are outside the scope of this change.

This PR also does not change language tokenizers or identifier rules. Whether a character is considered valid source-code syntax is independent of whether its glyph can be rendered.

Development note

OpenAI Codex was used to assist with implementation, code migration, review, and test automation.

The resulting changes and reported test results were manually reviewed before submission.

Screenshot

Example of mixed CJK filenames and directory names rendered through the fallback font path:

Before

image

After

image image

@focus-editor

Copy link
Copy Markdown
Owner

Hi, sorry I don't have the capacity to review PRs yet, even though the contribution is clearly valuable. With AI I might never be able to review any PRs going forward because it takes less effort to create them now than it is to review them.

@MingqiZhang0

Copy link
Copy Markdown
Author

Fair enough, AI really changes GitHub

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