Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RFC 0013 — Variable TrueType resource profile

Document status: proposed research contract. Metadata, coordinate normalization, typed. Canonical source.

package admission and direct package-to-raster delivery have executable evidence. Direct API, CLI, generated Node/browser WASM, live stdio MCP and a linked C release-library consumer now share exact snapshot evidence. Hosted cross-platform comparison and the other format/policy exclusions remain open, so no published conformance-profile claim is made. The static profile remains the required baseline; this variable profile is separately negotiated.

Motivation

NUIF can preserve, shape and rasterize one exact static TrueType face, but the model already has an axis-coordinate map. Accepting a variable font by merely allowing an fvar table would be unsound: one coordinate selection affects glyph outlines, advances, global metrics and OpenType Layout feature variations. Host-default coordinates, float rounding or partial table support could therefore produce different layout and pixels from the same package.

This RFC defines the smallest candidate variable profile that can reuse the existing exact-resource, policy, shaping and outline pipeline without combining collections, CFF2, color glyphs or web-font transport into one capability.

Prior art and evidence

  • OpenType 1.9.1 defines variable-font user coordinates, 16.16 normalization, final 2.14 coordinates, avar remapping, delta interpolation and the table relationships among fvar, gvar, HVAR, VVAR, MVAR and STAT: https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview.
  • The OpenType file specification distinguishes font collections, TrueType and CFF/CFF2 outlines, variation tables, vector color, bitmap color and SVG glyph data: https://learn.microsoft.com/en-us/typography/opentype/spec/otff.
  • The fvar table defines ordered axes, min/default/max user coordinates and optional named instances: https://learn.microsoft.com/en-us/typography/opentype/spec/fvar.
  • HarfBuzz configures variation coordinates on the font before shaping and treats named instances like independent static faces: https://harfbuzz.github.io/fonts-and-faces-variable.html.
  • Fontations exposes the same coordinate model to both HarfRust ShaperInstance and Skrifa LocationRef, which makes a shared normalized coordinate vector possible in the Rust reference implementation. Library support is an implementation option, not an interoperability oracle.

Profile identity and composition

The candidate identifier is nuif-opentype-variable-truetype-single-0.

It composes with nuif-package-0 and the existing reference text/runtime pipeline. It does not change CPU render profile 0 or make variable fonts a required NUIF baseline. A host advertises this capability separately.

The selected input is one variable TrueType-outline sfnt face at face index zero. The profile rejects:

  • TTC/OTC collections and nonzero face indices;
  • CFF or CFF2 outlines;
  • COLR/CPAL, CBDT/CBLC, sbix, EBDT/EBLC and SVG glyph sources;
  • WOFF and WOFF2 containers;
  • avar version 2, VARC and multi-axis mappings until separately specified;
  • font-program execution and hinted-output conformance;
  • implicit platform fonts, implicit network resolution and implicit axis defaults supplied by a host UI.

Semantic asset contract

The resource digest identifies the exact original variable-font bytes. The font asset retains the existing stable AssetId, exact digest, face index, family names, Unicode coverage, global OpenType feature values and portability policy.

FontAsset.axes is the complete selected user-coordinate tuple:

  • it contains exactly one entry for every fvar axis and no unknown tag;
  • keys are the case-sensitive four-byte axis tags from the font;
  • each value is finite, lies within that axis’s declared minimum and maximum, and converts to OpenType 16.16 by the specification’s required conversion;
  • omission is invalid for this first profile, even when the intended value is the font default;
  • canonical map order does not define axis order; the fvar axis array does;
  • an fvar named instance is only descriptive provenance. Its name or index is never font or instance identity; the exact byte digest plus complete coordinate tuple is authoritative.

The asset sets the executable semantic field:

FontAsset.decoder_profile = nuif-opentype-variable-truetype-single-0

Its separate policy-evidence map records:

opentype.fs_type = 0xNNNN
license.expression = publisher-reviewed expression
license.embedding_review = approved

Future subsetting or static-instancing output is a derived resource with its own digest, derivation record and embedding review. The profile does not infer permission to create or redistribute a derived instance from fsType, a named instance or the ability to render the source.

Coordinate algorithm

Implementations must produce one ordered normalized coordinate vector before shaping, metrics or outline extraction:

  1. read axes in fvar order and validate each min/default/max relation;
  2. convert selected user coordinates to signed 16.16 exactly as OpenType 1.9.1 requires;
  3. apply the two-segment min/default/max normalization in 16.16;
  4. apply avar version 1 segment maps in axis order when present;
  5. clamp and convert the final values to signed 2.14 using the OpenType rule;
  6. record the ordered tag, user 16.16 value and normalized 2.14 value in the resolved run or its referenced evaluation record;
  7. pass that same normalized vector to shaping, metric evaluation and outline extraction.

A stage must not independently renormalize from a lower-precision float. Any disagreement among the parser, shaper and outline engine is a typed evaluation failure, not approximated fidelity.

Global OpenType feature settings are applied after the variable instance is configured so GSUB/GPOS FeatureVariations are selected at the same normalized position. Requested feature values remain explicit in the resolved run.

Metrics, outlines and fidelity

Intrinsic inline size uses shaped advances at the selected location. Baseline placement uses location-adjusted ascent; vertical profiles must additionally specify VVAR and vertical-origin behavior before claiming vertical text. Unhinted outlines use the same normalized location. MVAR, HVAR and applicable gvar phantom-point effects must not be partially ignored.

An exact binding is lossless only when the complete coordinate tuple, features, shaping, metrics and outlines all use the declared profile. Missing bytes or an unsupported variable table produce item-level unsupported and no text command. A declared replacement asset follows the existing substituted/approximated contract.

Candidate resource limits

The first implementation experiment must measure and may reduce these proposed ceilings before the profile can become executable:

  • 32 MiB encoded font bytes and 256 top-level tables, inherited from the static profile;
  • 16 axes and 256 named instances;
  • 256 avar segment records per axis;
  • 65,536 glyphs and 32,767 variation regions;
  • 64 global feature settings;
  • 65,536 codepoints per shaped text item;
  • implemented research preflight ceilings of 4,096 gvar shared tuples, 65,536 tuples, 4,194,304 explicit deltas, 65,535 item-variation subtables, 65,536 delta rows, 1,048,576 region references, 256 MVAR records, and 4,096 STAT values;
  • separate future ceilings for VVAR, shaping allocations, outline commands and total evaluation time.

These are admission candidates, not accepted portable limits. The experiment must show that one-over inputs fail before proportional allocation and that region/tuple products cannot overflow internal arithmetic.

Security and policy

The implementation must retain NUIF-owned sfnt range, table-order, packing and checksum checks ahead of parser interpretation. It must validate all variation table versions, offsets, counts, axis ordering, tuple lengths, region indices, delta runs and arithmetic ranges. Unknown major versions or required table relationships fail closed.

The reference path remains safe Rust and unhinted. Native rasterizer execution, TrueType instruction conformance and arbitrary downstream font-stack safety are outside this profile. Parser budgets do not replace process cancellation, sandboxing or host quotas.

Conformance experiment

The first promotion prerequisite is now automated by cargo xtask gate-i-font-metadata: one bounded four-axis TrueType fixture is decoded by the NUIF path and Skrifa, while a committed HarfBuzz 14.4.0 C-API capture independently checks axis metadata, named-instance count, and five final normalized vectors including non-identity avar results. This is a single-fixture metadata milestone, not completion of the experiment below. cargo xtask gate-i-font-shaping then delivers that exact vector to HarfRust and reproduces seven hb-shape cases, including a GSUB FeatureVariations boundary. Skrifa advances and unhinted outlines use the same vector and repeat exactly against HarfBuzz’s independently captured horizontal advances and draw callbacks. The single fixture has no active HVAR deltas or MVAR table, so this does not complete variable-metric conformance. A second isolated cargo xtask gate-i-font-metrics fixture applies nonzero HVAR deltas to three horizontal advances at four locations, including a valid truncated advance index map. HarfRust and Skrifa agree exactly with pinned HarfBuzz 14.4.0 observations. A third executable gate uses a reproducibly subsetted OFL-1.1 Roboto Flex fixture and matches MVAR-adjusted x-height, cap height, ascent, descent, and line gap against HarfBuzz’s public metric API at eight complete 13-axis locations. A fourth cargo xtask gate-i-font-security preflights the gvar, HVAR, MVAR and STAT graphs of five fixtures, rejects 38 checksum-repaired hostile mutations, and enforces six warmed allocation/time regression trials. Nine hostile cases cover representative packed gvar point/delta failures, exact axis run counts, glyph/component-plus-phantom bounds, and exclusion of non-OpenType 32-bit delta runs; a separate mutation proves explicit VVAR exclusion. This closes the first graph/budget/packed-data milestone, but not every encoding combination, VVAR semantics, or package/runtime requirements.

cargo xtask gate-i-font-corpus adds independently authored Noto Sans and Recursive OFL-1.1 subsets with exact source, registry, license, derivation and oracle identities. Eight locations across distinct 2- and 5-axis graphs agree with HarfBuzz on metadata, normalization, shaping, HVAR advances and MVAR global metrics. Seven unhinted outlines are byte-exact; one Recursive interior control coordinate differs by one 26.6 unit under identical path topology, so the corpus enforces that measured bound instead of making a false exactness claim. Both fixtures pass candidate asset validation.

cargo xtask gate-i-font-gvar-generated replaces the source fixture’s glyf, loca, and gvar tables in memory, rebuilds canonical packing and checksums, and sends every result through production whole-font admission. Sixteen valid cases cover one-/two-byte counts, 128-point runs, 64-delta runs, byte/word/zero forms, all/private/shared/repeated points, private precedence, multiple tuples, phantom indices and the 32,767 maximum packed-point count. Three malformed two-byte count cases reject with named defects. This supplies generated boundary coverage, not a claim of enumerating all byte strings.

cargo xtask gate-i-font-package separately proves that the exact OFL fixture bytes survive a resource-only deterministic package fixpoint and unrelated semantic edit, and that a digest-pinned linked descriptor resolves only through an explicit verifying resolver. Its candidate asset validator rejects stale axes, names, coverage and policy. The package dispatcher admits the typed asset only when the manifest requires the exact variable decoder capability.

cargo xtask gate-i-font-runtime exercises two authorized Noto Sans packages at default and interior locations. The exact digest and normalized coordinate record reach the shaped run; glyphs, HVAR advances and unhinted gvar paths match the committed HarfBuzz oracle, intrinsic layout uses those advances, and the deterministic CPU raster changes with the coordinates. Repeated snapshots are identical and exact item fidelity is lossless. This closes direct runtime delivery in the direct Rust surface.

cargo xtask gate-i-font-surfaces then executes that direct gate plus the real CLI process, generated Node and browser WebAssembly packages, the stdio MCP server and the experimental C ABI’s linked release-library consumer on POSIX. Each surface consumes the same interior-location package and returns the exact canonical hash, ordered coordinate record, layout diagnostics, scene fidelity and CPU raster digest. WASM, MCP and C also prove that use before capability authorization fails closed. The aggregate report is target/variable-font-surface-report.json. This satisfies the local cross-surface requirement; its raster digest remains platform-scoped until retained hosted reports are compared across operating systems.

The CI resource matrix runs the direct runtime gate on Linux x86-64, Windows x86-64 and macOS arm64. A dependent cargo xtask gate-i-resource-host-matrix job downloads the immutable artifacts from that workflow run and binds the aggregate to the full source revision. It requires portable semantics to agree across all twelve package, image, static font and variable-font reports while treating measured durations and allocator observations as platform-specific budget evidence. The complete variable-font runtime JSON—including fixture, trials, canonical hashes, coordinates, layout and RGBA digests—must agree without normalization. The gate writes target/resource-host-matrix-report.json. The comparator and workflow are implemented; profile promotion still requires a retained passing hosted artifact rather than a locally copied simulation of the three inputs.

Promotion from proposed to experimental requires all of the following:

  1. Two independent metadata/normalization paths. Compare axis definitions, named instances and final 2.14 vectors from the Rust implementation with a pinned external HarfBuzz/FreeType or browser-derived oracle.
  2. Shaping oracle. At default, min, max, named and at least two non-named interior positions, compare glyph IDs, scalar-index clusters, advances and offsets with pinned hb-shape output. Include a FeatureVariations fixture.
  3. Outline oracle. Compare normalized unhinted paths or bounded geometric deltas at the same locations with a second implementation.
  4. Metric coherence. Prove layout and raster use the same location-adjusted advances and ascent; exercise HVAR and MVAR, and reject VVAR claims until a vertical profile exists.
  5. Package behavior. Preserve exact bytes through package fixpoint and an unrelated semantic edit; compare embedded and explicit digest-pinned linked resolution; reject stale axis metadata and forbidden portability outcomes.
  6. Metamorphic checks. Default coordinates reproduce the variable font’s default instance, identical tuples repeat exactly, map insertion order is irrelevant, and changing one axis leaves all other recorded coordinates unchanged.
  7. Negative corpus. Cover malformed fvar, avar, gvar, HVAR, VVAR, MVAR and STAT data; duplicate/missing/unknown axes; NaN, infinity and out-of-range coordinates; excessive axes/instances/regions; collections, CFF2, color, bitmap, SVG and WOFF2 inputs.
  8. Resource evidence. Use only fixtures whose redistribution and test use are recorded. Retain exact tool versions, commands, reports and source revision.
  9. Cross-surface parity. Direct API, CLI, WASM, MCP and the experimental C ABI must produce the same canonical document hash, coordinate record, diagnostics and fidelity on a platform where their runtime gates execute. Native raster hashes remain platform-scoped until a retained matrix passes.
  10. Cross-host parity. Retained Linux x86-64, Windows x86-64 and macOS arm64 reports from one full source revision must pass the runtime contract and have byte-equivalent decoded JSON payloads. Native system rasterizers remain outside this portable CPU claim.

The gate report must distinguish exact external comparisons, internal metamorphic checks and implementation-specific allocation measurements.

Compatibility and migration

Existing static font assets have an empty axis map and remain valid only for nuif-opentype-static-single-0. A variable resource cannot be silently opened under the static profile. Documents already carrying the explicit static decoder field do not change when a host adds the optional variable capability.

FontAsset.axes encodes the complete user tuple and the explicit FontAsset.decoder_profile selects the decoder without treating policy evidence as semantic storage. Resolved runs retain the normalized coordinate record. The field was added during alpha; pre-field records decode it as empty and fail model validation instead of guessing a profile. They must be reimported from their exact resource bytes through a declared decoder profile; the generic canonical migration command intentionally cannot invent this value. The resulting document has a new canonical identity.

Alternatives rejected

  • One general OpenType capability: combines unrelated parsers, renderers, security budgets and fidelity claims.
  • Store only a named-instance index or name: indices and names are resource-local labels, not semantic identity.
  • Allow omitted axes to use host defaults: hides an evaluation input and makes model intent dependent on a font/UI implementation.
  • Store only normalized coordinates: loses authored user-space intent and makes resource replacement/migration hard to explain.
  • Normalize separately in layout and rendering: permits metric/outline divergence.
  • Pre-instance every variable font: may lose variation behavior, creates a new derived resource and requires separate license/subsetting evidence.
  • Treat parser acceptance as raster equivalence: ignores shaping, FeatureVariations, metric deltas, outline interpolation and platform raster behavior.

Unresolved review questions

  • Should the first profile reject all TrueType instructions at admission, or merely exclude hinted output while retaining instruction tables inertly?
  • Which rights-cleared fixture set covers HVAR, MVAR, avar remapping and FeatureVariations without depending on one font family?
  • Is a location-adjusted vertical-metrics subset worth including, or should VVAR remain a separate vertical-text capability?
  • Which external implementation can independently retain and reproduce the complete conformance vectors?