Skip to content

Fix font width matching on macOS#82

Closed
pierloh wants to merge 1 commit intoalacritty:masterfrom
pierloh:fix/macos-font-width-matching
Closed

Fix font width matching on macOS#82
pierloh wants to merge 1 commit intoalacritty:masterfrom
pierloh:fix/macos-font-width-matching

Conversation

@pierloh
Copy link
Copy Markdown

@pierloh pierloh commented Mar 14, 2026

Summary

  • Fix get_matching_face() selecting wrong font variant for families with width variants on macOS
  • Add diagnostic logging for font selection (variant enumeration + selected variant)

Problem

The font matcher only checked bold/italic symbolic traits and took the first match from CoreText's arbitrary enumeration order. For families with width variants (e.g. Monaspace Neon NF which has Regular, SemiWide, and Wide variants for each weight), this selected the wrong variant.

Example with Monaspace Neon NF (42 variants):

Role Before (wrong) After (correct)
Normal Wide Medium (width=0.200) Regular (width=0.000)
Bold ExtraBold (width=0.000) Bold (width=0.000)
Italic Wide Light Italic (width=0.200) Italic (width=0.000)
Bold Italic SemiWide Bold Italic (width=0.100) Bold Italic (width=0.000)

This caused cell width inflation (24.880px vs correct 19.840px) and visually wrong font rendering.

Fix

Replace the first-match approach in get_matching_face() with a scoring system that:

  1. Filters candidates by bold/italic match (same as before)
  2. Scores remaining candidates by width_penalty * 10.0 + weight_penalty
    • width_penalty: distance from normal width (0.0 in CoreText's normalized scale)
    • weight_penalty: distance from target weight (0.0 for normal, 0.4 for bold)
  3. Selects the lowest-scoring (best) match

This matches the behavior of FreeType/fontconfig (Linux) and DirectWrite (Windows), which already handle width correctly:

  • fontconfig: delegates to fc_font_sort() which considers width
  • DirectWrite: explicitly passes FontStretch::Normal

Diagnostic logging

On first encounter of a font family, logs all available variants with their normalized weight, width, and symbolic bold/italic traits. Then logs each selected variant with its advance width.

Visible with ALACRITTY_LOG=info (or ALACRITTY_LOG=debug):

Font family "Monaspace Neon NF" at 32.0pt (42 variants):
  Wide Medium                       weight=  0.200  width=  0.200  bold=false  italic=false
  Regular                           weight=  0.000  width=  0.000  bold=false  italic=false
  SemiWide Regular                  weight=  0.000  width=  0.100  bold=false  italic=false
  ...
  -> Selected: "Regular"  advance=19.840px
  -> Selected: "Bold"  advance=19.840px
  -> Selected: "Italic"  advance=19.840px
  -> Selected: "Bold Italic"  advance=19.840px

Platform scope

macOS only. The other backends already handle this correctly.

The font matcher only checked bold/italic symbolic traits, taking the
first match from CoreText's arbitrary enumeration order. For families
with width variants (e.g. Monaspace Neon NF has Regular, SemiWide, and
Wide variants), this could select the wrong variant -- "Wide Medium"
instead of "Regular".

Score candidates by width distance from normal (0.0) and weight
distance from target, preferring the closest match.

Also add diagnostic logging: on first encounter of a font family, log
all available variants with their weight/width/bold/italic traits, then
log each selected variant with its advance width. Visible with
ALACRITTY_LOG=info or ALACRITTY_LOG=debug.
@pierloh
Copy link
Copy Markdown
Author

pierloh commented Mar 14, 2026

Note on approach: DirectWrite handles this natively via get_first_matching_font(weight, FontStretch::Normal, slant), and fontconfig via fc_font_sort(). CoreText doesn't expose an equivalent single-call API for matching by weight+width+slant -- the core-text crate gives us descriptors_for_family() (all variants) and trait accessors, but no "match with width preference" function. Creating a CTFontDescriptor with explicit trait attributes and using CTFontDescriptorCreateMatchingFontDescriptor would be possible but isn't exposed by the crate. Hence the manual scoring approach.

@chrisduerr chrisduerr closed this Mar 14, 2026
@pierloh pierloh deleted the fix/macos-font-width-matching branch March 14, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants