Skip to content

[Text] Glyph Outline Part 12/15 - FontVariations property, XAML and animation - #22185

Open
Gillibald wants to merge 5 commits into
pr4g/vvar-advancesfrom
pr4h/font-variations-user-api
Open

Gillibald wants to merge 5 commits into
pr4g/vvar-advancesfrom
pr4h/font-variations-user-api

Conversation

@Gillibald

@Gillibald Gillibald commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Part 12 of 15 of the glyph-outline / variable-font workstream. Stacked on Part 11 (pr4g/vvar-advances); please merge that one first. Parts 6 to 11 built variable-font support on GlyphTypeface; this part connects it to the properties, XAML and animations that application code actually uses.

What does the pull request do?

Makes variable fonts reachable without touching GlyphTypeface at all.

  • Typeface carries FontVariations, so a variation is part of what a typeface is rather than something applied afterwards. It arrives as a constructor overload, Typeface(family, style, weight, stretch, fontVariations), leaving the existing constructors untouched.
  • TextElement.FontVariationsProperty is an inherited attached property with GetFontVariations / SetFontVariations, so setting it on a container applies to the text below it. TextBlock, SelectableTextBlock, TextBox, TextPresenter, ContentPresenter, TemplatedControl and Inline all take part.
  • FontVariationSettings.Parse gives the type a XAML-friendly string form, so FontVariations="wght 700, wdth 87.5" works in markup.
  • FontVariationSettings.Interpolate, a registered animator and FontVariationSettingsTransition let a variation be animated or transitioned, per-axis, the way CSS animates font-variation-settings.
  • FormattedText and TextCharacters carry the variation through so text drawn outside the control layer varies too.
  • Normalized coordinates are quantized to the F2Dot14 grid the font binary actually stores.

What is the updated/expected behavior with this PR?

  • Setting FontVariations anywhere in the text property chain resolves through FontManager to a varied typeface, and inherits down like FontSize or FontWeight do.
  • Font fallback carries the variation with it: when text falls back to another family, the requested axis values are applied to the fallback font too if that font declares those axes.
  • Animation and transitions interpolate per axis. An axis present in one endpoint and absent in the other is treated as sitting at that font's default for the missing side, rather than snapping. Interpolating between infinite endpoints snaps to the nearer endpoint instead of producing a value the settings type would reject.
  • Parse accepts a comma-separated list of tag value pairs and round-trips with ToString. Unparseable input throws rather than silently producing an empty setting.
  • Coordinates are quantized to F2Dot14 before they become a cache key. Two requests differing by less than the font's own representable step therefore resolve to one typeface instead of two entries that render identically.
  • Text with no FontVariations set behaves exactly as before, and static fonts ignore the property.

How was the solution implemented (if it's not obvious)?

  • Explicit settings win per axis over a named instance, and an axis mentioned at its default value unsets any inherited value for that axis. This mirrors CSS, where font-weight maps onto wght unless font-variation-settings says otherwise.
  • Quantization happens before caching rather than after, so the cache key and the font's own resolution agree.

Checklist

Breaking changes

None. Typeface gains a five-parameter constructor overload rather than an optional parameter on the existing four-parameter one, so no signature is removed and assemblies compiled against an earlier release keep working. Everything else is additive, and text that does not set FontVariations resolves the same typeface it did before.

Obsoletions / Deprecations

None.

Fixed issues

None.


🤖 Generated with Claude Code

@Gillibald Gillibald changed the title pr4h/font variations user api [Text] Glyph Outline Part 12/15 - FontVariations property, XAML and animation Sep 8, 2026
@Gillibald Gillibald added enhancement area-textprocessing api-needs-review The PR adds new public APIs that should be reviewed. labels Sep 8, 2026
Typeface is the value every text property funnels into before font
resolution, so variable-font axis values belong on it - same pattern as
weight and stretch, and the only way a single settings value can flow
from a style through layout to the renderer.

- both ctors take an optional FontVariationSettings; Empty is coerced to
  null at construction so equality, hashing and cache keys never
  distinguish the two "design defaults" spellings
- equality and hash include the settings (structural, cached hash)
- Normalize() and the FormattedText per-run updaters (SetFontFamily /
  SetFontWeight / SetFontStyle) carry the settings over when rebuilding
FontManager is the single seam every Typeface passes through on its way
to a GlyphTypeface, so the user-space settings are applied there - once,
at the end, whichever resolution path produced the base typeface.

- the resolution body moves to a private TryResolveGlyphTypeface (the
  default-family recursions target it directly); the public method
  resolves, then binds the variations via GlyphTypeface.WithVariations
- per-variation instances are cached on the resolved typeface, so
  repeated lookups with equal settings return the same instance (pinned
  by test against the embedded Inter Variable)
- font fallback carries the variations over: the matched fallback family
  renders at the same axis values the primary typeface requested, each
  font clamping to its own axes (CSS behavior)
TextElement.FontVariationsProperty is the user-facing entry point for
variable-font configuration, following the FontStretch pattern exactly:
an inherited attached property that every typeface bake site folds into
the Typeface it constructs.

- attached property on TextElement (inherits) + CLR accessors and
  Get/SetFontVariations statics
- AddOwner on TemplatedControl, ContentPresenter and TextBlock
- baked into the typeface at every control bake site: TextBlock,
  SelectableTextBlock, TextPresenter, TextBox line-height layouts, and
  Inline run properties
- property changes invalidate like the sibling font properties
  (TextBlock/TextPresenter shaping group, TextElement inline host)
- XAML attribute strings ("wght=700, wdth=85") convert through the
  static Parse the XAML compiler picks up - no TypeConverter needed
  (pinned by a Markup.Xaml test)
FontVariationSettings.Interpolate blends two settings the way CSS
font-variation-settings does: per-axis linear interpolation when both
endpoints set exactly the same axes (easing overshoot extrapolates),
discrete switch at the midpoint otherwise. Discrete is the only sound
fallback because a missing axis means "the font''s default", a value that
is not knowable in user space.

- FontVariationSettingsAnimator registered like the other value
  animators, so keyframe Animations on FontVariations work out of the box
- FontVariationSettingsTransition for the Transitions collection,
  following the TransformOperationsTransition pattern
- interpolation contract pinned at the value level (lerp, order
  independence, overshoot, discrete mismatches, null/Empty endpoints)
  and at the transition level through a test clock
An animated axis sweeps through arbitrary float user values; without
quantization every frame produced a fresh normalized position and a
fresh clone in the per-source variation cache, growing it without bound
across repeated sweeps.

Snap each coordinate to 1/16384 after avar correction. That is the
resolution the font binary itself stores - gvar, avar and the item
variation stores speak F2Dot14 - so no representable position is lost,
while a swept axis lands on at most 32769 distinct cache entries and
sub-resolution user values collapse to one clone (pinned by tests at the
position and clone-identity level).
@Gillibald
Gillibald force-pushed the pr4h/font-variations-user-api branch from 325a640 to 3407fe4 Compare September 8, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-needs-review The PR adds new public APIs that should be reviewed. area-textprocessing enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants