/** The common Word families this package can stand in for. */ type WordDefaultFamily = 'Calibri' | 'Cambria' | 'Times New Roman' | 'Arial' | 'Courier New' | 'Century Gothic'; /** * One packaged font asset, as measured at packaging time. * * `byteLength` and `hash` are baked here and CI-verified against the shipped files, so a * fetched asset is content-checked without hashing at runtime. */ interface FontAssetManifestEntry { /** Asset filename under the package's `assets/` directory, e.g. `Carlito-Bold.ttf`. */ readonly file: string; /** Exact packaged size. A fetch returning any other length is rejected. */ readonly byteLength: number; /** `sha256:`-prefixed digest, re-derived and compared by the engine's admission path. */ readonly hash: string; } /** * Every font asset this package ships, in generator order. Drives both * `loadDefaultFonts` (which looks entries up by filename) and the packaging check that * keeps the shipped bytes honest. */ declare const FONT_ASSET_MANIFEST: readonly FontAssetManifestEntry[]; /** * `@docx-editor.dev/fonts` — metric-compatible substitutes for common Word faces. * * Word's own defaults (Calibri, Cambria, Times New Roman, Arial, Courier New) are proprietary * and cannot ship in an open package. What CAN ship are the faces built to MATCH THEIR METRICS: * matching advance widths for the glyphs they cover, though outlines differ slightly. * Scripts outside that coverage still need a suitable native or app-supplied font. * * Nothing loads until an app calls in. Importing this module fetches no bytes, and the editor * engine never calls it on its own. * * @example Serve the packaged substitutes on demand * ```ts * import { packagedFonts } from '@docx-editor.dev/fonts'; * * const editor = createDocxEditor({ document: bytes, fonts: packagedFonts() }); * ``` * * @example Load Word's five document defaults up front instead * ```ts * import { defaultFonts } from '@docx-editor.dev/fonts'; * * const fonts = await defaultFonts(); * const editor = createDocxEditor({ document: bytes, fonts }); * ``` * * @packageDocumentation * @public */ /** A concrete font face request, structurally identical to the editor contract's. */ interface DefaultFontFaceRequest { readonly family: string; readonly weight: number; readonly style: 'normal' | 'italic'; } /** A byte-backed source, structurally identical to the editor contract's `FontSource`. */ interface DefaultFontSource { readonly request: DefaultFontFaceRequest; readonly id: string; readonly bytes: Uint8Array; readonly hash: string; readonly faceIndex: number; } /** * One Word-name → substitute redirect, structurally identical to the editor contract's * `FontSourceSubstitution`. `from` is the proprietary face a document asks for, `to` is * the metric-compatible face this package actually ships. */ interface DefaultFontSubstitution { readonly from: DefaultFontFaceRequest; readonly to: DefaultFontFaceRequest; readonly lineMetrics?: { readonly heightEm: number; readonly baselineEm: number; }; } /** * One face that did not load. `family` is the Word name that was asked for, `file` the * packaged asset that failed, and `diagnostic` a human-readable cause — a missing * manifest entry, an HTTP status, a byte-length mismatch against the baked manifest, or * a thrown fetch error. * * Non-fatal by design: the surrounding fragment stays usable and the affected family * falls back to the engine's fixed measurement. */ interface DefaultFontLoadFailure { readonly family: string; readonly file: string; readonly diagnostic: string; } /** What `loadDefaultFonts` resolves to — composes as a `FontConfigurationFragment`. */ interface DefaultFontsFragment { readonly sources: readonly DefaultFontSource[]; readonly substitutions: readonly DefaultFontSubstitution[]; /** Faces that failed to load; the rest of the fragment is still usable. */ readonly failures: readonly DefaultFontLoadFailure[]; } /** * Options shared by {@link loadDefaultFonts} and {@link defaultFonts}. * All fields are optional, so `{}` loads * {@link WORD_DOCUMENT_DEFAULT_FAMILIES} over the global `fetch`. */ interface LoadDefaultFontsOptions { /** * Narrow or widen the families to load. The default is * {@link WORD_DOCUMENT_DEFAULT_FAMILIES}; pass {@link ALL_WORD_DEFAULT_FAMILIES} to * add the families this package substitutes for that Word does not apply by default. */ readonly families?: readonly WordDefaultFamily[]; /** Injectable for tests; defaults to global `fetch`. */ readonly fetcher?: typeof fetch; /** * Cancels outstanding reads in `loadDefaultFonts` and `defaultFonts`. Cancellation rejects with * `signal.reason`; it is not reported as one failure per face and does not call `onFailure`. */ readonly signal?: AbortSignal; } /** * Directory URL of the packaged font files this package serves. * * Headless exporters pass this to Core `createPackagedFileFetch` as `trustedRoot`. * The value comes from the same packaged face URLs the loaders fetch, so the trusted * directory matches Node, Bun, pnpm, Yarn PnP, and nested installs. * * In a browser bundle this resolves to an HTTP URL rather than a `file:` one. That is * why every consumer gates on `FONT_ASSET_ROOT.protocol === 'file:'`: HTTP fetches do * not use a trusted directory, and confining them to one would be meaningless. * * Deriving this MUST NOT throw. It runs at module scope, so a throw here is * uncatchable and takes down the whole bundle that imported this package rather than * degrading font loading. * * In Node, the `DOCX_EDITOR_FONT_ASSET_ROOT` environment variable relocates this * directory. Single-file bundles set it to a copy of the package's `assets/` directory * that ships beside the executable, and every packaged face is then read from there. * * @public */ declare const FONT_ASSET_ROOT: URL; /** * The families Word applies to a document by DEFAULT, and what * {@link LoadDefaultFontsOptions.families} falls back to. Frozen — treat it as a constant * rather than a mutable list to filter in place. * * This is the load-every-document set, so it stays as small as correctness allows: a * family here costs four faces on every load, whether or not the file names it. */ declare const WORD_DOCUMENT_DEFAULT_FAMILIES: readonly WordDefaultFamily[]; /** * Every Word family this package substitutes for, including the ones Word does not apply * by default. NOT the default for {@link LoadDefaultFontsOptions.families} — pass it * explicitly to load them all: * * ```ts * const fonts = await defaultFonts({ families: ALL_WORD_DEFAULT_FAMILIES }); * ``` * * {@link packagedFonts} covers the extra families on demand instead, so a document that * never names Century Gothic never pays for its assets — from these same bundled bytes, * with no network involved. `googleFonts()` covers them too, for an app already opted into * the catalog. */ declare const ALL_WORD_DEFAULT_FAMILIES: readonly WordDefaultFamily[]; /** * Load the packaged substitute faces for the given Word families * ({@link WORD_DOCUMENT_DEFAULT_FAMILIES} by default) and return a configuration * fragment: byte-backed sources for the SUBSTITUTE families plus the Word-name → * substitute substitution map, so a document naming "Calibri" resolves without the host * mapping anything. * * Only the requested families' assets are fetched, in parallel. A face that fails to * load appears in `failures` and the rest of the fragment stays usable — compose it * anyway and the missing face measures via the engine's fixed fallback. Caller cancellation is * different: the promise rejects with `signal.reason` and returns no partial failure report. */ declare function loadDefaultFonts(options?: LoadDefaultFontsOptions): Promise; /** * Compatibility stub for the removed page-wide font installer. * * @deprecated Does nothing and resolves to `0`. It does not fetch or register fonts. * Remove this call. Supply {@link defaultFonts} or {@link packagedFonts} through the * editor's `fonts` option for private font registration. */ declare function installDefaultFontFaces(_options?: LoadDefaultFontsOptions & { readonly document?: Document; readonly loaded?: readonly DefaultFontSource[]; }): Promise; /** * Load the default-font bytes and report failures for the editor's `fonts` prop. * * The editor registers these bytes under private aliases for measurement and paint. * This loader leaves public CSS family names unchanged, so native fonts remain available * when a packaged substitute lacks a script. * * Non-cancellation face failures are WARNED, not thrown: one unavailable face degrades that * family to fixed-width measurement rather than refusing the document. Pass `onFailure` to route * them somewhere other than the console. Caller cancellation rejects with `signal.reason` before * any per-face warnings or callbacks are produced. */ declare function defaultFonts(options?: LoadDefaultFontsOptions & { readonly onFailure?: (failure: DefaultFontLoadFailure) => void; }): Promise; /** * The TYPE-level half of the resolver mark, structurally identical to the editor * contract's `FontResolverMark`. * * A string key rather than a symbol precisely so the two unify without this package * importing anything from the engine: two `unique symbol` declarations in two packages are * two different types. Because they unify, `useFonts(packagedFonts())` typechecks against * a `FontOrigin` list that REQUIRES the mark, and a hand-written resolver that forgot * `defineFontResolver` does not. */ interface FontResolverMark { /** Always `true`. Set non-enumerably on the resolvers this package builds. */ readonly 'docx-editor.dev/font-resolver': true; } /** * One face an earlier origin can already paint, structurally identical to the editor * contract's `FontFaceRequest`. */ interface ResolvedFontFace { /** The family name, matched case-insensitively as Word matches font names. */ readonly family: string; /** CSS numeric weight; the packaged faces are 400 and 700. */ readonly weight: number; /** Whether this is the upright or the italic face. */ readonly style: 'normal' | 'italic'; } /** * The request both resolvers in this package take, structurally identical to the editor * contract's `FontResolutionRequest`. * * Named rather than written inline at each call site so a mismatch reports one line rather * than a four-line structural wall. * * @public */ interface FontOriginRequest { /** Families the document declares, already name-validated and capped by the engine. */ readonly families: readonly string[]; /** The face a run naming no font resolves to. The engine reports Calibri by default. */ readonly defaultFamily: string; /** * Cancels document-scoped resolution. `packagedFonts` rejects with `signal.reason` and does not * translate cancellation into per-face failures or `onFailure` callbacks. */ readonly signal?: AbortSignal; /** Faces an earlier origin in the same composition can already paint. */ readonly resolvedFaces?: readonly ResolvedFontFace[]; } /** * How {@link packagedFonts} behaves once a document hands it a family list. Every field is * optional; `packagedFonts()` with no options serves any of the six families a document * names, over the global `fetch`, warning to the console on failure. */ interface PackagedFontsOptions { /** * Narrow what may ever be loaded. Omitted, any of the six substituted families a * document names is fair game — {@link ALL_WORD_DEFAULT_FAMILIES}, not the smaller set * the eager loader defaults to. Set it to run against a shorter list. */ readonly allow?: readonly WordDefaultFamily[]; /** Injectable for tests; defaults to global `fetch`. */ readonly fetcher?: typeof fetch; /** * Non-cancellation per-face failures. Defaults to a console warning; pass a handler to route * them. Caller cancellation rejects the resolver without invoking this callback. */ readonly onFailure?: (failure: DefaultFontLoadFailure) => void; /** * Legacy registration option, retained for source compatibility. * * @deprecated Ignored. Font registration is always private to the editor. * Remove this option; `true` no longer enables page-wide registration. */ readonly install?: boolean; } /** * What {@link packagedFonts} returns: a marked resolver over the packaged substitutes. * * @public */ type PackagedFontsResolver = ((request: FontOriginRequest) => Promise) & FontResolverMark; /** What one {@link packagedFonts} resolver call produced. */ interface PackagedFontsFragment extends DefaultFontsFragment { /** Families this provider can serve, including faces not loaded by this request. */ readonly supportedFamilies?: readonly WordDefaultFamily[]; /** The Word families this call actually loaded, in {@link ALL_WORD_DEFAULT_FAMILIES} order. */ readonly families: readonly WordDefaultFamily[]; } /** * The packaged substitutes, served ON DEMAND: an editor-shaped font resolver that loads * the families a document turns out to name, plus its default face, rather than every * family this package ships. * * Same call shape as `googleFonts()` from `@docx-editor.dev/fonts/google`, so the two * compose by sitting next to each other rather than by being combined differently: * * ```ts * const fonts = useFonts(packagedFonts()); // bundled faces only * const fonts = useFonts(packagedFonts(), googleFonts()); // and the Google catalog * ``` * * Prefer this to {@link defaultFonts} unless you need the eager guarantee. `defaultFonts()` * loads all 20 faces of {@link WORD_DOCUMENT_DEFAULT_FAMILIES} — 7.4 MB — whichever * document opens, because it is called before there is a document to ask. This is called * AFTER the parse, so a file using only Times New Roman costs Liberation Serif plus the * four Carlito faces instead. * * A family loads when a document NAMES it, or when it is that document's DEFAULT face. The * default counts because a run that authors no font still has to be measured in one. The * engine reports Calibri as the default, so Carlito is a floor here: even a document * naming none of the six loads it. Narrow that with {@link PackagedFontsOptions.allow} if a * document's families are known in advance. * * What you trade for that is one reflow. The eager form settles before the first layout, so * the document paginates once; this form cannot know the families until the file is parsed, * so the document opens on the engine's fixed measurer and re-paginates when the faces * arrive. Nothing is fetched from a third party either way — the bytes are the ones inside * this package. * * The families are file-derived, so they are matched case-insensitively against the closed * {@link ALL_WORD_DEFAULT_FAMILIES} list and never used to build a path. A name outside it * resolves to nothing here; pair with `googleFonts()` to cover more. */ declare function packagedFonts(options?: PackagedFontsOptions): PackagedFontsResolver; export { ALL_WORD_DEFAULT_FAMILIES, type DefaultFontFaceRequest, type DefaultFontLoadFailure, type DefaultFontSource, type DefaultFontSubstitution, type DefaultFontsFragment, FONT_ASSET_MANIFEST, FONT_ASSET_ROOT, type FontAssetManifestEntry, type FontOriginRequest, type FontResolverMark, type LoadDefaultFontsOptions, type PackagedFontsFragment, type PackagedFontsOptions, type PackagedFontsResolver, type ResolvedFontFace, WORD_DOCUMENT_DEFAULT_FAMILIES, type WordDefaultFamily, defaultFonts, installDefaultFontFaces, loadDefaultFonts, packagedFonts };