Skip to content

feat: fontApplyFeatures, and letterSpacing on createLayout spans and fontShapeText - #70

Merged
sidorares merged 1 commit into
mainfrom
feat/text-features-letter-spacing
Sep 17, 2026
Merged

sidorares merged 1 commit into
mainfrom
feat/text-features-letter-spacing

Conversation

@sidorares

Copy link
Copy Markdown
Contributor

For sidorares/react-x11#588: tabular figures for a live readout, and spaced small caps. The ntk half is sidorares/ntk#357; react-x11 uses both once they are released.

fontApplyFeatures(font, features)

The font with OpenType features set by tag, through kCTFontFeatureSettingsAttribute with kCTFontOpenTypeFeatureTag — the sibling of fontApplyVariations:

  • an array turns each tag on: ['tnum'];
  • an object sets each tag to its value, true/false or a number for a feature that selects among alternates: { tnum: true, liga: false, salt: 2 }.

The settings replace any the font carries, the way a style names its features whole, and with nothing to set the font itself comes back — the same External — so a caller can apply it unconditionally. A non-font first argument is a TypeError, as for the other font verbs.

letterSpacing

Points added after every character, the last included — what kCTKernAttributeName does, and what CSS's letter-spacing long did; negative values tighten.

  • a createLayout span option;
  • an option to fontShapeText, as a third argument { letterSpacing }, so that a glyph run shaped alone and the paragraph it belongs to agree on every advance.

It says nothing about ligatures. CSS turns the optional ones off where letters are spaced, and a caller that wants that rule sets them through fontApplyFeatures on the spaced span — react-x11 will, to match ntk.

Tests

test/text-features.js, added to npm test: the system face's digits are proportional, one width under tnum in either form and still proportional under { tnum: false }; nothing to set answers the font itself; a non-font argument is a TypeError; a span's spacing adds three gaps to three characters, tightens when negative and spaces only its own span; fontShapeText spaces to the same width as createLayout, every advance carrying the gap, and without the option spaces nothing.

Mutation check, each change undone alone and rebuilt:

mutant caught by
an array of tags adds nothing tnum one width, the object form
false sets a feature on { tnum: false }
a span's letterSpacing ignored four spacing checks
fontShapeText's option ignored three fontShapeText checks
a copy made with nothing to set the two identity checks

npm test passes on macOS 15 with the verbs built from this branch.

…fontShapeText

For sidorares/react-x11#588: tabular figures for a live readout, and spaced
small caps.

fontApplyFeatures sets OpenType features by tag, the sibling of
fontApplyVariations. An array turns tags on, an object sets each tag to its
value, and with nothing to set the font itself comes back.

letterSpacing is points added after every character, the last included,
through kCTKernAttributeName: a createLayout span option, and an option to
fontShapeText so that a run shaped alone agrees with the paragraph.
@sidorares
sidorares merged commit d0a5811 into main Sep 17, 2026
4 checks passed
@sidorares
sidorares deleted the feat/text-features-letter-spacing branch September 17, 2026 05:40
sidorares added a commit that referenced this pull request Sep 17, 2026
…the fill colour (#71)

The system's icons by name, which createStatusItem's image and a menu
item's iconName already take, can now be drawn into a surface for a
renderer's own content, sidorares/react-x11#591.

ctxDrawSymbol draws the symbol fitted into a rect, centred, in the
current fill colour. The template's shape goes into a transparency layer
and the colour through it, so a symbol follows the CTM, the clip, the
global alpha and the blend mode as a glyph run does, over what is
already drawn. It answers false, drawing nothing, for a name the
catalogue does not know. symbolSize answers the symbol's size in points,
or null. Both take pointSize, weight, scale and variableValue, and run
on the calling thread in threaded mode, where they draw the same pixels.

The CSS weight mapping matchFont used is shared as FontWeightFromCss.
CI now runs test/text-features.js, which #70 added to the test script
only, and the new test/symbols.js.
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.

1 participant