{}OPF
DocsReferenceView source

Measured fonts and reproducible previews

For the starter set and delivery priorities, see the font roadmap.

The composition API accepts a textMeasurement provider. A provider resolves font faces and returns actual text widths; callers pass the same provider to pagination, editor geometry, SVG rendering, and PPTX export. Without one, the existing deterministic character-width estimate remains available.

The renderer's optional font registry uses Fontkit to shape text and measure glyph advances from local font bytes. It does not discover system fonts or fetch fonts. The Node helper loads the renderer's bundled Roboto and Roboto Mono faces:

js
import { loadBundledFontRegistry } from '@openpresentation/opf-render/fonts-node';
import { renderSvg, svgToPng } from '@openpresentation/opf-render';
import { paginatePresentation } from '@openpresentation/opf/pagination';
import { toPptx } from '@openpresentation/opf-pptx';

const registry = await loadBundledFontRegistry();
const options = { textMeasurement: registry.textMeasurement };
// Use design.fontScheme: 'roboto', or supply the document's actual font files.
const { presentation } = paginatePresentation(deck, options);
const svg = renderSvg(presentation, {
  ...options,
  embeddedFonts: registry.embeddedFonts,
});
const png = await svgToPng(svg, {
  fontFiles: registry.fontFiles,
  useBundledFonts: false,
  loadSystemFonts: false,
});
const pptx = await toPptx(presentation, options);

createFontRegistry from @openpresentation/opf-render/fonts accepts {data: Uint8Array, weight, italic?, family?, postscriptName?, license?} entries in Node or the browser. Weights are explicit, with 400 as the default. Supply each style that the document uses. Missing font families and unsupported glyphs fail with OPFFontError, including the source path where available. Collection fonts require a postscriptName selecting one face.

Aliases and fallback families are explicit choices:

js
const registry = createFontRegistry(faces, {
  aliases: { Aptos: 'Roboto', 'Aptos Display': 'Roboto' },
  fallbackFamily: 'Roboto',
});
console.log(registry.substitutions);
registry.clearSubstitutions(); // Start a fresh render's diagnostic collection.

An available exact family takes precedence over aliases. The registry resolves a requested weight to the closest supplied weight, reports the substitution, and makes the resolved style available to rendering. Missing italic/upright styles fail instead of synthesizing an unmeasured style. strictGlyphs: false is an explicit escape hatch for hosts with their own glyph-fallback policy; it is unsuitable for fidelity verification.

SVG embeds supplied fonts using data URIs and includes supplied license notices as metadata. The bundled loader carries the fonts' SIL Open Font License notices. For PNG/PDF, pass the same font files to the rasterizer; its native font loader does not depend on browser CSS font loading. In a browser, wait for document.fonts.ready before measuring or taking a screenshot. The editor playground loads and embeds bundled fonts and displays substitutions.

Office compatibility pack

loadOfficeFontRegistry from @openpresentation/opf-render/fonts-node supplies regular, bold, italic, and bold italic faces of Carlito, Caladea, Arimo, Tinos, Cousine, and Gelasio, plus the base Roboto pack. Package versions are pinned and each face carries its distribution's license notice. includeBaseFonts: false omits Roboto. Loading never installs fonts into the operating system or downloads fonts at render time.

js
const registry = await loadOfficeFontRegistry({
  substitutionPolicy: 'metric', // Default for this loader; no visual fallback.
});
registry.resolveFont({fontFamily: 'Calibri', fontWeight: 400});
// requestedFamily: Calibri, resolvedFamily: Carlito, compatibility: metric

createFontRegistry defaults to substitutionPolicy: 'none'. Policies are none, metric, and visual; visual permits both curated tiers. An explicit fallbackFamily is a separate, reported generic fallback. Aliases are explicit visual substitutions and never establish metric compatibility. resolveFont reports exact resolutions as well; substitutions only collects changes. Resolution records include requested/resolved weights, italic, source path, and supporting upstream information where available.

Requested familyBundled substituteCurrent automatic tier
CalibriCarlitoMetric intent, standard 400/700 styles
CambriaCaladeaFontconfig metric mapping; reference-version testing remains necessary
ArialArimoMetric, standard 400/700 styles
Times New RomanTinosMetric, standard 400/700 styles
Courier NewCousineMetric, standard 400/700 styles
GeorgiaGelasioVisual: optional ligatures changed measured widths
Calibri LightCarlitoVisual: the bundle has no Carlito Light face
Aptos / Aptos DisplayCarlito, unless Source Sans 3 is suppliedVisual; no Aptos metric claim

Upstream evidence: Carlito, Fontconfig mappings, Arimo, Tinos, Cousine, and Gelasio. Metric classification describes compatibility intent within the stated style scope, not universal identical output. Missing matching weights cannot silently qualify for the metric tier.

The exported FONT_COMPATIBILITY list also contains optional visual candidates and CJK families. Listing a candidate does not bundle it or imply complete character coverage. Liberation Sans Narrow is a separate legacy distribution with a different license history; it is not part of this bundle. Wingdings, Webdings, and Symbol require character mapping before substitution; an ordinary fallback fails with font-encoding-required. Missing math fonts require an explicit math-aware choice and fail with math-font-required instead of falling through to body text.

DrawingML tokens such as +mn-lt resolve through the registry's explicit themeFonts option before substitution. Supply concrete majorLatin, minorLatin, and, where used, majorEastAsia, minorEastAsia, majorComplexScript, or minorComplexScript families. Missing theme mappings fail. This helper does not yet extract theme font records or embedded fonts from imported PPTX files.

Measured results and experimental fonts

node --import ./scripts/register-local-opf.mjs scripts/test-office-fonts.mjs --system compares the bundle to reference fonts already installed in macOS's Supplemental directory. It does not redistribute reference fonts. The report records source-file hashes and individual shaped widths. Across four samples and four styles, Arimo/Arial, Tinos/Times New Roman, and Cousine/Courier New matched exactly on 48 runs. Gelasio/Georgia differed on ligature-containing runs, with a maximum difference of 2.0125%. Individual basic-Latin advances matched; disabling optional ligatures removed the tested difference. Until feature handling is consistent across outputs, the policy conservatively labels Gelasio approximate. Calibri and Cambria reference fonts were not available for this comparison.

Akasia is a real upstream project claiming Aptos compatibility across twelve styles using open-licensed donor outlines. Its upstream README was inspected, but OPF has not yet validated its conformance or bundled its files. EXPERIMENTAL_FONT_CANDIDATES records it separately. Its claims do not imply compatibility with Aptos Narrow or Aptos Display.

An original OPF font project is technically feasible: independently designed or suitably open-licensed glyph outlines can be fitted to target advance widths, placement, vertical metrics, and shaping behavior. A successful font needs a reproducible source build, provenance, style/coverage tests, visual review, and cross-renderer conformance. Matching bounding boxes alone is insufficient: OpenType horizontal metrics and glyph positioning jointly control text placement. Universal pixel identity across rasterizers is not the acceptance criterion; measured layout preservation over an explicit test matrix is.

Verification and remaining work

pnpm test:fonts checks that editor and SVG geometry match, every native PPTX text box has the same coordinates and measured line breaks, and export uses the resolved family. It writes artifacts to artifacts/fonts/. A real-browser check of the same Roboto run measured 324.032 pixels versus the font engine's 324.170 pixels at 25 pixels, a difference of 0.138 pixels. These are measured tolerances, not a promise of pixel identity.

PPTX currently records the resolved font family; it does not embed font binaries. PowerPoint still needs those fonts installed or may substitute them. Line height remains the shared 1.22 multiplier, rather than a complete ascent/descent model. Rich-text font overrides, mixed-script fallback and bidi layout, specialized payload internals, and native font embedding remain active fidelity work. Passing a width provider does not remove those limits.