Skip to content

Fonts: fractional sizes, variable font axes, OpenType features and letter spacing - #712

Merged
stanoddly merged 4 commits into
mainfrom
worktree-fonts-709
Nov 10, 2026
Merged

stanoddly merged 4 commits into
mainfrom
worktree-fonts-709

Conversation

@botoddly

@botoddly botoddly commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #709.

IFontSystem.Load takes a float size and three new optional settings:

Font score = fonts.Load("fonts/Inter-Variable.ttf", 11 * 1.33f, variations: [new FontVariation("wght", 600)], features: [new FontFeature("tnum")], letterSpacing: 0.5f);
  • Fractional sizes: from 1 to 65535 px, kept to 1/64 px by FreeType. A bitmap font still needs a matching strike.
  • Variable fonts: FontVariation(tag, value) sets any axis, such as weight 200 or 600 from one file. Values clamp to the axis's range. An axis the font lacks throws.
  • OpenType features: FontFeature(tag, value = 1), such as tnum for tabular figures or liga with 0 to turn ligatures off.
  • Letter spacing: extra pixels after each character. A letter with its combining marks, or a ligature, counts as one.

The settings are part of the font's cache key, because SDL_ttf keeps them per TTF_Font. A label gets them by taking a font loaded with them. Lists that differ only in order, or in an overridden tag, load the same Font. Font reports each setting, and Font.Size is now a float. A scaled font from Font.GetScaled (#711) is a TTF_CopyFont, which keeps the variations, features and spacing since XDL 3.4.20-12. Pixely only multiplies the copy's letter spacing by the scale. main's scaled-font tests skipped without an outline font, and now run on Cantarell.

They come from XDL's additions to SDL_ttf (stanoddly/XDL#29, with TTF_CopyFont copying them since stanoddly/XDL#30), bound in XdlSharp (stanoddly/XdlSharp#4, #5). This PR pins XdlSharp 3.4.20.4, which carries XDL 3.4.20-12. StubFontSystem checks the same arguments but not against a font file, and none of them changes its metrics.

Docs: docs/fonts.md covers each setting with examples, and docs/testing.md, AGENTS.md and a DECISIONS.md entry are updated.

Tests

FontSystemTests uses Cantarell VF 0.301 (OFL, notice in tests/Pixely.Tests/THIRD-PARTY-NOTICES.md). Its figures are proportional and it has a weight axis from 100 to 800.

  • 14.67 px measures narrower than 15 px.

  • With tnum, "1111.11" and "8888.88" measure the same. Without it they differ.

  • Weight 200 and 600 from one file: TTF_GetFontWeight reports each, and 600 covers more pixels when rendered.

  • Letter spacing of 2 px widens "Hello" by 10 px, and -0.5 px narrows it.

  • Errors: an unknown axis, variations on a bitmap font, a fractional bitmap size, invalid sizes, spacing, tags and values.

  • Caching: settings, normalized lists and release.

  • A scaled font keeps weight 600 and tnum, and its spacing doubles at a scale of 2. This test fails on XDL 3.4.20-11, where it gets weight 400 instead of 600.

  • Scaled fonts at a fractional loaded size, such as 14.67 px: GetScaled(1) returns the loaded font, a scaled font releases with it, and sizes below 1 px share the 1 px face.

Scaled-font fixes from the review

These also cover behavior from #711:

  • Scaled fonts are cached by a stored size in 64ths, so releasing one never misses its cache entry and leaks the SDL font.
  • Sizes round half away from zero, as SDL_ttf does. A size halfway between two 64ths no longer makes a second face at the loaded size.
  • GetScaled throws ObjectDisposedException for a released font, where it read freed memory before. It also rejects a scaled size above 65535 px.
  • A second Dispose closes nothing, and keeps a font loaded again since in the cache.

Locally, Pixely.Tests, Pixely.Testing.Tests and Pixely.Ui.Tests pass against a local pack with XDL 3.4.20-12's libXDL3_ttf.so. CI needs XdlSharp 3.4.20.4 on nuget.org, so this PR stays a draft until it is listed.

@botoddly
botoddly marked this pull request as draft November 10, 2026 15:08
… scaled fonts only rescale the letter spacing
…nd guard released fonts and out-of-range scales
@stanoddly
stanoddly marked this pull request as ready for review November 10, 2026 15:22
@stanoddly
stanoddly marked this pull request as draft November 10, 2026 15:22
@botoddly
botoddly marked this pull request as ready for review November 10, 2026 15:51
@stanoddly
stanoddly merged commit 2cad191 into main Nov 10, 2026
5 of 8 checks passed
@stanoddly
stanoddly deleted the worktree-fonts-709 branch November 10, 2026 16:19
@github-actions

Copy link
Copy Markdown

📦 Development package Pixely 0.0.166 is available.

Workflow run (attempt 1)

@github-actions

Copy link
Copy Markdown

✅ Development package validation passed on all platforms.

Workflow run (attempt 1)

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.

Fonts: fractional sizes, variable weights, tabular figures and letter spacing

2 participants