Linux: a family name matching a fontconfig substring rule resolves silently to an unrelated face #187

Open
opened 2026-10-19 12:04:25 +09:00 by natsukium · 0 comments
Owner

Problem

On Linux, font.family names that are not installed can resolve to an unrelated face with no warning. The unknown-family check in crates/felis-shaping/src/provider/fontconfig.rs (substitutes_as_unknown) calls a name unknown only when fontconfig substitution treats it exactly as it treats a name no rule mentions. Stock fontconfig rules match on part of the family name, so a name that hits one of them counts as known even when no installed family carries it.

Observed on this host (NixOS, stock fontconfig rules), with font.family set to each name:

font.family Warned Primary drawn
Qwxyz yes default monospace
Qwxyz Nerd Font yes default monospace
Qwxyz Mono no DejaVu Sans Mono
JetBrainsMono Nerd Fnt (typo) no DejaVu Sans Mono
Noto Sans Qwxyz no DejaVu Sans (proportional)

fc-match gives the same faces, so felis follows fontconfig here. The result is still wrong for felis's purpose:

  • The most common typo goes unreported. Nerd Font family names usually contain Mono.
  • A name starting with Noto Sans makes a proportional face the primary, with no warning.

It is not known yet which rules are responsible; the contains-style tests in the stock config are the first suspects.

Constraint

docs/explanation/rendering/text-shaping.md "Linux faces" records that unknown families are detected by the config rules fontconfig applies, not by comparing names, so that an <alias> mapping a requested name elsewhere is honored. A fix must keep that: an explicit alias for the exact name still counts as known, while a generic rule that matched only part of the name does not.

Possible direction

Treat a name as known when an installed family matches it (fontconfig's comparison: case-insensitive, ignoring blanks), or when substitution put a family other than the generic ones (monospace, sans-serif, serif, and their expansions) ahead of where the unknown sentinel puts them. This needs a design, and the "Linux faces" record updated.

## Problem On Linux, `font.family` names that are not installed can resolve to an unrelated face with no warning. The unknown-family check in `crates/felis-shaping/src/provider/fontconfig.rs` (`substitutes_as_unknown`) calls a name unknown only when fontconfig substitution treats it exactly as it treats a name no rule mentions. Stock fontconfig rules match on part of the family name, so a name that hits one of them counts as known even when no installed family carries it. Observed on this host (NixOS, stock fontconfig rules), with `font.family` set to each name: | `font.family` | Warned | Primary drawn | | --- | --- | --- | | `Qwxyz` | yes | default monospace | | `Qwxyz Nerd Font` | yes | default monospace | | `Qwxyz Mono` | no | DejaVu Sans Mono | | `JetBrainsMono Nerd Fnt` (typo) | no | DejaVu Sans Mono | | `Noto Sans Qwxyz` | no | DejaVu Sans (proportional) | `fc-match` gives the same faces, so felis follows fontconfig here. The result is still wrong for felis's purpose: - The most common typo goes unreported. Nerd Font family names usually contain `Mono`. - A name starting with `Noto Sans` makes a proportional face the primary, with no warning. It is not known yet which rules are responsible; the `contains`-style tests in the stock config are the first suspects. ## Constraint docs/explanation/rendering/text-shaping.md "Linux faces" records that unknown families are detected by the config rules fontconfig applies, not by comparing names, so that an `<alias>` mapping a requested name elsewhere is honored. A fix must keep that: an explicit alias for the exact name still counts as known, while a generic rule that matched only part of the name does not. ## Possible direction Treat a name as known when an installed family matches it (fontconfig's comparison: case-insensitive, ignoring blanks), or when substitution put a family other than the generic ones (`monospace`, `sans-serif`, `serif`, and their expansions) ahead of where the unknown sentinel puts them. This needs a design, and the "Linux faces" record updated.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
felis-terminal/felis#187
No description provided.