desktop: list only fonts actually installed, using a local() lookup - #239
Open
raymondginger2018-sudo wants to merge 1 commit into
Open
raymondginger2018-sudo wants to merge 1 commit into
raymondginger2018-sudo wants to merge 1 commit into
Conversation
…fonts.check()
The Appearance → Fonts picker is supposed to list only the families the machine
actually has, but the test behind it was `document.fonts.check()`, which answers
"can this text be rendered" rather than "is this family installed". An unmatched
family still renders through the fallback, so `check()` answered true for
everything and every candidate survived the filter.
Measured on Edge/WebView2 153.0.4234 — the engine Tauri uses on Windows, and the
one this picker has to work in — `check()` returned true for all 39 families
swept out of the Windows font registry, including the sentinel
`__Absent Font 12345__`. The picker therefore offered fonts the machine does not
have.
`local()` goes through font matching and answers the real question. In the same
engine it resolved the six families this machine has and rejected the rest
(Roboto, Helvetica, PingFang SC, …). Reconciliation against the Windows font
registry: 15/17 candidates agree by exact name, and the two remaining rows are
the registry labelling font *files* rather than families — `Cascadia Code
Regular` is the full name of the family `Cascadia Code`, and `Segoe UI Variable`
is the family name inside SegUIVar.ttf while only its optical sizes (`… Text`,
`… Display`) resolve as families. `local("Segoe UI Variable")` fails, so the
candidate list now carries `Segoe UI Variable Text`. No installed family is
wrongly filtered out.
The probe is asynchronous, so `isFontAvailable` and `availableFontCandidates`
now return promises, and the settings component holds the result in state rather
than a `useMemo`. A probe loads a throwaway face and never registers it, so it
leaves `document.fonts` untouched. An engine without `FontFace`, or one that
refuses local lookups, reports nothing rather than claiming every family exists.
The suite is rebuilt around a stub faithful to the measured engine behaviour
(resolve for an installed family, NetworkError otherwise), including a
regression test that asserts `document.fonts.check` is never consulted.
Gates: tsc --noEmit, eslint ., vitest run (39 files / 267 tests), npm run build,
npm run build:web and cargo fmt --check all pass.
Refs HKUDS#155
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.
What this fixes
The Appearance → Fonts family picker is meant to list only the families the machine actually has. It does not: the presence test behind it is
document.fonts.check(), which answers "can this text be rendered?", not "is this family installed?". A family the machine lacks still renders through the fallback, socheck()says yes to everything and every candidate survives the filter.Measured on Edge/WebView2 153.0.4234 — the engine Tauri uses on Windows, and the one this picker has to work in:
document.fonts.check(\12px "…"`)` over 39 families swept from the Windows font registry__Absent Font 12345__new FontFace(name, \local("…")`).load()` over the 17 candidatesNetworkError) for the other 11Segoe UI Variable Text,Segoe UI,Cascadia Code,Consolas,Microsoft YaHei,Noto Sans SCSo the picker currently offers fonts the machine does not have, and the drop-down is identical on every machine.
The fix
local()goes through font matching and answers the real question: it resolves for an installed family and rejects for an absent one. It is the only practical option here —queryLocalFonts()enumerates the system list directly but needs a user gesture and a permission grant, so it cannot back a picker that is populated when the settings page opens.isFontAvailable/availableFontCandidatesbecome async, awaitingnew FontFace(name, \local("…")`).load()`; a failed load is the negative answer.useState+useEffect) instead of auseMemo. The probe runs once per mount, as before.document.fontsis left untouched.FontFace, or one that refuses local lookups, reports nothing rather than claiming every family exists.Segoe UI Variable→Segoe UI Variable Text: the bare name is not a family the system resolves (see below), so that candidate was dead.Reconciliation with the OS font registry
15 of the 17 candidates agree by exact name. The two remaining rows are the registry labelling font files rather than families, confirmed independently by reading the
nametable inside the font files:Cascadia Code Regularis the registry label forCascadiaCode.ttf, whose family (name ID 1) isCascadia Code— whichlocal()resolves.SegUIVar.ttfcarries the family nameSegoe UI Variable, but the system exposes only its optical sizes as families:local("Segoe UI Variable")fails,local("Segoe UI Variable Text")andlocal("Segoe UI Variable Display")resolve. Hence the candidate change.No installed family is wrongly filtered out, and the false positive the canvas-measurement approach produced (
Roboto, installed nowhere on this machine) does not occur.Tests
The suite is rebuilt around a stub faithful to the measured engine behaviour — a
local()lookup resolves for an installed family and rejects withNetworkErrorotherwise. The old stub encoded the assumption thatcheck()reports absence, which is exactly how a picker that listed every family stayed green. Added a regression test that assertsdocument.fonts.checkis never consulted, plus coverage for the no-FontFaceand refused-lookup paths. 15 tests in the file; full suite 39 files / 267 tests.Gates
tsc --noEmit,eslint .,vitest run,npm run buildandnpm run build:weball pass;cargo fmt --checkpasses. Not run:setup:sidecar/build:sidecar/audit:licenses(no sidecar or Python content in the diff; the sidecar env is absent and needs a network install) andcargo clippy/cargo test --all-targets(no Rust content in the diff). Not verified: behaviour inside a real Tauri window, and font detection on macOS/Linux — the evidence above is from the Windows WebView engine.Refs #155.