shaping: fall back to an installed monospace face when the generic misses #370

Merged
natsukium merged 2 commits from client/issue-362-default-font into main 2026-09-23 14:08:36 +09:00
Owner

What

A default-config felis-client now opens on Debian and Ubuntu. Before, it failed with Font(NoFont) there. When fontdb's monospace generic names a family that is not installed, felis tries a fixed list (DejaVu Sans Mono, Liberation Mono, Noto Sans Mono, Ubuntu Mono, Menlo, Consolas). If none of those is installed, it takes the first installed fixed-pitch family by name. The bold, italic and bold-italic faces come from whichever family was picked. NoFont now happens only when no fixed-pitch face is installed at all.

The release smoke no longer needs its font.family workaround. install.md no longer asks you to name a font. fontconfig is removed from nix/package.nix because nothing links or dlopens it.

Why

fontdb reads fontconfig's XML itself, but it keeps only the first preferred family of the last monospace alias it parses. On Ubuntu 24.04 that alias is in 69-unifont.conf and prefers FreeMono, which the host does not have. So family_name(Monospace) == "FreeMono" and the lookup finds nothing, while fc-match monospace finds DejaVu Sans Mono. I confirmed this with a standalone fontdb 0.24 probe run in the container. The rejected alternatives (fc-match, linking libfontconfig) are recorded in text-shaping.md.

How verified

  • Built nix build .#felis-dist from this branch. It passes the closure classifier without fontconfig.
  • Ran the unmodified tools/release/linux-archive-smoke.sh against that archive in ubuntu:24.04, with the font.family line removed. Step c opened a real Xvfb window with the default config, logged the fallback from FreeMono to DejaVu Sans Mono, and passed the frontend smoke (painted frame, 143 colors).
  • New unit tests in felis-shaping:
    • an empty database gives NoFont;
    • with the pinned font dir and the generic set to FreeMono, felis picks an installed fixed-pitch regular face, and bold is a real distinct face.
  • just check passes.

Closes #362

## What A default-config `felis-client` now opens on Debian and Ubuntu. Before, it failed with `Font(NoFont)` there. When fontdb's `monospace` generic names a family that is not installed, felis tries a fixed list (DejaVu Sans Mono, Liberation Mono, Noto Sans Mono, Ubuntu Mono, Menlo, Consolas). If none of those is installed, it takes the first installed fixed-pitch family by name. The bold, italic and bold-italic faces come from whichever family was picked. `NoFont` now happens only when no fixed-pitch face is installed at all. The release smoke no longer needs its `font.family` workaround. install.md no longer asks you to name a font. `fontconfig` is removed from `nix/package.nix` because nothing links or dlopens it. ## Why fontdb reads fontconfig's XML itself, but it keeps only the first preferred family of the *last* `monospace` alias it parses. On Ubuntu 24.04 that alias is in `69-unifont.conf` and prefers `FreeMono`, which the host does not have. So `family_name(Monospace) == "FreeMono"` and the lookup finds nothing, while `fc-match monospace` finds DejaVu Sans Mono. I confirmed this with a standalone fontdb 0.24 probe run in the container. The rejected alternatives (`fc-match`, linking libfontconfig) are recorded in text-shaping.md. ## How verified - Built `nix build .#felis-dist` from this branch. It passes the closure classifier without fontconfig. - Ran the unmodified `tools/release/linux-archive-smoke.sh` against that archive in `ubuntu:24.04`, with the `font.family` line removed. Step c opened a real Xvfb window with the default config, logged the fallback from `FreeMono` to `DejaVu Sans Mono`, and passed the frontend smoke (painted frame, 143 colors). - New unit tests in felis-shaping: - an empty database gives `NoFont`; - with the pinned font dir and the generic set to `FreeMono`, felis picks an installed fixed-pitch regular face, and bold is a real distinct face. - `just check` passes. Closes #362
A default-config felis-client failed with NoFont on Debian and Ubuntu.
fontdb parses fontconfig's XML itself but keeps only the first preferred
family of the last `monospace` alias it reads; Debian's 69-unifont.conf
is that alias and prefers FreeMono, so the generic names a family the
host need not install even though `fc-match monospace` finds DejaVu.

When the generic resolves to nothing, felis now tries a fixed list of
common monospace families and then the first installed fixed-pitch
family by name, and the styled faces derive from whichever family won
rather than from the unresolved generic. NoFont remains the error only
when no fixed-pitch face is installed. Shelling out to fc-match was
rejected because a host with only fontconfig-config has no fc-match,
and linking libfontconfig would put back the C dependency fontdb's
parser avoids and the relocatable archive would have to take from the
host.

The release smoke drops its `font.family` workaround so it proves the
default path on stock Ubuntu, and install.md no longer asks the user
to name a face.

Closes #362

Assisted-by: Claude Code
nix: drop fontconfig from the package inputs
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Failing after 2m3s
darwin / detect relevant changes (pull_request) Successful in 3s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m5s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m39s
pr / wire schema is compatible with the base (pull_request) Successful in 11s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 2m1s
darwin / publish felis (aarch64-darwin) (pull_request) Has been skipped
pr / nix flake check (pull_request) Successful in 1m3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m16s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo nextest (Windows) (pull_request) Successful in 3m58s
windows / frontend smoke (Windows) (pull_request) Successful in 1m45s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 10s
fc97523007
No felis binary links or dlopens libfontconfig: fontdb reads the
fontconfig XML in pure Rust. Keeping it in buildInputs and on the
client's LD_LIBRARY_PATH only widened the closure and suggested a
runtime dependency that does not exist.

Assisted-by: Claude Code
natsukium deleted branch client/issue-362-default-font 2026-09-23 14:08:36 +09:00
Commenting is not possible because the repository is archived.
No description provided.