@weasel-js/font 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -763,6 +763,26 @@ function isFontCollection(bytes) {
763
763
  if (bytes.byteLength < 4) return false;
764
764
  return tagAt(new DataView(bytes), 0) === "ttcf";
765
765
  }
766
+ var FONT_SIGNATURES = /* @__PURE__ */ new Set([
767
+ "\0\0\0",
768
+ "true",
769
+ "typ1",
770
+ "OTTO",
771
+ "ttcf",
772
+ "wOFF",
773
+ "wOF2"
774
+ ]);
775
+ var RESOURCE_HEADER_BYTES = 16;
776
+ function isDataForkFont(bytes) {
777
+ if (bytes.byteLength < RESOURCE_HEADER_BYTES) return false;
778
+ const view = new DataView(bytes);
779
+ if (FONT_SIGNATURES.has(tagAt(view, 0))) return false;
780
+ const dataOffset = view.getUint32(0);
781
+ const mapOffset = view.getUint32(4);
782
+ const dataLength = view.getUint32(8);
783
+ const mapLength = view.getUint32(12);
784
+ return dataOffset >= RESOURCE_HEADER_BYTES && dataOffset + dataLength === mapOffset && mapOffset + mapLength <= bytes.byteLength;
785
+ }
766
786
  function readTableDirectory(view, dirOffset) {
767
787
  const numTables = view.getUint16(dirOffset + 4);
768
788
  const records = [];
@@ -826,6 +846,11 @@ function extractFont(source, view, dirOffset) {
826
846
  return out;
827
847
  }
828
848
  function sfntFromCollection(bytes, postScriptName) {
849
+ if (isDataForkFont(bytes)) {
850
+ throw new Error(
851
+ "Datafork TrueType (.dfont) is not supported \u2014 the outline tier reads sfnt tables and a .dfont holds them inside a Macintosh resource map."
852
+ );
853
+ }
829
854
  if (!isFontCollection(bytes)) return { bytes, matched: true };
830
855
  const view = new DataView(bytes);
831
856
  const numFonts = view.getUint32(8);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/FontAtlas.ts","../src/glyphReady.ts","../src/fallback.ts","../src/dynamic/shelfPack.ts","../src/dynamic/distanceTransform.ts","../src/dynamic/glyphRasterizer.ts","../src/dynamic/dynamicAtlas.ts","../src/registerFont.ts","../src/outline/OutlineFace.ts","../src/outline/sfnt.ts","../src/outline/opentypeParser.ts","../src/outline/outlineRegistry.ts","../src/outline/localFonts.ts","../src/textSdf.ts"],"names":["policy","faces"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * BmFont JSON types and parser for the MSDF atlas format produced by\n * msdf-bmfont-xml. The parser builds accelerator maps (charMap, kerningMap)\n * for O(1) glyph and kerning lookup during layout.\n */\n\nexport interface BmFontInfo {\n face: string;\n size: number;\n}\n\nexport interface BmFontCommon {\n lineHeight: number;\n base: number;\n scaleW: number;\n scaleH: number;\n}\n\nexport interface BmFontChar {\n id: number;\n x: number;\n y: number;\n width: number;\n height: number;\n xoffset: number;\n yoffset: number;\n xadvance: number;\n page: number;\n}\n\nexport interface BmFontKerning {\n first: number;\n second: number;\n amount: number;\n}\n\nexport interface BmFont {\n info: BmFontInfo;\n common: BmFontCommon;\n chars: BmFontChar[];\n kernings: BmFontKerning[];\n charMap: Map<number, BmFontChar>;\n kerningMap: Map<number, Map<number, number>>;\n}\n\n/** Two-glyph fixture for unit tests. */\nexport const FIXTURE_FONT = {\n info: { face: 'Inter', size: 32 },\n common: { lineHeight: 38, base: 29, scaleW: 512, scaleH: 512 },\n chars: [\n { id: 65, x: 0, y: 0, width: 22, height: 28, xoffset: 1, yoffset: 4, xadvance: 23, page: 0 },\n { id: 66, x: 24, y: 0, width: 20, height: 28, xoffset: 2, yoffset: 4, xadvance: 22, page: 0 },\n ],\n kernings: [\n { first: 65, second: 66, amount: -1 },\n ],\n};\n\nexport function parseBmFont(raw: unknown): BmFont {\n if (typeof raw !== 'object' || raw === null) throw new Error('parseBmFont: expected object');\n const r = raw as Record<string, unknown>;\n\n if (!r.info || typeof r.info !== 'object') throw new Error('parseBmFont: missing info');\n if (!r.common || typeof r.common !== 'object') throw new Error('parseBmFont: missing common');\n if (!Array.isArray(r.chars)) throw new Error('parseBmFont: chars must be an array');\n\n const info = r.info as BmFontInfo;\n const common = r.common as BmFontCommon;\n const chars = r.chars as BmFontChar[];\n const kernings: BmFontKerning[] = Array.isArray(r.kernings)\n ? (r.kernings as BmFontKerning[])\n : [];\n\n const charMap = new Map<number, BmFontChar>();\n for (const ch of chars) charMap.set(ch.id, ch);\n\n const kerningMap = new Map<number, Map<number, number>>();\n for (const k of kernings) {\n let inner = kerningMap.get(k.first);\n if (!inner) { inner = new Map(); kerningMap.set(k.first, inner); }\n inner.set(k.second, k.amount);\n }\n\n return { info, common, chars, kernings, charMap, kerningMap };\n}\n","/**\n * \"A glyph the renderer asked for can now paint\" — the redraw signal shared\n * by every asynchronous glyph source.\n *\n * Both lazy tiers land their work *after* the frame that asked for it: the\n * dynamic canvas-SDF atlas defers bakes past a per-frame budget, and the\n * outline tier has to fetch and parse font bytes. Neither can paint anything\n * on the frame that first requests a glyph, so both have to tell the canvas\n * to draw again.\n *\n * One subscriber set, not one per tier: a consumer wiring \"redraw when text\n * becomes drawable\" should not have to know how many tiers exist, or grow a\n * second subscription every time one is added. `<SceneCanvas>` subscribes\n * once and both tiers reach it.\n */\n\nconst subscribers = new Set<() => void>();\n\n/** Advanced by every `notifyGlyphReady`, so state can be checked rather than\n * listened for. See {@link glyphGeneration}. */\nlet generation = 0;\n\n/**\n * How many times glyph availability has changed — the pull-based companion to\n * {@link subscribeGlyphReady}.\n *\n * For caches that cannot hold a subscription. Subscribing at module load is a\n * side effect that reaches across a package boundary: it makes importing the\n * module that holds the cache require this package's whole surface, so a\n * consumer partially mocking `@weasel-js/font` breaks at import rather than at\n * use, and import order starts to matter. Comparing a counter has neither\n * problem, and costs a field read on a path that was going to check a cache\n * key anyway.\n *\n * Callers keep the last value they saw and compare; any difference means\n * anything derived from font metrics is stale. The absolute value carries no\n * meaning and may wrap in principle — compare with `!==`, not `<`.\n */\nexport function glyphGeneration(): number {\n return generation;\n}\n\n/**\n * Subscribe to glyph availability. Fires after a batch of deferred SDF bakes\n * completes, and when an outline face finishes loading — in both cases, text\n * that painted nothing (or painted from a lower tier) can now paint. Returns\n * an unsubscribe.\n */\nexport function subscribeGlyphReady(cb: () => void): () => void {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n}\n\n/** @internal Fired by a glyph source once newly-available glyphs can paint. */\nexport function notifyGlyphReady(): void {\n generation++;\n for (const cb of subscribers) cb();\n}\n\n/** @internal Test seam — the subscriber set is module state. */\nexport function _clearGlyphReadySubscribers(): void {\n subscribers.clear();\n}\n","/**\n * Cross-family fallback policy. The per-family chain in `resolveFontVariant`\n * (weight → style → synthetic) has always been rich; what was missing is what\n * happens when the family itself was never registered. That case used to\n * render nothing at all, which is the single most common cause of \"my text is\n * invisible\".\n */\n\n/**\n * What happens when a requested family has no baked atlas:\n * - `'substitute'` — render with the default family (see\n * `setDefaultFontFamily`), reporting the swap on `ResolveResult.substituted`.\n * - `'canvas'` — auto-enroll the family with the dynamic canvas-SDF\n * rasterizer. The consumer gets the *real* typeface if the browser has it,\n * at canvas-SDF quality rather than baked-MSDF quality.\n * - `'none'` — hard miss; the run renders nothing.\n */\nexport type FontFallbackPolicy = 'substitute' | 'canvas' | 'none';\n\nlet policy: FontFallbackPolicy = 'substitute';\nlet defaultFamily: string | null = null;\n\nexport function setFontFallbackPolicy(next: FontFallbackPolicy): void {\n policy = next;\n}\n\nexport function getFontFallbackPolicy(): FontFallbackPolicy {\n return policy;\n}\n\n/** Explicit default family for `'substitute'`. When unset, the first\n * registered family wins — the right answer for the common case of an app\n * that registers exactly one. */\nexport function setDefaultFontFamily(family: string): void {\n defaultFamily = family;\n}\n\nexport function getDefaultFontFamily(): string | null {\n return defaultFamily;\n}\n\n/**\n * Warn-once gate for the cross-family fallback warnings. The messages are\n * built in `registerFont.ts` (they read the registry to name the actual gap),\n * but the dedup state lives here so `_resetFallbackForTests` clears it —\n * warnings about a fallback are fallback state. While it sat in\n * `registerFont.ts`, only a registry reset cleared it, and a test that reset\n * just the policy silently swallowed its next warning.\n */\nconst warnedFallbacks = new Set<string>();\n\n/** @internal Claim `key` for a one-time warning; true the first time only. */\nexport function claimFallbackWarning(key: string): boolean {\n if (warnedFallbacks.has(key)) return false;\n warnedFallbacks.add(key);\n return true;\n}\n\n/** @internal Also called by `_resetFontRegistryForTests`: dropping the fonts\n * a warning was about has to drop the warning too. */\nexport function _clearFallbackWarnings(): void {\n warnedFallbacks.clear();\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetFallbackForTests(): void {\n policy = 'substitute';\n defaultFamily = null;\n warnedFallbacks.clear();\n}\n","/**\n * Shelf packer for the dynamic glyph atlas: fixed-size square pages, rows\n * (\"shelves\") of same-ish-height rects filled left to right. Simple and\n * good enough for glyph rects; no rotation, no eviction (v1).\n */\n\nexport interface ShelfAlloc {\n page: number;\n x: number;\n y: number;\n}\n\ninterface Shelf { y: number; height: number; x: number; }\ninterface Page { shelves: Shelf[]; nextY: number; }\n\nexport class ShelfPacker {\n private pages: Page[] = [];\n private warned = false;\n\n constructor(\n private readonly pageSize: number,\n private readonly maxPages: number,\n ) {}\n\n get pageCount(): number {\n return this.pages.length;\n }\n\n /** Allocate a w×h rect. Returns null (warning once) when capacity is out. */\n alloc(w: number, h: number): ShelfAlloc | null {\n if (w > this.pageSize || h > this.pageSize) return this.fail();\n for (let p = 0; p < this.pages.length; p++) {\n const spot = this.allocInPage(this.pages[p], w, h);\n if (spot) return { page: p, ...spot };\n }\n if (this.pages.length < this.maxPages) {\n const page: Page = { shelves: [], nextY: 0 };\n this.pages.push(page);\n const spot = this.allocInPage(page, w, h);\n if (spot) return { page: this.pages.length - 1, ...spot };\n }\n return this.fail();\n }\n\n private allocInPage(page: Page, w: number, h: number): { x: number; y: number } | null {\n for (const shelf of page.shelves) {\n if (h <= shelf.height && shelf.x + w <= this.pageSize) {\n const x = shelf.x;\n shelf.x += w;\n return { x, y: shelf.y };\n }\n }\n if (page.nextY + h <= this.pageSize) {\n const shelf: Shelf = { y: page.nextY, height: h, x: w };\n page.shelves.push(shelf);\n page.nextY += h;\n return { x: 0, y: shelf.y };\n }\n return null;\n }\n\n private fail(): null {\n if (!this.warned) {\n this.warned = true;\n console.warn(\n `weasel DynamicGlyphAtlas: glyph pages full (${this.maxPages} × ${this.pageSize}²); ` +\n 'further dynamic glyphs will not render.',\n );\n }\n return null;\n }\n}\n","/**\n * Euclidean distance transform for runtime SDF glyph baking (the TinySDF\n * technique): antialiased canvas coverage in → single-channel SDF bytes out.\n *\n * Uses Felzenszwalb & Huttenlocher's 1D squared-distance transform applied\n * along columns then rows. Pure JS, no dependencies, no allocation beyond\n * the work arrays sized to the input.\n */\n\nconst INF = 1e20;\n\n/** One 1D pass of the squared EDT (Felzenszwalb & Huttenlocher 2012, §2). */\nfunction edt1d(\n f: Float64Array, d: Float64Array, v: Int32Array, z: Float64Array, n: number,\n): void {\n v[0] = 0;\n z[0] = -INF;\n z[1] = INF;\n let k = 0;\n for (let q = 1; q < n; q++) {\n let s = (f[q] + q * q - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);\n while (s <= z[k]) {\n k--;\n s = (f[q] + q * q - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);\n }\n k++;\n v[k] = q;\n z[k] = s;\n z[k + 1] = INF;\n }\n k = 0;\n for (let q = 0; q < n; q++) {\n while (z[k + 1] < q) k++;\n d[q] = (q - v[k]) * (q - v[k]) + f[v[k]];\n }\n}\n\n/** In-place 2D squared EDT over `grid` (width × height): columns, then rows. */\nfunction edt2d(\n grid: Float64Array, width: number, height: number,\n f: Float64Array, d: Float64Array, v: Int32Array, z: Float64Array,\n): void {\n for (let x = 0; x < width; x++) {\n for (let y = 0; y < height; y++) f[y] = grid[y * width + x];\n edt1d(f, d, v, z, height);\n for (let y = 0; y < height; y++) grid[y * width + x] = d[y];\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) f[x] = grid[y * width + x];\n edt1d(f, d, v, z, width);\n for (let x = 0; x < width; x++) grid[y * width + x] = d[x];\n }\n}\n\n/**\n * Convert an alpha-coverage bitmap (0–255) to single-channel SDF bytes.\n * Signed distance is positive outside the shape; the output maps it as\n * `255 − 255·(dist/radius + cutoff)` clamped to [0,255], so with\n * cutoff 0.5 the shape edge lands at byte ~128 (shader threshold 0.5).\n */\nexport function alphaToSdf(\n alpha: Uint8ClampedArray | Uint8Array,\n width: number,\n height: number,\n radius: number,\n cutoff: number,\n): Uint8Array {\n const n = width * height;\n const gridOuter = new Float64Array(n);\n const gridInner = new Float64Array(n);\n const size = Math.max(width, height);\n const f = new Float64Array(size);\n const d = new Float64Array(size);\n const v = new Int32Array(size);\n const z = new Float64Array(size + 1);\n\n for (let i = 0; i < n; i++) {\n const a = alpha[i] / 255;\n gridOuter[i] = a === 1 ? 0 : a === 0 ? INF : Math.max(0, 0.5 - a) ** 2;\n gridInner[i] = a === 1 ? INF : a === 0 ? 0 : Math.max(0, a - 0.5) ** 2;\n }\n edt2d(gridOuter, width, height, f, d, v, z);\n edt2d(gridInner, width, height, f, d, v, z);\n\n const out = new Uint8Array(n);\n for (let i = 0; i < n; i++) {\n const dist = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);\n const byte = Math.round(255 - 255 * (dist / radius + cutoff));\n out[i] = byte < 0 ? 0 : byte > 255 ? 255 : byte;\n }\n return out;\n}\n","/**\n * Canvas-2D glyph rasterizer for the dynamic SDF atlas. `fillText` is the\n * only web-platform route to *installed* machine fonts (no outline access),\n * which is exactly why this tier exists.\n *\n * Coordinate contract (all values in bake-size px, i.e. at BAKE_SIZE):\n * bitmap left = penX + left (left is negative: pad + left bearing)\n * bitmap top = baseline − top (top is positive above the baseline)\n * pen advance = advance\n * The bitmap includes a PAD border on all four sides so the SDF field has\n * room to fall off. Blank glyphs (e.g. space) return width/height 0.\n *\n * On BAKE_SIZE — measured 2026-07-29, comparing the reconstructed field\n * against a direct `fillText` of the same glyph at each display size, with\n * sub-pixel registration searched out so it measures fidelity and not\n * alignment. Mean per-pixel coverage error, averaged over A/S/e/W/8:\n *\n * display px 12 16 24 32 48 64 96 128\n * error .096 .083 .059 .039 .024 .023 .037 .049\n *\n * Two things follow, both of them counterintuitive enough to be worth\n * writing down. The error is minimized *at* the bake size, so raising\n * BAKE_SIZE does not improve small text — it moves the sweet spot away from\n * the 12–32px range where UI text actually lives and makes it worse. And the\n * magnification side is the mild one: 128px costs about twice the floor,\n * which is the corner rounding the shader header already owns.\n *\n * The 12–16px end is the largest divergence, and it is not undersampling —\n * 3×3 supersampling the reconstruction roughly halves the error at 24–48px\n * but recovers almost nothing at 12–16px (.096 → .089, .083 → .068). What is\n * left there is a hinted rasterizer placing stems on the pixel grid, which no\n * size-independent field can encode. So there is nothing to buy with more\n * taps, a bigger bake, or mipmaps (which a packed atlas cannot have anyway —\n * mip levels blend across glyph rects).\n */\n\nexport const BAKE_SIZE = 48;\nexport const PAD = 8;\n\nexport interface FaceMetrics {\n ascent: number;\n descent: number;\n}\n\nexport interface RasterizedGlyph {\n width: number;\n height: number;\n alpha: Uint8ClampedArray;\n left: number;\n top: number;\n advance: number;\n}\n\nexport interface GlyphRasterizer {\n faceMetrics(family: string, weight: number, style: 'normal' | 'italic'): FaceMetrics;\n rasterize(\n family: string, weight: number, style: 'normal' | 'italic', codepoint: number,\n ): RasterizedGlyph;\n}\n\ntype Canvas2D = OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;\n\nfunction cssFontString(weight: number, style: 'normal' | 'italic', family: string): string {\n return `${style === 'italic' ? 'italic ' : ''}${weight} ${BAKE_SIZE}px ${JSON.stringify(family)}`;\n}\n\nexport function createCanvasRasterizer(): GlyphRasterizer {\n let canvas: OffscreenCanvas | HTMLCanvasElement;\n if (typeof OffscreenCanvas !== 'undefined') {\n canvas = new OffscreenCanvas(BAKE_SIZE * 3, BAKE_SIZE * 3);\n } else if (typeof document !== 'undefined') {\n canvas = document.createElement('canvas');\n canvas.width = BAKE_SIZE * 3;\n canvas.height = BAKE_SIZE * 3;\n } else {\n throw new Error('weasel DynamicGlyphAtlas: no canvas available for glyph rasterization');\n }\n const ctx = canvas.getContext('2d', { willReadFrequently: true }) as Canvas2D | null;\n if (!ctx) throw new Error('weasel DynamicGlyphAtlas: 2D context unavailable');\n\n function setFont(family: string, weight: number, style: 'normal' | 'italic'): void {\n ctx!.font = cssFontString(weight, style, family);\n ctx!.textBaseline = 'alphabetic';\n ctx!.textAlign = 'left';\n }\n\n return {\n faceMetrics(family, weight, style) {\n setFont(family, weight, style);\n const m = ctx!.measureText('Hg');\n return {\n ascent: m.fontBoundingBoxAscent ?? BAKE_SIZE * 0.8,\n descent: m.fontBoundingBoxDescent ?? BAKE_SIZE * 0.2,\n };\n },\n\n rasterize(family, weight, style, codepoint) {\n setFont(family, weight, style);\n const chStr = String.fromCodePoint(codepoint);\n const m = ctx!.measureText(chStr);\n const advance = m.width;\n const inkLeft = Math.ceil(m.actualBoundingBoxLeft ?? 0);\n const inkRight = Math.ceil(m.actualBoundingBoxRight ?? advance);\n const inkAscent = Math.ceil(m.actualBoundingBoxAscent ?? BAKE_SIZE * 0.8);\n const inkDescent = Math.ceil(m.actualBoundingBoxDescent ?? BAKE_SIZE * 0.2);\n const inkW = inkLeft + inkRight;\n const inkH = inkAscent + inkDescent;\n if (inkW <= 0 || inkH <= 0) {\n return { width: 0, height: 0, alpha: new Uint8ClampedArray(0), left: 0, top: 0, advance };\n }\n const w = inkW + 2 * PAD;\n const h = inkH + 2 * PAD;\n if (canvas.width < w || canvas.height < h) {\n canvas.width = Math.max(canvas.width, w);\n canvas.height = Math.max(canvas.height, h);\n setFont(family, weight, style); // resizing resets 2D state\n }\n ctx!.clearRect(0, 0, w, h);\n ctx!.fillStyle = '#fff';\n ctx!.fillText(chStr, PAD + inkLeft, PAD + inkAscent);\n const img = ctx!.getImageData(0, 0, w, h);\n const alpha = new Uint8ClampedArray(w * h);\n for (let i = 0; i < alpha.length; i++) alpha[i] = img.data[i * 4 + 3];\n return {\n width: w,\n height: h,\n alpha,\n left: -(inkLeft + PAD),\n top: inkAscent + PAD,\n advance,\n };\n },\n };\n}\n","/**\n * DynamicGlyphAtlas — runtime single-channel SDF glyphs for canvas-sourced\n * (installed machine) fonts. TinySDF technique: canvas fillText at 48 px →\n * Euclidean distance transform → shelf-packed R8 pages (1024², max 4).\n *\n * Baked MSDF atlases always win: this tier only serves families registered\n * via `registerCanvasFont` that have no baked entry (see resolveFontVariant).\n *\n * Faces expose a BmFont-shaped `font` whose charMap grows as glyphs are\n * requested, so `layoutRuns` consumes them through the same code path as\n * baked atlases. A char's advance is valid immediately (measureText); its\n * atlas rect fills in when the bake lands (width 0 / page -1 until then, so\n * layout advances the pen but emits no quad).\n */\n\nimport type { BmFont, BmFontChar } from '../FontAtlas';\nimport type { GlyphTextureSink } from '../textureSink';\n// `fallback.ts` imports nothing, so this edge cannot close a cycle; keep it\n// that way. (registerFont.ts imports both this module and fallback.ts.)\nimport { getFontFallbackPolicy } from '../fallback';\nimport { notifyGlyphReady, _clearGlyphReadySubscribers } from '../glyphReady';\nimport { ShelfPacker } from './shelfPack';\nimport { alphaToSdf } from './distanceTransform';\nimport {\n createCanvasRasterizer, BAKE_SIZE,\n type GlyphRasterizer, type RasterizedGlyph,\n} from './glyphRasterizer';\n\nexport const PAGE_SIZE = 1024;\nexport const MAX_PAGES = 4;\nexport const SDF_RADIUS = 8;\nexport const SDF_CUTOFF = 0.5;\nexport const DEFAULT_BAKE_BUDGET = 16;\n\nexport interface DynamicFace {\n family: string;\n weight: number;\n style: 'normal' | 'italic';\n /** BmFont-shaped view consumed by layoutRuns; charMap grows lazily. */\n font: BmFont;\n /** Char record for `cp`, measured on first request (advance always valid\n * immediately; atlas rect fills in when the bake lands). */\n requestGlyph(cp: number): BmFontChar;\n}\n\ninterface DynamicPage {\n data: Uint8Array;\n /** Bumps on every glyph blit; each blit appends a patch with seq = version. */\n version: number;\n patches: { seq: number; x: number; y: number; w: number; h: number }[];\n}\n\ninterface PendingBake { char: BmFontChar; raster: RasterizedGlyph; }\n\n/** One family served by the dynamic canvas-SDF tier. See `listCanvasFonts`. */\nexport interface CanvasFontEntry {\n family: string;\n /** `'explicit'` — a `registerCanvasFont` call; `'auto'` — the `'canvas'`\n * fallback policy enrolled it on a miss, and the enrollment lapses if\n * that policy changes. */\n enrollment: 'explicit' | 'auto';\n}\n\nconst canvasFamilies = new Set<string>();\n// Enrollment provenance. A family the `'canvas'` fallback policy enrolled on\n// its own is only canvas-served while that policy is in force; one a consumer\n// named via registerCanvasFont outranks every policy. Tracked here rather\n// than in fallback.ts so the two sets can only ever be mutated together —\n// every register / unregister / reset path is in this file.\nconst autoEnrolledFamilies = new Set<string>();\nlet faces = new Map<string, DynamicFace>();\nlet pages: DynamicPage[] = [];\nlet packer = new ShelfPacker(PAGE_SIZE, MAX_PAGES);\nlet pending: PendingBake[] = [];\nlet flushScheduled = false;\nlet budget = DEFAULT_BAKE_BUDGET;\nlet rasterizer: GlyphRasterizer | null = null;\n\nfunction getRasterizer(): GlyphRasterizer {\n if (!rasterizer) rasterizer = createCanvasRasterizer();\n return rasterizer;\n}\n\n/** Mark `family` as canvas-sourced: when no baked atlas covers it,\n * resolveFontVariant serves it from this dynamic atlas. */\nexport function registerCanvasFont(family: string): void {\n canvasFamilies.add(family);\n // An explicit call promotes a previously auto-enrolled family for good.\n autoEnrolledFamilies.delete(family);\n}\n\n/**\n * Will `family` be served by the dynamic canvas-SDF tier *right now*?\n *\n * Service, not membership — the answer depends on the fallback policy in\n * force and can change without any enrollment call:\n * - explicitly enrolled via `registerCanvasFont` → `true` under every\n * policy; a consumer naming a family outranks the policy.\n * - auto-enrolled by the `'canvas'` policy → `true` only while that policy\n * is still in force. The enrollment lapses rather than being discarded,\n * so returning to `'canvas'` makes it `true` again.\n * - never enrolled → `false`, including under `'canvas'` (that policy\n * enrolls lazily, on the first miss).\n *\n * The membership reading would answer `true` for an auto-enrolled family\n * under `'substitute'` / `'none'`, where nothing routes to this tier — a\n * caller predicting what renders would be told the opposite of the truth.\n * Mirrors `listFonts`, which reports the baked registry alone for the same\n * reason: \"enrolled\" is not \"will render\".\n */\nexport function isCanvasFont(family: string): boolean {\n if (!canvasFamilies.has(family)) return false;\n return !autoEnrolledFamilies.has(family) || getFontFallbackPolicy() === 'canvas';\n}\n\n/**\n * Every family the dynamic canvas-SDF tier will serve *right now* — the\n * enumeration companion to `isCanvasFont`, which answers one family at a\n * time. Same \"service, not membership\" rule: an auto-enrolled family appears\n * only while the `'canvas'` policy is in force.\n *\n * Exists so a consumer can offer these families in a font picker.\n * `listFonts` reports the baked registry alone, so without this the only\n * ways to present canvas-served families were to hard-code a list beside the\n * `registerCanvasFont` calls or to omit them — a UI inventing its own answer\n * about what will render.\n *\n * `enrollment` distinguishes a consumer's explicit `registerCanvasFont` from\n * a lazy enrollment the policy made on a miss; both render identically, but\n * only the explicit one survives a policy change.\n */\nexport function listCanvasFonts(): readonly CanvasFontEntry[] {\n const out: CanvasFontEntry[] = [];\n for (const family of canvasFamilies) {\n if (!isCanvasFont(family)) continue;\n out.push({ family, enrollment: autoEnrolledFamilies.has(family) ? 'auto' : 'explicit' });\n }\n return out.sort((a, b) => a.family.localeCompare(b.family));\n}\n\n/**\n * @internal Enroll `family` on behalf of the `'canvas'` fallback policy.\n * Distinct from `registerCanvasFont` only in provenance: the enrollment\n * lapses (see `isExplicitCanvasFont`) if the policy later changes.\n */\nexport function autoEnrollCanvasFont(family: string): void {\n if (canvasFamilies.has(family) && !autoEnrolledFamilies.has(family)) return;\n canvasFamilies.add(family);\n autoEnrolledFamilies.add(family);\n}\n\n/**\n * @internal Enrolled by a consumer's own `registerCanvasFont` call rather than\n * by the `'canvas'` policy. Only these are served by the dynamic tier\n * regardless of the policy in force; an auto-enrolled family falls back to\n * whatever the current policy says once that policy is no longer `'canvas'`.\n */\nexport function isExplicitCanvasFont(family: string): boolean {\n return canvasFamilies.has(family) && !autoEnrolledFamilies.has(family);\n}\n\n/** Remove a canvas family. Its faces are dropped; already-baked glyph\n * pixels stay in their pages (no eviction in v1). */\nexport function unregisterCanvasFont(family: string): void {\n canvasFamilies.delete(family);\n autoEnrolledFamilies.delete(family);\n for (const key of [...faces.keys()]) {\n if (faces.get(key)!.family === family) faces.delete(key);\n }\n}\n\n/**\n * Internal resolver helper — not part of the public barrel; consumers go\n * through registerCanvasFont + resolveFontVariant.\n */\nexport function getDynamicFace(\n family: string, weight: number, style: 'normal' | 'italic',\n): DynamicFace {\n const key = `${family}|${weight}|${style}`;\n const existing = faces.get(key);\n if (existing) return existing;\n\n const r = getRasterizer();\n const metrics = r.faceMetrics(family, weight, style);\n const base = Math.round(metrics.ascent);\n const font: BmFont = {\n info: { face: family, size: BAKE_SIZE },\n common: {\n lineHeight: Math.round(metrics.ascent + metrics.descent),\n base,\n scaleW: PAGE_SIZE,\n scaleH: PAGE_SIZE,\n },\n chars: [],\n kernings: [], // no kerning in v1 (measured-pair kerning is future work)\n charMap: new Map(),\n kerningMap: new Map(),\n };\n\n const face: DynamicFace = {\n family, weight, style, font,\n requestGlyph(cp: number): BmFontChar {\n const cached = font.charMap.get(cp);\n if (cached) return cached;\n const raster = r.rasterize(family, weight, style, cp);\n const char: BmFontChar = {\n id: cp, x: 0, y: 0, width: 0, height: 0,\n xoffset: raster.left,\n yoffset: base - raster.top,\n xadvance: raster.advance,\n page: -1,\n };\n font.charMap.set(cp, char);\n font.chars.push(char);\n if (raster.width === 0 || raster.height === 0) {\n char.page = 0; // blank glyph (space): nothing to bake\n return char;\n }\n if (budget > 0) {\n budget--;\n bake(char, raster);\n } else {\n pending.push({ char, raster });\n scheduleFlush();\n }\n return char;\n },\n };\n faces.set(key, face);\n return face;\n}\n\nfunction bake(char: BmFontChar, raster: RasterizedGlyph): void {\n const spot = packer.alloc(raster.width, raster.height);\n if (!spot) return; // pages full — packer warned once; glyph stays invisible\n const sdf = alphaToSdf(raster.alpha, raster.width, raster.height, SDF_RADIUS, SDF_CUTOFF);\n while (pages.length <= spot.page) {\n pages.push({ data: new Uint8Array(PAGE_SIZE * PAGE_SIZE), version: 0, patches: [] });\n }\n const page = pages[spot.page];\n for (let row = 0; row < raster.height; row++) {\n page.data.set(\n sdf.subarray(row * raster.width, (row + 1) * raster.width),\n (spot.y + row) * PAGE_SIZE + spot.x,\n );\n }\n page.version++;\n page.patches.push({ seq: page.version, x: spot.x, y: spot.y, w: raster.width, h: raster.height });\n char.x = spot.x;\n char.y = spot.y;\n char.width = raster.width;\n char.height = raster.height;\n char.page = spot.page;\n}\n\nfunction scheduleFlush(): void {\n if (flushScheduled) return;\n flushScheduled = true;\n setTimeout(flushPending, 0);\n}\n\nfunction flushPending(): void {\n flushScheduled = false;\n let n = 0;\n while (pending.length > 0 && n < DEFAULT_BAKE_BUDGET) {\n const job = pending.shift()!;\n if (job.char.page !== -1) continue; // already baked\n bake(job.char, job.raster);\n n++;\n }\n if (pending.length > 0) scheduleFlush();\n notifyGlyphReady();\n}\n\n/** Reset the per-frame synchronous bake budget. Called by\n * `WeaselRenderer.render()` at frame start; the headless\n * `renderSceneToPixels` path passes Infinity so print never defers. */\nexport function resetBakeBudget(n: number = DEFAULT_BAKE_BUDGET): void {\n budget = n;\n}\n\n/* ------------------------------------------------------------------ */\n/* GPU sync */\n/* ------------------------------------------------------------------ */\n\n/** Texture-cache key for a dynamic page (parallel to `textureCacheKey`). */\nexport function dynamicPageTextureId(page: number): string {\n return `weasel-dyn-sdf-page-${page}`;\n}\n\n// Per-GL-cache upload progress: last page version each cache has seen.\n// WeakMap-keyed so disposed renderers don't pin anything.\nconst uploadedVersions = new WeakMap<GlyphTextureSink, Map<number, number>>();\n\n/** Bring `cache`'s copy of page `pageIndex` up to date: full R8 upload the\n * first time, `texSubImage2D` patches after. Returns false if the page\n * doesn't exist yet. */\nexport function syncDynamicPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean {\n const page = pages[pageIndex];\n if (!page) return false;\n const id = dynamicPageTextureId(pageIndex);\n let seen = uploadedVersions.get(cache);\n if (!seen) {\n seen = new Map();\n uploadedVersions.set(cache, seen);\n }\n if (!cache.has(id)) {\n cache.uploadR8(id, PAGE_SIZE, PAGE_SIZE, page.data);\n seen.set(pageIndex, page.version);\n return true;\n }\n const last = seen.get(pageIndex) ?? 0;\n if (last >= page.version) return true;\n for (const patch of page.patches) {\n if (patch.seq <= last) continue;\n const tight = new Uint8Array(patch.w * patch.h);\n for (let row = 0; row < patch.h; row++) {\n const src = (patch.y + row) * PAGE_SIZE + patch.x;\n tight.set(page.data.subarray(src, src + patch.w), row * patch.w);\n }\n cache.subImageR8(id, patch.x, patch.y, patch.w, patch.h, tight);\n }\n seen.set(pageIndex, page.version);\n return true;\n}\n\n/* ------------------------------------------------------------------ */\n/* Test seams */\n/* ------------------------------------------------------------------ */\n\n/** @internal test seam — inject a fake rasterizer (jsdom has no canvas\n * metrics). Pass null to restore the lazy default. */\nexport function __setGlyphRasterizerForTests(r: GlyphRasterizer | null): void {\n rasterizer = r;\n}\n\n/** @internal test seam — inspect CPU-side pages. */\nexport function _getPagesForTests(): readonly DynamicPage[] {\n return pages;\n}\n\n/** @internal test seam — clear all dynamic-font state. */\nexport function _resetDynamicFontsForTests(): void {\n canvasFamilies.clear();\n autoEnrolledFamilies.clear();\n faces = new Map();\n pages = [];\n packer = new ShelfPacker(PAGE_SIZE, MAX_PAGES);\n pending = [];\n flushScheduled = false;\n budget = DEFAULT_BAKE_BUDGET;\n _clearGlyphReadySubscribers();\n rasterizer = null;\n}\n","/**\n * FontRegistry and registerFont() public API.\n *\n * Variants are keyed by (family, weight, style). registerFont() takes a\n * FontVariant alongside the family and the two URLs; the registry stores\n * entries in a two-level Map so resolveFontVariant() can iterate a family's\n * variants for the fallback chain.\n */\n\nimport { notifyGlyphReady } from './glyphReady';\nimport { parseBmFont, type BmFont } from './FontAtlas';\nimport type { GlyphTextureSink } from './textureSink';\nimport {\n isCanvasFont, isExplicitCanvasFont, autoEnrollCanvasFont, getDynamicFace,\n type DynamicFace,\n} from './dynamic/dynamicAtlas';\nimport {\n getFontFallbackPolicy, getDefaultFontFamily,\n claimFallbackWarning, _clearFallbackWarnings,\n} from './fallback';\n\nexport interface FontEntry {\n font: BmFont;\n bitmap: ImageBitmap;\n}\n\nexport interface FontVariant {\n weight?: number;\n style?: 'normal' | 'italic';\n}\n\ntype FontStyle = 'normal' | 'italic';\n\nlet registry = new Map<string, Map<string, FontEntry>>();\n\nfunction variantKey(weight: number, style: FontStyle): string {\n return `${weight}|${style}`;\n}\n\nfunction normalizeVariant(v: FontVariant): { weight: number; style: FontStyle } {\n return {\n weight: v.weight ?? 400,\n style: v.style ?? 'normal',\n };\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetFontRegistryForTests(): void {\n registry = new Map();\n _clearFallbackWarnings();\n}\n\n/** Exact lookup — does NOT walk the fallback chain. Use `resolveFontVariant` for that. */\nexport function getFont(\n family: string,\n weight: number = 400,\n style: FontStyle = 'normal',\n): FontEntry | null {\n return registry.get(family)?.get(variantKey(weight, style)) ?? null;\n}\n\nexport interface RegisteredFont {\n family: string;\n variants: readonly { weight: number; style: FontStyle }[];\n}\n\n/**\n * Enumerate the registry — what a font picker can honestly offer. Families\n * come back in registration order; variants sorted by weight, then style, so\n * the output is stable enough to assert against.\n */\nexport function listFonts(): readonly RegisteredFont[] {\n const out: RegisteredFont[] = [];\n for (const [family, variantMap] of registry) {\n const variants = [...variantMap.keys()]\n .map((key) => {\n const [w, s] = key.split('|') as [string, FontStyle];\n return { weight: Number(w), style: s };\n })\n .sort((a, b) => a.weight - b.weight || a.style.localeCompare(b.style));\n out.push({ family, variants });\n }\n return out;\n}\n\nexport async function registerFont(\n family: string,\n variant: FontVariant,\n metricsUrl: string,\n atlasUrl: string,\n): Promise<void> {\n const { weight, style } = normalizeVariant(variant);\n const key = variantKey(weight, style);\n\n if (registry.get(family)?.has(key)) return;\n\n try {\n const [metricsRes, atlasRes] = await Promise.all([\n fetch(metricsUrl),\n fetch(atlasUrl),\n ]);\n\n if (!metricsRes.ok) {\n throw new Error(`HTTP ${metricsRes.status} fetching metrics from ${metricsUrl}`);\n }\n if (!atlasRes.ok) {\n throw new Error(`HTTP ${atlasRes.status} fetching atlas from ${atlasUrl}`);\n }\n\n const [rawJson, blob] = await Promise.all([\n metricsRes.json(),\n atlasRes.blob(),\n ]);\n\n const font = parseBmFont(rawJson);\n const bitmap = await createImageBitmap(blob);\n\n // Re-read the registry here (not the pre-await snapshot): concurrent\n // registerFont() calls for other variants of the same family (e.g. the\n // 400/700 weights registered together in Promise.all) may have created\n // the family's Map while this call was awaiting its fetch. Reusing a\n // stale local reference would recreate the Map and silently drop\n // whichever variant's registerFont() resolved first.\n let familyMap = registry.get(family);\n if (!familyMap) {\n familyMap = new Map();\n registry.set(family, familyMap);\n }\n familyMap.set(key, { font, bitmap });\n // Same meaning the lazy tiers give it: text that was painting from a\n // fallback face — or painting nothing — can now paint from this one. The\n // early return above covers the already-registered case, so this fires\n // once per variant that actually lands.\n notifyGlyphReady();\n } catch (err) {\n throw new Error(\n `weasel registerFont(\"${family}\" ${weight}/${style}): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\n/**\n * Ensure the atlas for `(family, weight, style)` is uploaded to\n * `textureCache`. Cache key is `${family}|${weight}|${style}` so each\n * variant occupies its own texture slot.\n */\nexport function ensureFontTexture(\n family: string,\n weight: number,\n style: FontStyle,\n textureCache: GlyphTextureSink,\n): boolean {\n const entry = getFont(family, weight, style);\n if (!entry) return false;\n textureCache.upload(textureCacheKey(family, weight, style), entry.bitmap);\n return true;\n}\n\n/** The texture cache key used by `ensureFontTexture` for a given variant. */\nexport function textureCacheKey(family: string, weight: number, style: FontStyle): string {\n return `${family}|${weight}|${style}`;\n}\n\n/** Kept as a no-op for context-restore call sites; per-cache dedup handles it now. */\nexport function markAllFontsNotUploaded(): void {}\n\nexport interface ResolveResult {\n entry: FontEntry | null;\n /**\n * The (family, weight, style) triple that was actually matched. May differ\n * from the requested values when the resolver walked the fallback chain —\n * including `family`, when the cross-family policy substituted a default.\n *\n * This is the atlas identity: pass all three to `getFont` /\n * `textureCacheKey` and the lookup hits. Describing only weight and style\n * here once let a caller key its draw on the *requested* family, which\n * resolves to no atlas at all and paints nothing.\n *\n * The synthetic flags describe the gap between requested and resolved for\n * shader-side compensation. When `entry` is null, these mirror the\n * requested values.\n */\n resolved: { family: string; weight: number; style: FontStyle };\n synthetic: { bold: boolean; italic: boolean };\n /** Which tier resolved: a baked MSDF atlas, or the runtime canvas-SDF\n * dynamic atlas. Misses report 'atlas' (the default tier). */\n source: 'atlas' | 'canvas';\n /** Set only when source === 'canvas': the dynamic face whose BmFont-shaped\n * `font` layoutRuns consumes in place of `entry.font`. */\n dynamicFace?: DynamicFace;\n /**\n * Set when the requested family was not registered and the fallback policy\n * substituted a different one. Reported structurally so a UI can say\n * \"Inter — not loaded, showing Roboto\" instead of leaving the user to\n * wonder why the family control did nothing.\n */\n substituted?: { requested: string; resolved: string };\n}\n\nfunction missResolveResult(\n family: string, weight: number, style: FontStyle, suppressWarn = false,\n): ResolveResult {\n // Canvas-dynamic tier: reached only when the fallback chain selected no\n // baked variant, so any selected baked match always wins. Dynamic faces\n // rasterize the real weight/style — no synthetic flags.\n //\n // Explicit enrollment only. A family the `'canvas'` policy enrolled for\n // itself must not keep routing here after the policy changes, or `'none'`\n // could never restore the hard miss it documents once any family had been\n // auto-enrolled.\n if (isExplicitCanvasFont(family)) {\n return {\n entry: null,\n dynamicFace: getDynamicFace(family, weight, style),\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'canvas',\n };\n }\n\n const policy = getFontFallbackPolicy();\n\n if (policy === 'canvas') {\n // Auto-enroll: the browser probably has this family even though no atlas\n // was baked for it. Real typeface, canvas-SDF quality.\n autoEnrollCanvasFont(family);\n return {\n entry: null,\n dynamicFace: getDynamicFace(family, weight, style),\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'canvas',\n };\n }\n\n if (policy === 'substitute') {\n const fallback = getDefaultFontFamily() ?? firstRegisteredFamily();\n // Guard against recursing when the default family is itself unknown.\n // Canvas families never enter `registry`, so membership there alone would\n // reject `setDefaultFontFamily` pointed at one. `isCanvasFont` answers\n // \"served by the dynamic tier right now\", which is the question here: a\n // family the `'canvas'` policy auto-enrolled is not served under this\n // policy, so substituting *to* it would report a swap that paints nothing.\n if (fallback !== null && fallback !== family) {\n if (registry.has(fallback) || isCanvasFont(fallback)) {\n // Probing whether the fallback family renders, not resolving a\n // top-level request for it — suppress so this recursion can't also\n // land in the `fallback === family` branch below (the fallback\n // family's own default is itself) and double-warn for one miss.\n // Whichever of `warnMissingFamilyOnce` / `warnUnusableDefaultOnce`\n // fires below, based on this probe's result, is the single warning.\n const result = resolveFontVariantInternal(fallback, weight, style, true);\n // Renderable, not baked: a fallback served by the dynamic tier reports\n // `entry: null` with a dynamicFace, and testing entry alone threw it away.\n if (result.entry !== null || result.dynamicFace !== undefined) {\n if (!suppressWarn) warnMissingFamilyOnce(family, weight, style, fallback);\n return { ...result, substituted: { requested: family, resolved: fallback } };\n }\n }\n // Substitution was supposed to happen and produced nothing. Falling\n // through silently here is the invisible-text failure this whole policy\n // exists to eliminate, so say which of the two families to fix.\n if (!suppressWarn) warnUnusableDefaultOnce(family, weight, style, fallback);\n } else if (fallback === family && !suppressWarn) {\n // `fallback === family` above exists to stop the request from\n // substituting for itself — recursion into the same miss forever. But\n // when the family that can't serve this variant *is* the effective\n // default, that guard also throws away the one case it was most likely\n // to hit: a default registered at the wrong variant. Nothing renders\n // and, without this branch, nothing is logged either.\n // `suppressWarn` keeps this from firing when a *different* top-level\n // request's substitution probe happens to recurse into the default\n // family and find it can't serve either — that miss is reported by the\n // top-level caller via `warnUnusableDefaultOnce`, not from in here.\n if (registry.has(family)) {\n warnSelfUnusableDefaultOnce(family, weight, style);\n } else {\n // Unregistered, yet still the effective default: the default can only\n // have been named explicitly, because the implicit one is the first\n // *registered* family (and with nothing registered at all, `fallback`\n // is null and never equals `family`). So this is a real\n // misconfiguration — someone pointed setDefaultFontFamily at a family\n // that does not exist — and not the deliberately-silent case of an app\n // that simply hasn't loaded any fonts yet.\n warnUnregisteredDefaultOnce(family, weight, style);\n }\n }\n }\n\n return {\n entry: null,\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'atlas',\n };\n}\n\n/** Insertion order of the registry Map — the first family an app registered. */\nfunction firstRegisteredFamily(): string | null {\n for (const family of registry.keys()) return family;\n return null;\n}\n\n// Resolution runs per frame, so an unguarded warn would flood the console.\n// Keyed per (family, weight, style) variant, not per family: each variant is a\n// distinct thing the app asked for and failed to get. The claimed keys live in\n// fallback.ts so both reset seams clear them.\nfunction warnMissingFamilyOnce(\n family: string, weight: number, style: FontStyle, resolved: string,\n): void {\n if (!claimFallbackWarning(`substituted|${family}|${weight}|${style}`)) return;\n // Two distinct failures land here. Saying \"not registered\" for a family\n // that IS registered — just not in a variant the within-family chain can\n // reach — sends the reader hunting for a registerFont call that already\n // exists. Name the actual gap so the fix is the obvious one.\n const cause = registry.has(family)\n ? `has no variant matching ${weight}/${style}, and none of its registered ` +\n `variants are close enough for the within-family chain to substitute — ` +\n `rendering with \"${resolved}\" instead. Bake that variant with ` +\n `registerFont(\"${family}\", { weight: ${weight}, style: '${style}' }, …)`\n : `is not registered — rendering with \"${resolved}\" instead. ` +\n `Call registerFont(\"${family}\", …)`;\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) ${cause}. ` +\n `Advance widths will differ from the requested font. Use ` +\n `setFontFallbackPolicy('none') to make this a hard miss instead.`,\n );\n}\n\n/**\n * The substitute policy engaged and still came up empty. Distinct from\n * `warnMissingFamilyOnce`, which reports a *successful* swap: here nothing\n * renders, and the fix is almost always in the fallback family rather than\n * the requested one — so name it, and say which way it was chosen.\n */\nfunction warnUnusableDefaultOnce(\n family: string, weight: number, style: FontStyle, fallback: string,\n): void {\n if (!claimFallbackWarning(`unusable-default|${family}|${weight}|${style}`)) return;\n const origin = getDefaultFontFamily() === fallback\n ? 'set via setDefaultFontFamily'\n : 'the first registered family, since setDefaultFontFamily was never called';\n // Same policy-aware question as the substitution guard: a fallback the\n // `'canvas'` policy auto-enrolled has nothing serving it under this policy,\n // so \"has no variant\" would send the reader looking for a variant to bake\n // when the family has no atlas at all.\n const gap = registry.has(fallback) || isCanvasFont(fallback)\n ? `has no variant that can serve ${weight}/${style}`\n : 'is not registered either';\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) is not available, and ` +\n `the fallback family \"${fallback}\" (${origin}) ${gap} — this text will not ` +\n `render at all. Bake that variant with registerFont(\"${fallback}\", ` +\n `{ weight: ${weight}, style: '${style}' }, …), or point ` +\n `setDefaultFontFamily() at a family that covers it.`,\n );\n}\n\n/**\n * The requested family is also the effective default — there is nowhere left\n * to fall back to. Distinct from `warnUnusableDefaultOnce`, which names a\n * *different* fallback family to fix; here the requested and fallback\n * families are the same one, so repeating the name the way that message does\n * would read as nonsense (\"X is not available, and the fallback X … \").\n * Named once, with both facts — registered-but-wrong-variant, and also the\n * fallback — folded into a single sentence.\n */\nfunction warnSelfUnusableDefaultOnce(\n family: string, weight: number, style: FontStyle,\n): void {\n if (!claimFallbackWarning(`self-unusable-default|${family}|${weight}|${style}`)) return;\n const origin = getDefaultFontFamily() === family\n ? 'set via setDefaultFontFamily'\n : 'the first registered family, since setDefaultFontFamily was never called';\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) has no variant that can ` +\n `serve this request, and \"${family}\" is also the fallback family (${origin}) — ` +\n `there is nothing left to fall back to, so this text will not render at all. ` +\n `Bake that variant with registerFont(\"${family}\", { weight: ${weight}, ` +\n `style: '${style}' }, …), or point setDefaultFontFamily() at a different family.`,\n );\n}\n\n/**\n * The requested family is the explicitly set default, and that default names\n * a family that was never registered at all. Distinct from\n * `warnSelfUnusableDefaultOnce`, which reports a default that *is* registered\n * but not in a variant that can serve the request: there the fix is to bake a\n * variant, here the family has no atlas whatsoever, so pointing the reader at\n * a missing variant would send them looking for a `registerFont` call to\n * amend rather than one to write.\n */\nfunction warnUnregisteredDefaultOnce(\n family: string, weight: number, style: FontStyle,\n): void {\n if (!claimFallbackWarning(`unregistered-default|${family}|${weight}|${style}`)) return;\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) was never registered, and ` +\n `it is also the fallback family — setDefaultFontFamily(\"${family}\") names a ` +\n `family with no registered variants at all, so there is nothing left to fall ` +\n `back to and this text will not render at all. Call registerFont(\"${family}\", ` +\n `{ weight: ${weight}, style: '${style}' }, …), or point setDefaultFontFamily() ` +\n `at a family you have registered.`,\n );\n}\n\nfunction weightBucket(w: number): 'regular' | 'bold' {\n return w >= 600 ? 'bold' : 'regular';\n}\n\n/**\n * Resolve a `(family, weight, style)` request to a registered font entry,\n * walking the fallback chain when an exact match isn't available. Returns\n * synthetic flags describing the gap between requested and resolved so the\n * renderer can apply SDF-thicken / vertex-skew fakes.\n */\nexport function resolveFontVariant(\n family: string,\n weight: number,\n style: FontStyle,\n): ResolveResult {\n return resolveFontVariantInternal(family, weight, style, false);\n}\n\n/**\n * Escalate a *single codepoint* to the dynamic tier when the atlas that\n * resolved for the run has no glyph for it.\n *\n * `resolveFontVariant` answers at family granularity: it picks one tier for a\n * whole run. But a baked MSDF atlas covers a fixed charset, so a run served by\n * a perfectly good atlas can still contain a character that atlas never baked\n * — an em dash, a curly quote, anything outside the subset. The dynamic tier\n * rasterizes on demand from installed fonts and can serve exactly those.\n *\n * Returns a canvas-tier `ResolveResult` whose `dynamicFace` the caller drives\n * with `requestGlyph(cp)`, or `null` when escalation isn't available:\n *\n * - Policy `'none'` documents a miss as a *hard* miss. Quietly reaching for\n * another tier per codepoint would undo that, so it doesn't.\n * - No canvas to rasterize into (SSR, a jsdom test without one) makes the\n * dynamic tier constructible-but-broken; `getDynamicFace` throws and this\n * reports the miss instead of taking the caller down with it.\n *\n * Cheap to call per missing codepoint: faces are cached by variant and glyphs\n * by codepoint, so a repeat is two map lookups.\n */\nexport function resolveGlyphFallback(\n family: string,\n weight: number,\n style: FontStyle,\n): ResolveResult | null {\n if (getFontFallbackPolicy() === 'none') return null;\n try {\n return {\n entry: null,\n dynamicFace: getDynamicFace(family, weight, style),\n resolved: { family, weight, style },\n // The dynamic tier rasterizes the real weight and style, so there is\n // nothing for the shader to fake.\n synthetic: { bold: false, italic: false },\n source: 'canvas',\n };\n } catch {\n return null;\n }\n}\n\n/**\n * `suppressWarn` is set only by the recursive substitution probe in\n * `missResolveResult`, which resolves the fallback family purely to check\n * whether it renders. That probe is not itself a request anything asked\n * for, so it must not emit — or claim the warn-once key for — a message\n * about a resolution the caller never made; the caller's own top-level\n * `missResolveResult` call decides, from the probe's result, which single\n * warning (if any) describes the overall miss.\n */\nfunction resolveFontVariantInternal(\n family: string,\n weight: number,\n style: FontStyle,\n suppressWarn: boolean,\n): ResolveResult {\n const familyMap = registry.get(family);\n if (!familyMap || familyMap.size === 0) {\n return missResolveResult(family, weight, style, suppressWarn);\n }\n\n // 1. Exact match\n const exact = familyMap.get(variantKey(weight, style));\n if (exact) {\n return {\n entry: exact,\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'atlas',\n };\n }\n\n // 2. Same style, nearest weight in same bucket (ties broken by higher weight)\n const requestedBucket = weightBucket(weight);\n let bestSameStyle: { entry: FontEntry; weight: number; distance: number } | null = null;\n for (const [key, entry] of familyMap) {\n const [wStr, s] = key.split('|') as [string, FontStyle];\n const w = Number(wStr);\n if (s !== style) continue;\n if (weightBucket(w) !== requestedBucket) continue;\n const distance = Math.abs(w - weight);\n if (\n bestSameStyle === null ||\n distance < bestSameStyle.distance ||\n (distance === bestSameStyle.distance && w > bestSameStyle.weight)\n ) {\n bestSameStyle = { entry, weight: w, distance };\n }\n }\n if (bestSameStyle) {\n return {\n entry: bestSameStyle.entry,\n resolved: { family, weight: bestSameStyle.weight, style },\n synthetic: { bold: false, italic: false },\n source: 'atlas',\n };\n }\n\n // 3. (family, 400, style)\n const sameStyleRegular = familyMap.get(variantKey(400, style));\n if (sameStyleRegular) {\n return {\n entry: sameStyleRegular,\n resolved: { family, weight: 400, style },\n synthetic: {\n bold: weight >= 600,\n italic: false,\n },\n source: 'atlas',\n };\n }\n\n // 4. (family, weight, 'normal') — same weight, no italic\n const sameWeightNormal = familyMap.get(variantKey(weight, 'normal'));\n if (sameWeightNormal) {\n return {\n entry: sameWeightNormal,\n resolved: { family, weight, style: 'normal' },\n synthetic: {\n bold: false,\n italic: style === 'italic',\n },\n source: 'atlas',\n };\n }\n\n // 4b. Nearest weight, normal style, same bucket\n let bestNormal: { entry: FontEntry; weight: number; distance: number } | null = null;\n for (const [key, entry] of familyMap) {\n const [wStr, s] = key.split('|') as [string, FontStyle];\n const w = Number(wStr);\n if (s !== 'normal') continue;\n if (weightBucket(w) !== requestedBucket) continue;\n const distance = Math.abs(w - weight);\n if (\n bestNormal === null ||\n distance < bestNormal.distance ||\n (distance === bestNormal.distance && w > bestNormal.weight)\n ) {\n bestNormal = { entry, weight: w, distance };\n }\n }\n if (bestNormal) {\n return {\n entry: bestNormal.entry,\n resolved: { family, weight: bestNormal.weight, style: 'normal' },\n synthetic: {\n bold: false,\n italic: style === 'italic',\n },\n source: 'atlas',\n };\n }\n\n // 5. (family, 400, 'normal') — last resort within family\n const regular = familyMap.get(variantKey(400, 'normal'));\n if (regular) {\n return {\n entry: regular,\n resolved: { family, weight: 400, style: 'normal' },\n synthetic: {\n bold: weight >= 600,\n italic: style === 'italic',\n },\n source: 'atlas',\n };\n }\n\n return missResolveResult(family, weight, style, suppressWarn);\n}\n","/**\n * The parsed-font seam for the outline tier.\n *\n * A face answers exactly one question — \"what does codepoint N look like?\" —\n * and answers it as **SVG path data in em space**: one unit is one em, y\n * grows downward, and the origin sits on the baseline at the glyph's pen\n * position. That is a complete description of the glyph independent of size,\n * zoom, and position, which is the whole reason the tier exists: tessellate\n * once, transform per instance.\n *\n * ### Why `d` and not a command stream\n *\n * `@weasel-js/font` is a Tier A leaf — `@weasel-js/core` depends on it and\n * never the reverse (see `leaf-purity.test.ts`), so this package cannot name\n * core's `PolygonPath`. The alternative to a string would be re-declaring\n * core's `PATH_M`/`PATH_L`/… opcodes over here and trusting two packages to\n * keep the same numbering forever. SVG `d` is the kit's documented language\n * for geometry crossing a boundary (`docs/conventions.md`), core already\n * ships `pathFromD`, and a glyph outline is exactly the case it describes.\n *\n * The string is also a natural cache key and costs nothing to hold: the\n * parse happens once per glyph, and core caches the *tessellation*, not the\n * text.\n */\n\n/** Weight/style pair identifying one face within a family. */\nexport type OutlineFontStyle = 'normal' | 'italic';\n\nexport interface OutlineFace {\n /** Font design units per em. Reported for diagnostics; `glyphD` has already\n * divided by it, so callers never need to. */\n unitsPerEm: number;\n /**\n * Em-space SVG path data for `cp`, or `null` when this face has no glyph\n * for the codepoint *or* the glyph has no contours (a space). Both answers\n * mean the same thing to a caller: there is nothing here to tessellate, so\n * fall through to whatever tier would have drawn it.\n */\n glyphD(cp: number): string | null;\n}\n\n/**\n * Turn font file bytes into a face. Async because the default implementation\n * dynamically imports its parser — see `opentypeParser.ts` for why that\n * matters — and because a caller may want to hand back a face that finishes\n * initializing off-thread.\n */\nexport type OutlineParser = (bytes: ArrayBuffer) => OutlineFace | Promise<OutlineFace>;\n\n/**\n * Decimal places kept when serializing em-space coordinates.\n *\n * Five is chosen against two constraints, not by taste. Below: 1e-5 em is\n * 0.01px on a 1000px-tall glyph, an order of magnitude under anything a\n * display can show. Above: `pathFromD`'s tokenizer splits argument runs on\n * *letters*, so a coordinate that JavaScript chooses to print in exponential\n * form (`1e-7`) would tokenize as the command `e`. Numbers stay in\n * positional notation down to 1e-6, so five decimals cannot produce one.\n */\nexport const OUTLINE_PRECISION = 5;\n","/**\n * TrueType/OpenType **collection** unpacking.\n *\n * `queryLocalFonts()` hands back the font *file* behind a face, and on macOS\n * a large share of the interesting ones — Helvetica, Times, Courier, Menlo —\n * are `.ttc` collections holding every weight and style in a single file with\n * one shared glyph store. A collection is not a font: its header is `ttcf`\n * rather than an sfnt version, so every parser worth using rejects it, and\n * opentype.js is no exception (it recognizes `\\0\\1\\0\\0`, `true`, `typ1`,\n * `OTTO`, `wOFF` and throws on anything else).\n *\n * The fix is mechanical, because a collection is just N table directories\n * over one pool of tables: pick the directory whose PostScript name matches\n * the face we were asked for, then re-emit it as a standalone sfnt with the\n * tables copied and the offsets rewritten. Nothing is re-encoded — the table\n * bytes are the same bytes — so the result parses identically to a font that\n * had shipped on its own.\n *\n * Kept separate from `opentypeParser.ts` so the collection handling is\n * testable without a parser, and so swapping the parser later doesn't drag\n * this along.\n *\n * ### Why this lives here rather than upstream or in a bigger library\n *\n * Decided 2026-07-31, after looking at both alternatives — recorded so this\n * doesn't get \"tidied up\" into one of them later.\n *\n * The upstream fix is genuinely small: `parseOpenTypeTableEntries` hardcodes\n * `p = 12`, so the only thing opentype.js cannot do is be *told* the table\n * directory starts somewhere other than byte zero. Table records already\n * carry absolute offsets and the parser already reads them from a\n * whole-buffer view, so a `dirOffset` parameter and a `ttcf` branch would be\n * about ten lines. (Typr takes an offset in `readFONT` and gets collections\n * for free — same capability, one better-chosen signature.) It would make a\n * good PR. It would also not help for a while: opentype.js sat untouched from\n * 2021 to 2026, and we would carry this file until a release landed anyway.\n *\n * fontkit handles `.ttc` and `.dfont` natively, and WOFF2 besides — but it is\n * ~5.6 MB unpacked across nine dependencies (brotli, unicode-trie, dfa, …).\n * That is a shaping engine, and all this tier wants is a glyph outline.\n *\n * This version has an advantage over both: it runs *before* anything parses,\n * so it is parser-agnostic and survives swapping opentype.js out.\n *\n * **Known gap: `.dfont`** (Datafork TrueType) is not handled — the signature\n * check rejects it and the face degrades to the SDF tier, which is the right\n * failure but a silent one. Not currently reachable on macOS, whose system\n * font directories are 204 `.ttf` / 128 `.ttc` / 38 `.otf` and no `.dfont`;\n * it is a real format on older systems, so it is the first thing to suspect\n * if a machine face inexplicably refuses to sharpen.\n */\n\nconst SFNT_HEADER_BYTES = 12;\nconst TABLE_RECORD_BYTES = 16;\n\nfunction tagAt(view: DataView, offset: number): string {\n return String.fromCharCode(\n view.getUint8(offset), view.getUint8(offset + 1),\n view.getUint8(offset + 2), view.getUint8(offset + 3),\n );\n}\n\n/** Is `bytes` a font collection rather than a single font? */\nexport function isFontCollection(bytes: ArrayBuffer): boolean {\n if (bytes.byteLength < 4) return false;\n return tagAt(new DataView(bytes), 0) === 'ttcf';\n}\n\ninterface TableRecord { tag: string; offset: number; length: number; checksum: number }\n\nfunction readTableDirectory(view: DataView, dirOffset: number): TableRecord[] {\n const numTables = view.getUint16(dirOffset + 4);\n const records: TableRecord[] = [];\n for (let i = 0; i < numTables; i++) {\n const at = dirOffset + SFNT_HEADER_BYTES + i * TABLE_RECORD_BYTES;\n records.push({\n tag: tagAt(view, at),\n checksum: view.getUint32(at + 4),\n offset: view.getUint32(at + 8),\n length: view.getUint32(at + 12),\n });\n }\n return records;\n}\n\n/**\n * PostScript name (`name` table, nameID 6) of the sub-font at `dirOffset`,\n * or `null` when it carries none.\n *\n * A deliberately minimal reader: identifying which member of a collection to\n * unpack is the only thing needed here, and a full `name` parser would be a\n * second implementation of something the real parser does properly once the\n * font is standalone. PostScript names are ASCII by specification, so both\n * the Macintosh (single-byte) and Windows (UTF-16BE) encodings reduce to\n * \"take every byte that isn't a zero pad\".\n */\nfunction postScriptNameAt(view: DataView, records: readonly TableRecord[]): string | null {\n const name = records.find((r) => r.tag === 'name');\n if (!name) return null;\n const base = name.offset;\n const count = view.getUint16(base + 2);\n const stringOffset = view.getUint16(base + 4);\n for (let i = 0; i < count; i++) {\n const rec = base + 6 + i * 12;\n if (view.getUint16(rec + 6) !== 6) continue; // nameID 6 === PostScript name\n const length = view.getUint16(rec + 8);\n const offset = view.getUint16(rec + 10);\n let out = '';\n for (let b = 0; b < length; b++) {\n const code = view.getUint8(base + stringOffset + offset + b);\n if (code !== 0) out += String.fromCharCode(code);\n }\n if (out.length > 0) return out;\n }\n return null;\n}\n\n/** Re-emit the table directory at `dirOffset` as a standalone sfnt buffer. */\nfunction extractFont(source: ArrayBuffer, view: DataView, dirOffset: number): ArrayBuffer {\n const records = readTableDirectory(view, dirOffset);\n // Tables are copied 4-byte aligned, which is what the format requires and\n // what every checksum in the directory was computed over.\n const padded = (n: number): number => (n + 3) & ~3;\n let total = SFNT_HEADER_BYTES + records.length * TABLE_RECORD_BYTES;\n for (const r of records) total += padded(r.length);\n\n const out = new ArrayBuffer(total);\n const dst = new DataView(out);\n const dstBytes = new Uint8Array(out);\n const srcBytes = new Uint8Array(source);\n\n dst.setUint32(0, view.getUint32(dirOffset)); // sfntVersion\n dst.setUint16(4, records.length);\n // searchRange / entrySelector / rangeShift are a binary-search hint over the\n // table records. Recomputed rather than copied: the member directory's\n // values describe its own table count, which is usually but not always the\n // same, and a wrong hint is a parser walking off the end of the directory.\n const entrySelector = Math.floor(Math.log2(records.length));\n const searchRange = 2 ** entrySelector * 16;\n dst.setUint16(6, searchRange);\n dst.setUint16(8, entrySelector);\n dst.setUint16(10, records.length * 16 - searchRange);\n\n let cursor = SFNT_HEADER_BYTES + records.length * TABLE_RECORD_BYTES;\n records.forEach((r, i) => {\n const at = SFNT_HEADER_BYTES + i * TABLE_RECORD_BYTES;\n for (let b = 0; b < 4; b++) dst.setUint8(at + b, r.tag.charCodeAt(b));\n dst.setUint32(at + 4, r.checksum);\n dst.setUint32(at + 8, cursor);\n dst.setUint32(at + 12, r.length);\n dstBytes.set(srcBytes.subarray(r.offset, r.offset + r.length), cursor);\n cursor += padded(r.length);\n });\n return out;\n}\n\n/**\n * Standalone font bytes for one member of a collection.\n *\n * `postScriptName` selects the member; when it is absent or matches nothing,\n * the first member is returned. Falling back rather than failing is\n * deliberate — the name comes from `FontData.postscriptName`, and a browser\n * that reports it differently from the `name` table should degrade to \"some\n * weight of the right family\", not to no text at all. The caller sees which\n * it got via the returned `matched` flag.\n *\n * Passing a single font through is a no-op, so this is safe to call on any\n * bytes.\n */\nexport function sfntFromCollection(\n bytes: ArrayBuffer,\n postScriptName?: string,\n): { bytes: ArrayBuffer; matched: boolean } {\n if (!isFontCollection(bytes)) return { bytes, matched: true };\n\n const view = new DataView(bytes);\n const numFonts = view.getUint32(8);\n if (numFonts === 0) throw new Error('font collection contains no fonts');\n\n const offsets: number[] = [];\n for (let i = 0; i < numFonts; i++) offsets.push(view.getUint32(12 + i * 4));\n\n if (postScriptName) {\n for (const dirOffset of offsets) {\n const records = readTableDirectory(view, dirOffset);\n if (postScriptNameAt(view, records) === postScriptName) {\n return { bytes: extractFont(bytes, view, dirOffset), matched: true };\n }\n }\n }\n return { bytes: extractFont(bytes, view, offsets[0]), matched: !postScriptName };\n}\n","/**\n * The default `OutlineParser`: opentype.js, loaded on demand.\n *\n * ### Why the dynamic import\n *\n * opentype.js is ~49 kB gzipped and it is the *only* runtime dependency this\n * package has. Charging that to every consumer of `@weasel-js/font` — most of\n * whom render text through a baked atlas and never touch an outline — would\n * be a poor trade for a tier that only engages above a size threshold. The\n * tier is inherently asynchronous anyway (font bytes arrive over `fetch` or\n * `queryLocalFonts`), so there is no synchronous path to slow down: the\n * import rides along with the load that was already going to happen, and a\n * bundler splits it into a chunk nobody fetches until large text appears.\n *\n * The other candidate was Typr — smaller, but unmaintained and untyped, and\n * this is a runtime dependency of a published package.\n *\n * ### Coordinate convention\n *\n * `glyph.getPath(0, 0, 1)` already emits exactly what `OutlineFace` promises:\n * font units divided by the em, y flipped to grow downward, and the origin on\n * the baseline at the pen position. That is opentype.js's canvas-rendering\n * convention, and it happens to be the kit's world convention too, so no\n * transform is applied here — a flip would have to be undone downstream.\n */\n\nimport { OUTLINE_PRECISION, type OutlineFace, type OutlineParser } from './OutlineFace';\nimport { sfntFromCollection } from './sfnt';\n\ntype OpenType = typeof import('opentype.js');\n\nlet modulePromise: Promise<OpenType> | null = null;\n\n/** Load (once) and cache the parser module. */\nfunction loadOpenType(): Promise<OpenType> {\n modulePromise ??= import('opentype.js');\n return modulePromise;\n}\n\n/**\n * Wrap a parsed opentype.js font.\n *\n * `charToGlyphIndex` reports 0 — `.notdef` — for a codepoint the font does\n * not cover. That is a real glyph (the tofu box) and it renders, so returning\n * its outline would silently substitute a box for a character the atlas tier\n * might have been able to serve. Report the miss instead and let the caller's\n * fallback ladder run.\n */\nfunction faceFor(font: import('opentype.js').Font): OutlineFace {\n return {\n unitsPerEm: font.unitsPerEm,\n glyphD(cp: number): string | null {\n const index = font.charToGlyphIndex(String.fromCodePoint(cp));\n if (!index) return null;\n const d = font.glyphs.get(index).getPath(0, 0, 1).toPathData(OUTLINE_PRECISION);\n // A glyph with no contours (a space) serializes to the empty string.\n // Same answer as a missing glyph from the caller's point of view:\n // nothing to tessellate.\n return d.length > 0 ? d : null;\n },\n };\n}\n\n/**\n * Parse font file bytes into a face. Accepts single fonts and collections\n * (`.ttc`); `postScriptName` picks the member of a collection, and is\n * ignored for a single font.\n */\nexport function createOpenTypeParser(postScriptName?: string): OutlineParser {\n return async (bytes: ArrayBuffer): Promise<OutlineFace> => {\n const opentype = await loadOpenType();\n const { bytes: single } = sfntFromCollection(bytes, postScriptName);\n return faceFor(opentype.parse(single));\n };\n}\n\n/** The parser used when a registration supplies none of its own. */\nexport const openTypeParser: OutlineParser = createOpenTypeParser();\n\n/** @internal Test seam — drop the memoized module so a test can re-import. */\nexport function _resetOpenTypeModuleForTests(): void {\n modulePromise = null;\n}\n","/**\n * The outline tier's registry: which faces have real font bytes behind them,\n * and what a given glyph looks like.\n *\n * ### What this tier is for\n *\n * A distance field is a *sampled* representation. The baked MSDF atlas is\n * generated from outlines and holds up well; the dynamic canvas-SDF tier\n * (`../dynamic/`) reconstructs its field from a 48px raster, so magnifying it\n * past a few times the bake size exposes the raster as contour wobble — bumps\n * one bake-texel apart, ±2–3px at 8×. No bake size fixes that, it only moves\n * the size at which it appears (`glyphRasterizer.ts` measures the tradeoff:\n * error is *minimized at* the bake size, so raising it spoils the 12–32px\n * range where most text lives).\n *\n * Above a size threshold the answer is to stop sampling: parse the font, take\n * the glyph's outline, and hand it to the path renderer the kit already has.\n * Exact at every zoom, and — because a glyph becomes an ordinary path — it\n * takes strokes and non-solid fills for free.\n *\n * Below the threshold the atlas still wins, and not narrowly: outlines carry\n * no hinting and no stem darkening, so body text at 12–16px rendered from\n * them looks *worse* than a platform rasterizer that puts stems on the pixel\n * grid. Two tiers with a size threshold is the standard hybrid, not a\n * compromise.\n *\n * ### Metric neutrality (the load-bearing invariant)\n *\n * This tier replaces how a glyph is *painted*, never where it sits. Advances,\n * kerning, line breaking and baselines all keep coming from whichever tier\n * resolved the run — the baked atlas or the canvas face. That is what lets\n * the threshold be a rendering decision: zooming past it swaps glyph geometry\n * with the layout untouched, so text cannot reflow under the user's cursor,\n * and `measureTextBounds` / `textLineBoxes` need to know nothing about\n * outlines at all.\n *\n * The cost is that a face whose real advances disagree with the atlas's\n * inherits the atlas's. For the bundled Inter that is exact (the subset ships\n * from the same source the atlas was baked from), and for a machine font the\n * outline bytes and the canvas metrics come from the same file.\n *\n * ### Availability\n *\n * Everything here degrades rather than blocks. Bytes arrive over `fetch` or\n * `queryLocalFonts` — asynchronous, and in the local case gated behind a\n * permission the user can refuse — so `glyphOutline` answers `null` until a\n * face is loaded and forever if it failed, and the caller falls back down the\n * ladder to SDF. A family must never render *nothing* because the outline\n * tier could not get bytes.\n */\n\nimport { notifyGlyphReady } from '../glyphReady';\nimport type { OutlineFace, OutlineFontStyle, OutlineParser } from './OutlineFace';\nimport { openTypeParser } from './opentypeParser';\n\n/**\n * Where a face's bytes come from. A URL is fetched; a buffer or blob is used\n * as-is; a thunk is called at first use, which is what lets `queryLocalFonts`\n * results be registered eagerly and read lazily — the permission prompt and\n * the (potentially many megabytes of) blob only happen for a family the\n * document actually sets text in.\n */\nexport type OutlineSource =\n | string\n | ArrayBuffer\n | Blob\n | (() => ArrayBuffer | Blob | Promise<ArrayBuffer | Blob>);\n\nexport interface OutlineVariant {\n weight?: number;\n style?: OutlineFontStyle;\n}\n\nexport interface OutlineFontOptions {\n /** Override the default opentype.js parser. Mostly a test seam; also the\n * hook for a consumer who already has a font parser in their bundle. */\n parser?: OutlineParser;\n}\n\n/** Load state of one registered face. */\nexport type OutlineStatus = 'idle' | 'loading' | 'ready' | 'failed';\n\ninterface FaceSlot {\n family: string;\n weight: number;\n style: OutlineFontStyle;\n source: OutlineSource;\n parser: OutlineParser;\n status: OutlineStatus;\n face: OutlineFace | null;\n /** Memoized `glyphD` answers, including the `null` misses — a codepoint the\n * face lacks is asked for on every frame that draws it, and re-deriving\n * the miss means re-running the cmap lookup forever. */\n glyphs: Map<number, string | null>;\n}\n\nlet slots = new Map<string, FaceSlot>();\n\nfunction slotKey(family: string, weight: number, style: OutlineFontStyle): string {\n return `${family}|${weight}|${style}`;\n}\n\nfunction normalize(v: OutlineVariant): { weight: number; style: OutlineFontStyle } {\n return { weight: v.weight ?? 400, style: v.style ?? 'normal' };\n}\n\n/**\n * Register font file bytes for one face, so text set in it can render from\n * outlines above the size threshold.\n *\n * Registration is cheap and synchronous: nothing is fetched, and the parser\n * is not even imported, until a frame actually asks for a glyph from this\n * face. Registering a face that is never drawn large costs one map entry.\n *\n * The variant must match exactly what the layout tier resolved — this is\n * deliberately *not* a fallback chain. Painting 400-weight outlines at\n * 700-weight advances, or upright outlines where the shader was going to\n * fake an oblique, looks worse than the SDF it replaced; a miss here falls\n * back down the ladder instead, which is always safe.\n */\nexport function registerFontOutlines(\n family: string,\n variant: OutlineVariant,\n source: OutlineSource,\n opts: OutlineFontOptions = {},\n): void {\n const { weight, style } = normalize(variant);\n slots.set(slotKey(family, weight, style), {\n family, weight, style, source,\n parser: opts.parser ?? openTypeParser,\n status: 'idle',\n face: null,\n glyphs: new Map(),\n });\n}\n\n/** Drop a registration. Glyphs already handed out stay valid — they are\n * plain path data — but nothing further resolves from this face. */\nexport function unregisterFontOutlines(family: string, variant: OutlineVariant = {}): void {\n const { weight, style } = normalize(variant);\n slots.delete(slotKey(family, weight, style));\n}\n\n/**\n * Is an outline face registered for this exact variant? Answers `true` while\n * the bytes are still loading — the question is \"could this face serve\n * outlines\", which is what a caller deciding whether to *ask* wants; use\n * `outlineStatus` for the narrower \"can it right now\".\n */\nexport function hasFontOutlines(\n family: string, weight = 400, style: OutlineFontStyle = 'normal',\n): boolean {\n return slots.has(slotKey(family, weight, style));\n}\n\n/** Load state of a registered face; `null` when nothing is registered. */\nexport function outlineStatus(\n family: string, weight = 400, style: OutlineFontStyle = 'normal',\n): OutlineStatus | null {\n return slots.get(slotKey(family, weight, style))?.status ?? null;\n}\n\n/** Every registered outline face and its load state — the enumeration a\n * debug overlay or font picker needs, mirroring `listFonts`. */\nexport function listFontOutlines(): readonly {\n family: string; weight: number; style: OutlineFontStyle; status: OutlineStatus;\n}[] {\n return [...slots.values()]\n .map(({ family, weight, style, status }) => ({ family, weight, style, status }))\n .sort((a, b) => a.family.localeCompare(b.family) || a.weight - b.weight\n || a.style.localeCompare(b.style));\n}\n\n/**\n * Em-space SVG path data for one glyph, or `null` when this tier cannot serve\n * it — no registration, bytes still loading, load failed, no such glyph, or a\n * glyph with no contours.\n *\n * Synchronous by construction: it is called from the per-frame glyph walk, so\n * it can only ever report what is already in hand. The first call for an\n * unloaded face starts the load and returns `null`; when the load lands,\n * `notifyGlyphReady` asks the canvas to draw again and the second frame gets\n * the outline. Same shape as the dynamic tier's deferred bakes.\n */\n/**\n * Close every contour of a glyph's path data.\n *\n * A glyph contour is closed by definition — neither TrueType nor CFF has any\n * other kind — but not every serializer writes the `Z` that says so, and the\n * omission is invisible until someone *strokes* the outline: a fill closes the\n * contour implicitly (earcut joins last point to first), while a stroke\n * follows exactly the path it is handed and leaves the closing edge unpainted,\n * with a cap at each loose end. That showed up as glyphs missing part of their\n * outline along long straight edges, which is where a contour's seam usually\n * falls.\n *\n * Normalized here rather than in one parser because the same hole is open to\n * every consumer-supplied `OutlineParser`, and a contract that says \"closed\"\n * is cheaper to guarantee than to document.\n */\nexport function closeContours(d: string): string {\n let out = '';\n let start = 0;\n for (let i = 1; i < d.length; i++) {\n const c = d[i];\n if (c !== 'M' && c !== 'm') continue;\n out += closeOne(d.slice(start, i));\n start = i;\n }\n return out + closeOne(d.slice(start));\n}\n\nfunction closeOne(contour: string): string {\n const trimmed = contour.trimEnd();\n if (trimmed.length === 0) return '';\n const last = trimmed[trimmed.length - 1];\n return last === 'Z' || last === 'z' ? trimmed : `${trimmed}Z`;\n}\n\nexport function glyphOutline(\n family: string,\n weight: number,\n style: OutlineFontStyle,\n cp: number,\n): string | null {\n const slot = slots.get(slotKey(family, weight, style));\n if (!slot) return null;\n if (slot.status === 'idle') {\n void beginLoad(slot);\n return null;\n }\n if (slot.status !== 'ready') return null;\n\n const cached = slot.glyphs.get(cp);\n if (cached !== undefined) return cached;\n let d: string | null = null;\n try {\n const raw = slot.face!.glyphD(cp);\n d = raw === null ? null : closeContours(raw);\n } catch (err) {\n // A parser that throws on one glyph should cost that glyph, not the face.\n warnOnce(`glyph|${slotKey(family, weight, style)}`,\n `weasel: outline face \"${family}\" (${weight}/${style}) could not produce a glyph ` +\n `for U+${cp.toString(16).toUpperCase().padStart(4, '0')} — ` +\n `${err instanceof Error ? err.message : String(err)}. Falling back to the SDF tier.`);\n }\n slot.glyphs.set(cp, d);\n return d;\n}\n\nasync function beginLoad(slot: FaceSlot): Promise<void> {\n slot.status = 'loading';\n try {\n slot.face = await slot.parser(await readSource(slot.source));\n slot.status = 'ready';\n // The frame that asked for these glyphs has long since drawn. Without\n // this the outlines sit in memory until something else invalidates the\n // canvas, which on a static document is \"never\".\n notifyGlyphReady();\n } catch (err) {\n slot.status = 'failed';\n warnOnce(`load|${slotKey(slot.family, slot.weight, slot.style)}`,\n `weasel registerFontOutlines(\"${slot.family}\" ${slot.weight}/${slot.style}): ` +\n `${err instanceof Error ? err.message : String(err)}. Large text in this face ` +\n `keeps rendering from the SDF tier.`);\n }\n}\n\nasync function readSource(source: OutlineSource): Promise<ArrayBuffer> {\n const resolved = typeof source === 'function' ? await source() : source;\n if (typeof resolved === 'string') {\n const res = await fetch(resolved);\n if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${resolved}`);\n return res.arrayBuffer();\n }\n if (resolved instanceof ArrayBuffer) return resolved;\n return resolved.arrayBuffer();\n}\n\n// Loading and glyph lookup both run off the render loop, so an unguarded warn\n// would repeat every frame.\nconst warned = new Set<string>();\n\nfunction warnOnce(key: string, message: string): void {\n if (warned.has(key)) return;\n warned.add(key);\n console.warn(message);\n}\n\n/** @internal Test seam — registry and warn-once keys are module state. */\nexport function _resetFontOutlinesForTests(): void {\n slots = new Map();\n warned.clear();\n}\n","/**\n * Local Font Access → outline registrations.\n *\n * The dynamic canvas-SDF tier can render any installed family without ever\n * seeing its bytes: it asks canvas 2D to `fillText` and takes the pixels. The\n * outline tier cannot — it needs the actual font file — and the only web API\n * that hands one over is `window.queryLocalFonts()`, which is Chromium-only\n * and gated behind a permission prompt that requires a user gesture.\n *\n * So this is the one part of the tier a consumer has to *ask* for, from a\n * click. Everything downstream treats a refusal as ordinary: no\n * registrations, `glyphOutline` keeps answering `null`, and large text keeps\n * rendering from the SDF tier exactly as it does today. That ladder is\n * load-bearing rather than defensive — Safari and Firefox have no equivalent\n * API at all, so \"denied\" is the permanent state for most of the web.\n *\n * Registration is eager but reading is lazy: every matching face gets a thunk\n * that calls `FontData.blob()` at first glyph request. A machine can easily\n * have hundreds of faces and a `.ttc` runs to tens of megabytes, so\n * downloading them all at enable time would be worse than not offering the\n * tier.\n */\n\nimport type { OutlineFontStyle } from './OutlineFace';\nimport { createOpenTypeParser } from './opentypeParser';\nimport { registerFontOutlines } from './outlineRegistry';\n\n/**\n * The slice of the Local Font Access API this module uses. Declared here\n * rather than pulled from a lib: `queryLocalFonts` is not in TypeScript's DOM\n * types, and the alternative — casting `window` to `any` at the call site —\n * would lose the shape of what comes back.\n */\ninterface LocalFontData {\n postscriptName: string;\n fullName: string;\n family: string;\n style: string;\n blob(): Promise<Blob>;\n}\n\ntype LocalFontWindow = typeof globalThis & {\n queryLocalFonts?: (opts?: { postscriptNames?: string[] }) => Promise<LocalFontData[]>;\n};\n\n/** Is the Local Font Access API present at all? False on every non-Chromium\n * browser and in any non-secure context. */\nexport function canQueryLocalFonts(): boolean {\n return typeof globalThis !== 'undefined'\n && typeof (globalThis as LocalFontWindow).queryLocalFonts === 'function';\n}\n\n/**\n * CSS weights for the weight words that appear in a `style` string.\n *\n * Ordered longest-first where one name contains another (\"ExtraBold\" before\n * \"Bold\", \"DemiBold\" before \"Bold\"), because the match is a substring scan\n * and \"Bold\" would otherwise claim every heavier face for 700.\n */\nconst WEIGHT_WORDS: readonly (readonly [string, number])[] = [\n ['extrablack', 950], ['ultrablack', 950],\n ['extrabold', 800], ['ultrabold', 800],\n ['extralight', 200], ['ultralight', 200],\n ['semibold', 600], ['demibold', 600],\n ['semilight', 350],\n ['black', 900], ['heavy', 900],\n ['bold', 700],\n ['medium', 500],\n ['light', 300],\n ['thin', 100], ['hairline', 100],\n ['book', 400], ['regular', 400], ['normal', 400],\n];\n\n/**\n * Parse a `FontData.style` string — \"Regular\", \"Bold Italic\", \"Condensed\n * ExtraLight Oblique\" — into the CSS variant the kit's registry is keyed by.\n *\n * Anything unrecognized lands on 400/normal, which is what the string\n * \"Regular\" would have produced anyway. A wrong guess here costs a face that\n * registers under a variant nothing asks for; it cannot mis-paint anything,\n * because resolution is an exact-match lookup that simply misses.\n */\nexport function parseFontStyle(style: string): { weight: number; style: OutlineFontStyle } {\n const s = style.toLowerCase();\n const italic = s.includes('italic') || s.includes('oblique');\n const weight = WEIGHT_WORDS.find(([word]) => s.includes(word))?.[1] ?? 400;\n return { weight, style: italic ? 'italic' : 'normal' };\n}\n\nexport interface LocalFontOutlinesOptions {\n /**\n * Restrict registration to these families. Defaults to every installed\n * family. Worth passing when the consumer already knows its font menu:\n * a machine with 900 faces otherwise gets 900 registry entries, all inert\n * but all held.\n */\n families?: readonly string[];\n}\n\nexport interface LocalFontOutlinesResult {\n /** Families that now have at least one outline face, in menu order. */\n families: readonly string[];\n /** Total faces registered across those families. */\n faces: number;\n}\n\n/**\n * Ask for permission to read installed fonts, and register outline sources\n * for the faces it returns. **Must be called from a user gesture** — the\n * permission prompt requires one, and without it the call rejects.\n *\n * Rejects when the API is missing or the user denies; both are ordinary\n * outcomes and neither leaves the registry in a partial state, so a caller\n * that simply ignores the rejection still has a working SDF tier.\n */\nexport async function enableLocalFontOutlines(\n opts: LocalFontOutlinesOptions = {},\n): Promise<LocalFontOutlinesResult> {\n const query = (globalThis as LocalFontWindow).queryLocalFonts;\n if (typeof query !== 'function') {\n throw new Error(\n 'weasel enableLocalFontOutlines: this browser has no Local Font Access API ' +\n '(queryLocalFonts). Large text keeps rendering from the SDF tier.',\n );\n }\n\n const wanted = opts.families ? new Set(opts.families) : null;\n const data = await query.call(globalThis);\n\n const families = new Set<string>();\n let faces = 0;\n for (const font of data) {\n if (wanted && !wanted.has(font.family)) continue;\n const variant = parseFontStyle(font.style);\n registerFontOutlines(font.family, variant, () => font.blob(), {\n // The PostScript name is how a member is picked out of a `.ttc`\n // collection, which is what most macOS system families ship as.\n parser: createOpenTypeParser(font.postscriptName),\n });\n families.add(font.family);\n faces++;\n }\n return { families: [...families].sort((a, b) => a.localeCompare(b)), faces };\n}\n","/**\n * GLSL ES 3.0 sources for the built-in MSDF text shader.\n *\n * Vertex inputs (interleaved, stride 20 bytes = 5 × float):\n * a_position vec2 screen-space x,y of the glyph quad vertex\n * a_uv vec2 atlas UV (0..1)\n * a_baselineY float line baseline Y in screen space (for synth-italic skew)\n *\n * Uniforms:\n * u_proj mat3 screen → clip projection\n * u_model mat3 cumulative group transform\n * u_atlas sampler2D the MSDF atlas texture (bound to TEXTURE0)\n * u_color vec4 text color (straight RGBA)\n * u_alpha float group alpha multiplier\n * u_colorMatrix mat4 color transform applied to u_color before alpha modulation\n * u_colorBias vec4 bias added after the matrix (identity = zero bias)\n *\n * Output: PREMULTIPLIED alpha — `vec4(color.rgb * a, a)` per conventions §2.\n * Blend func: ONE / ONE_MINUS_SRC_ALPHA.\n *\n * MSDF channel layout: msdf-bmfont-xml outputs R,G,B channels as independent\n * signed-distance fields covering different edge directions. The true SDF\n * value is the median of R,G,B; this recovers sharp outlines while averaging\n * out single-channel aliasing artifacts.\n *\n * Antialiasing (`aaWidth`, both shaders): the smoothstep band must be one\n * *screen* pixel wide, so it is derived per-fragment from `fwidth(sdfVal)` —\n * the rate the field changes between adjacent fragments. That single quantity\n * already folds in font size, zoom, and DPR: minify the glyph and the field\n * changes faster, so the band widens in field units to stay one pixel on\n * screen; magnify it and the band narrows.\n *\n * A *constant* band cannot be correct at more than one scale, and this shader\n * used one (0.05) until 2026-07-29. At 16px text the band collapsed to well\n * under a pixel and glyph edges quantized to hard stair-steps; at display\n * sizes the same constant read mushy. `fwidth` is core in GLSL ES 3.00, so\n * no extension guard is needed. The `max()` floor keeps a degenerate\n * derivative (flat field, or a driver returning 0) from producing a\n * zero-width band, which would be the aliased behavior all over again.\n */\n\nexport const TEXT_VERT_SRC = /* glsl */ `#version 300 es\nin vec2 a_position;\nin vec2 a_uv;\nin float a_baselineY;\nuniform mat3 u_proj;\nuniform mat3 u_model;\nuniform float u_synthItalic;\nout vec2 v_uv;\nvoid main() {\n // Synthetic italic: shift x by (a_baselineY - a_position.y) * tan(angle).\n // Above-baseline vertices (lower y in screen coords) lean further right.\n vec2 skewed = vec2(\n a_position.x + (a_baselineY - a_position.y) * tan(u_synthItalic),\n a_position.y\n );\n vec3 screen = u_model * vec3(skewed, 1.0);\n vec3 clip = u_proj * vec3(screen.xy, 1.0);\n gl_Position = vec4(clip.xy, 0.0, 1.0);\n v_uv = a_uv;\n}\n`;\n\nexport const TEXT_FRAG_SRC = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 v_uv;\nuniform sampler2D u_atlas;\nuniform vec4 u_color;\nuniform float u_alpha;\nuniform float u_synthBold;\nuniform mat4 u_colorMatrix;\nuniform vec4 u_colorBias;\nout vec4 outColor;\n\nfloat median(float r, float g, float b) {\n return max(min(r, g), min(max(r, g), b));\n}\n\nvoid main() {\n vec3 sdf = texture(u_atlas, v_uv).rgb;\n float sdfVal = median(sdf.r, sdf.g, sdf.b);\n // Screen-space AA band — see the file header. Half of fwidth spans ~1px.\n float aaW = max(0.5 * fwidth(sdfVal), 0.0005);\n // u_synthBold shifts the SDF threshold to thicken strokes when the\n // resolver fell back from a missing bold variant to the regular atlas.\n float threshold = 0.5 - u_synthBold;\n float msdfAlpha = smoothstep(threshold - aaW, threshold + aaW, sdfVal);\n vec4 src = vec4(u_color.rgb, u_color.a);\n vec4 mapped = clamp(u_colorMatrix * src + u_colorBias, 0.0, 1.0);\n float a = mapped.a * msdfAlpha * u_alpha;\n outColor = vec4(mapped.rgb * a, a);\n}\n`;\n\n/**\n * Single-channel sibling of TEXT_FRAG_SRC for runtime canvas-SDF glyphs\n * (DynamicGlyphAtlas R8 pages): the R channel IS the distance field, so no\n * median. Threshold semantics (0.5 edge, u_synthBold shift) match the MSDF\n * shader because the bake encodes the edge at ~128.\n *\n * Accepted trade: corner rounding away from the bake size, mildest near it.\n * `glyphRasterizer.ts` carries the measurements and the reason neither a\n * larger bake nor extra taps would improve the small-text end.\n */\nexport const TEXT_FRAG_R8_SRC = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 v_uv;\nuniform sampler2D u_atlas;\nuniform vec4 u_color;\nuniform float u_alpha;\nuniform float u_synthBold;\nuniform mat4 u_colorMatrix;\nuniform vec4 u_colorBias;\nout vec4 outColor;\n\nvoid main() {\n float sdfVal = texture(u_atlas, v_uv).r;\n // Screen-space AA band — see the file header. Half of fwidth spans ~1px.\n float aaW = max(0.5 * fwidth(sdfVal), 0.0005);\n float threshold = 0.5 - u_synthBold;\n float sdfAlpha = smoothstep(threshold - aaW, threshold + aaW, sdfVal);\n vec4 src = vec4(u_color.rgb, u_color.a);\n vec4 mapped = clamp(u_colorMatrix * src + u_colorBias, 0.0, 1.0);\n float a = mapped.a * sdfAlpha * u_alpha;\n outColor = vec4(mapped.rgb * a, a);\n}\n`;\n\nexport const TEXT_SDF_UNIFORMS = [\n 'u_proj', 'u_model', 'u_atlas', 'u_color', 'u_alpha',\n 'u_synthBold', 'u_synthItalic', 'u_colorMatrix', 'u_colorBias',\n] as const;\n\nexport const TEXT_SDF_ATTRIBUTES = ['a_position', 'a_uv', 'a_baselineY'] as const;\n"]}
1
+ {"version":3,"sources":["../src/FontAtlas.ts","../src/glyphReady.ts","../src/fallback.ts","../src/dynamic/shelfPack.ts","../src/dynamic/distanceTransform.ts","../src/dynamic/glyphRasterizer.ts","../src/dynamic/dynamicAtlas.ts","../src/registerFont.ts","../src/outline/OutlineFace.ts","../src/outline/sfnt.ts","../src/outline/opentypeParser.ts","../src/outline/outlineRegistry.ts","../src/outline/localFonts.ts","../src/textSdf.ts"],"names":["policy","faces"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * BmFont JSON types and parser for the MSDF atlas format produced by\n * msdf-bmfont-xml. The parser builds accelerator maps (charMap, kerningMap)\n * for O(1) glyph and kerning lookup during layout.\n */\n\nexport interface BmFontInfo {\n face: string;\n size: number;\n}\n\nexport interface BmFontCommon {\n lineHeight: number;\n base: number;\n scaleW: number;\n scaleH: number;\n}\n\nexport interface BmFontChar {\n id: number;\n x: number;\n y: number;\n width: number;\n height: number;\n xoffset: number;\n yoffset: number;\n xadvance: number;\n page: number;\n}\n\nexport interface BmFontKerning {\n first: number;\n second: number;\n amount: number;\n}\n\nexport interface BmFont {\n info: BmFontInfo;\n common: BmFontCommon;\n chars: BmFontChar[];\n kernings: BmFontKerning[];\n charMap: Map<number, BmFontChar>;\n kerningMap: Map<number, Map<number, number>>;\n}\n\n/** Two-glyph fixture for unit tests. */\nexport const FIXTURE_FONT = {\n info: { face: 'Inter', size: 32 },\n common: { lineHeight: 38, base: 29, scaleW: 512, scaleH: 512 },\n chars: [\n { id: 65, x: 0, y: 0, width: 22, height: 28, xoffset: 1, yoffset: 4, xadvance: 23, page: 0 },\n { id: 66, x: 24, y: 0, width: 20, height: 28, xoffset: 2, yoffset: 4, xadvance: 22, page: 0 },\n ],\n kernings: [\n { first: 65, second: 66, amount: -1 },\n ],\n};\n\nexport function parseBmFont(raw: unknown): BmFont {\n if (typeof raw !== 'object' || raw === null) throw new Error('parseBmFont: expected object');\n const r = raw as Record<string, unknown>;\n\n if (!r.info || typeof r.info !== 'object') throw new Error('parseBmFont: missing info');\n if (!r.common || typeof r.common !== 'object') throw new Error('parseBmFont: missing common');\n if (!Array.isArray(r.chars)) throw new Error('parseBmFont: chars must be an array');\n\n const info = r.info as BmFontInfo;\n const common = r.common as BmFontCommon;\n const chars = r.chars as BmFontChar[];\n const kernings: BmFontKerning[] = Array.isArray(r.kernings)\n ? (r.kernings as BmFontKerning[])\n : [];\n\n const charMap = new Map<number, BmFontChar>();\n for (const ch of chars) charMap.set(ch.id, ch);\n\n const kerningMap = new Map<number, Map<number, number>>();\n for (const k of kernings) {\n let inner = kerningMap.get(k.first);\n if (!inner) { inner = new Map(); kerningMap.set(k.first, inner); }\n inner.set(k.second, k.amount);\n }\n\n return { info, common, chars, kernings, charMap, kerningMap };\n}\n","/**\n * \"A glyph the renderer asked for can now paint\" — the redraw signal shared\n * by every asynchronous glyph source.\n *\n * Both lazy tiers land their work *after* the frame that asked for it: the\n * dynamic canvas-SDF atlas defers bakes past a per-frame budget, and the\n * outline tier has to fetch and parse font bytes. Neither can paint anything\n * on the frame that first requests a glyph, so both have to tell the canvas\n * to draw again.\n *\n * One subscriber set, not one per tier: a consumer wiring \"redraw when text\n * becomes drawable\" should not have to know how many tiers exist, or grow a\n * second subscription every time one is added. `<SceneCanvas>` subscribes\n * once and both tiers reach it.\n */\n\nconst subscribers = new Set<() => void>();\n\n/** Advanced by every `notifyGlyphReady`, so state can be checked rather than\n * listened for. See {@link glyphGeneration}. */\nlet generation = 0;\n\n/**\n * How many times glyph availability has changed — the pull-based companion to\n * {@link subscribeGlyphReady}.\n *\n * For caches that cannot hold a subscription. Subscribing at module load is a\n * side effect that reaches across a package boundary: it makes importing the\n * module that holds the cache require this package's whole surface, so a\n * consumer partially mocking `@weasel-js/font` breaks at import rather than at\n * use, and import order starts to matter. Comparing a counter has neither\n * problem, and costs a field read on a path that was going to check a cache\n * key anyway.\n *\n * Callers keep the last value they saw and compare; any difference means\n * anything derived from font metrics is stale. The absolute value carries no\n * meaning and may wrap in principle — compare with `!==`, not `<`.\n */\nexport function glyphGeneration(): number {\n return generation;\n}\n\n/**\n * Subscribe to glyph availability. Fires after a batch of deferred SDF bakes\n * completes, and when an outline face finishes loading — in both cases, text\n * that painted nothing (or painted from a lower tier) can now paint. Returns\n * an unsubscribe.\n */\nexport function subscribeGlyphReady(cb: () => void): () => void {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n}\n\n/** @internal Fired by a glyph source once newly-available glyphs can paint. */\nexport function notifyGlyphReady(): void {\n generation++;\n for (const cb of subscribers) cb();\n}\n\n/** @internal Test seam — the subscriber set is module state. */\nexport function _clearGlyphReadySubscribers(): void {\n subscribers.clear();\n}\n","/**\n * Cross-family fallback policy. The per-family chain in `resolveFontVariant`\n * (weight → style → synthetic) has always been rich; what was missing is what\n * happens when the family itself was never registered. That case used to\n * render nothing at all, which is the single most common cause of \"my text is\n * invisible\".\n */\n\n/**\n * What happens when a requested family has no baked atlas:\n * - `'substitute'` — render with the default family (see\n * `setDefaultFontFamily`), reporting the swap on `ResolveResult.substituted`.\n * - `'canvas'` — auto-enroll the family with the dynamic canvas-SDF\n * rasterizer. The consumer gets the *real* typeface if the browser has it,\n * at canvas-SDF quality rather than baked-MSDF quality.\n * - `'none'` — hard miss; the run renders nothing.\n */\nexport type FontFallbackPolicy = 'substitute' | 'canvas' | 'none';\n\nlet policy: FontFallbackPolicy = 'substitute';\nlet defaultFamily: string | null = null;\n\nexport function setFontFallbackPolicy(next: FontFallbackPolicy): void {\n policy = next;\n}\n\nexport function getFontFallbackPolicy(): FontFallbackPolicy {\n return policy;\n}\n\n/** Explicit default family for `'substitute'`. When unset, the first\n * registered family wins — the right answer for the common case of an app\n * that registers exactly one. */\nexport function setDefaultFontFamily(family: string): void {\n defaultFamily = family;\n}\n\nexport function getDefaultFontFamily(): string | null {\n return defaultFamily;\n}\n\n/**\n * Warn-once gate for the cross-family fallback warnings. The messages are\n * built in `registerFont.ts` (they read the registry to name the actual gap),\n * but the dedup state lives here so `_resetFallbackForTests` clears it —\n * warnings about a fallback are fallback state. While it sat in\n * `registerFont.ts`, only a registry reset cleared it, and a test that reset\n * just the policy silently swallowed its next warning.\n */\nconst warnedFallbacks = new Set<string>();\n\n/** @internal Claim `key` for a one-time warning; true the first time only. */\nexport function claimFallbackWarning(key: string): boolean {\n if (warnedFallbacks.has(key)) return false;\n warnedFallbacks.add(key);\n return true;\n}\n\n/** @internal Also called by `_resetFontRegistryForTests`: dropping the fonts\n * a warning was about has to drop the warning too. */\nexport function _clearFallbackWarnings(): void {\n warnedFallbacks.clear();\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetFallbackForTests(): void {\n policy = 'substitute';\n defaultFamily = null;\n warnedFallbacks.clear();\n}\n","/**\n * Shelf packer for the dynamic glyph atlas: fixed-size square pages, rows\n * (\"shelves\") of same-ish-height rects filled left to right. Simple and\n * good enough for glyph rects; no rotation, no eviction (v1).\n */\n\nexport interface ShelfAlloc {\n page: number;\n x: number;\n y: number;\n}\n\ninterface Shelf { y: number; height: number; x: number; }\ninterface Page { shelves: Shelf[]; nextY: number; }\n\nexport class ShelfPacker {\n private pages: Page[] = [];\n private warned = false;\n\n constructor(\n private readonly pageSize: number,\n private readonly maxPages: number,\n ) {}\n\n get pageCount(): number {\n return this.pages.length;\n }\n\n /** Allocate a w×h rect. Returns null (warning once) when capacity is out. */\n alloc(w: number, h: number): ShelfAlloc | null {\n if (w > this.pageSize || h > this.pageSize) return this.fail();\n for (let p = 0; p < this.pages.length; p++) {\n const spot = this.allocInPage(this.pages[p], w, h);\n if (spot) return { page: p, ...spot };\n }\n if (this.pages.length < this.maxPages) {\n const page: Page = { shelves: [], nextY: 0 };\n this.pages.push(page);\n const spot = this.allocInPage(page, w, h);\n if (spot) return { page: this.pages.length - 1, ...spot };\n }\n return this.fail();\n }\n\n private allocInPage(page: Page, w: number, h: number): { x: number; y: number } | null {\n for (const shelf of page.shelves) {\n if (h <= shelf.height && shelf.x + w <= this.pageSize) {\n const x = shelf.x;\n shelf.x += w;\n return { x, y: shelf.y };\n }\n }\n if (page.nextY + h <= this.pageSize) {\n const shelf: Shelf = { y: page.nextY, height: h, x: w };\n page.shelves.push(shelf);\n page.nextY += h;\n return { x: 0, y: shelf.y };\n }\n return null;\n }\n\n private fail(): null {\n if (!this.warned) {\n this.warned = true;\n console.warn(\n `weasel DynamicGlyphAtlas: glyph pages full (${this.maxPages} × ${this.pageSize}²); ` +\n 'further dynamic glyphs will not render.',\n );\n }\n return null;\n }\n}\n","/**\n * Euclidean distance transform for runtime SDF glyph baking (the TinySDF\n * technique): antialiased canvas coverage in → single-channel SDF bytes out.\n *\n * Uses Felzenszwalb & Huttenlocher's 1D squared-distance transform applied\n * along columns then rows. Pure JS, no dependencies, no allocation beyond\n * the work arrays sized to the input.\n */\n\nconst INF = 1e20;\n\n/** One 1D pass of the squared EDT (Felzenszwalb & Huttenlocher 2012, §2). */\nfunction edt1d(\n f: Float64Array, d: Float64Array, v: Int32Array, z: Float64Array, n: number,\n): void {\n v[0] = 0;\n z[0] = -INF;\n z[1] = INF;\n let k = 0;\n for (let q = 1; q < n; q++) {\n let s = (f[q] + q * q - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);\n while (s <= z[k]) {\n k--;\n s = (f[q] + q * q - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);\n }\n k++;\n v[k] = q;\n z[k] = s;\n z[k + 1] = INF;\n }\n k = 0;\n for (let q = 0; q < n; q++) {\n while (z[k + 1] < q) k++;\n d[q] = (q - v[k]) * (q - v[k]) + f[v[k]];\n }\n}\n\n/** In-place 2D squared EDT over `grid` (width × height): columns, then rows. */\nfunction edt2d(\n grid: Float64Array, width: number, height: number,\n f: Float64Array, d: Float64Array, v: Int32Array, z: Float64Array,\n): void {\n for (let x = 0; x < width; x++) {\n for (let y = 0; y < height; y++) f[y] = grid[y * width + x];\n edt1d(f, d, v, z, height);\n for (let y = 0; y < height; y++) grid[y * width + x] = d[y];\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) f[x] = grid[y * width + x];\n edt1d(f, d, v, z, width);\n for (let x = 0; x < width; x++) grid[y * width + x] = d[x];\n }\n}\n\n/**\n * Convert an alpha-coverage bitmap (0–255) to single-channel SDF bytes.\n * Signed distance is positive outside the shape; the output maps it as\n * `255 − 255·(dist/radius + cutoff)` clamped to [0,255], so with\n * cutoff 0.5 the shape edge lands at byte ~128 (shader threshold 0.5).\n */\nexport function alphaToSdf(\n alpha: Uint8ClampedArray | Uint8Array,\n width: number,\n height: number,\n radius: number,\n cutoff: number,\n): Uint8Array {\n const n = width * height;\n const gridOuter = new Float64Array(n);\n const gridInner = new Float64Array(n);\n const size = Math.max(width, height);\n const f = new Float64Array(size);\n const d = new Float64Array(size);\n const v = new Int32Array(size);\n const z = new Float64Array(size + 1);\n\n for (let i = 0; i < n; i++) {\n const a = alpha[i] / 255;\n gridOuter[i] = a === 1 ? 0 : a === 0 ? INF : Math.max(0, 0.5 - a) ** 2;\n gridInner[i] = a === 1 ? INF : a === 0 ? 0 : Math.max(0, a - 0.5) ** 2;\n }\n edt2d(gridOuter, width, height, f, d, v, z);\n edt2d(gridInner, width, height, f, d, v, z);\n\n const out = new Uint8Array(n);\n for (let i = 0; i < n; i++) {\n const dist = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);\n const byte = Math.round(255 - 255 * (dist / radius + cutoff));\n out[i] = byte < 0 ? 0 : byte > 255 ? 255 : byte;\n }\n return out;\n}\n","/**\n * Canvas-2D glyph rasterizer for the dynamic SDF atlas. `fillText` is the\n * only web-platform route to *installed* machine fonts (no outline access),\n * which is exactly why this tier exists.\n *\n * Coordinate contract (all values in bake-size px, i.e. at BAKE_SIZE):\n * bitmap left = penX + left (left is negative: pad + left bearing)\n * bitmap top = baseline − top (top is positive above the baseline)\n * pen advance = advance\n * The bitmap includes a PAD border on all four sides so the SDF field has\n * room to fall off. Blank glyphs (e.g. space) return width/height 0.\n *\n * On BAKE_SIZE — measured 2026-07-29, comparing the reconstructed field\n * against a direct `fillText` of the same glyph at each display size, with\n * sub-pixel registration searched out so it measures fidelity and not\n * alignment. Mean per-pixel coverage error, averaged over A/S/e/W/8:\n *\n * display px 12 16 24 32 48 64 96 128\n * error .096 .083 .059 .039 .024 .023 .037 .049\n *\n * Two things follow, both of them counterintuitive enough to be worth\n * writing down. The error is minimized *at* the bake size, so raising\n * BAKE_SIZE does not improve small text — it moves the sweet spot away from\n * the 12–32px range where UI text actually lives and makes it worse. And the\n * magnification side is the mild one: 128px costs about twice the floor,\n * which is the corner rounding the shader header already owns.\n *\n * The 12–16px end is the largest divergence, and it is not undersampling —\n * 3×3 supersampling the reconstruction roughly halves the error at 24–48px\n * but recovers almost nothing at 12–16px (.096 → .089, .083 → .068). What is\n * left there is a hinted rasterizer placing stems on the pixel grid, which no\n * size-independent field can encode. So there is nothing to buy with more\n * taps, a bigger bake, or mipmaps (which a packed atlas cannot have anyway —\n * mip levels blend across glyph rects).\n */\n\nexport const BAKE_SIZE = 48;\nexport const PAD = 8;\n\nexport interface FaceMetrics {\n ascent: number;\n descent: number;\n}\n\nexport interface RasterizedGlyph {\n width: number;\n height: number;\n alpha: Uint8ClampedArray;\n left: number;\n top: number;\n advance: number;\n}\n\nexport interface GlyphRasterizer {\n faceMetrics(family: string, weight: number, style: 'normal' | 'italic'): FaceMetrics;\n rasterize(\n family: string, weight: number, style: 'normal' | 'italic', codepoint: number,\n ): RasterizedGlyph;\n}\n\ntype Canvas2D = OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;\n\nfunction cssFontString(weight: number, style: 'normal' | 'italic', family: string): string {\n return `${style === 'italic' ? 'italic ' : ''}${weight} ${BAKE_SIZE}px ${JSON.stringify(family)}`;\n}\n\nexport function createCanvasRasterizer(): GlyphRasterizer {\n let canvas: OffscreenCanvas | HTMLCanvasElement;\n if (typeof OffscreenCanvas !== 'undefined') {\n canvas = new OffscreenCanvas(BAKE_SIZE * 3, BAKE_SIZE * 3);\n } else if (typeof document !== 'undefined') {\n canvas = document.createElement('canvas');\n canvas.width = BAKE_SIZE * 3;\n canvas.height = BAKE_SIZE * 3;\n } else {\n throw new Error('weasel DynamicGlyphAtlas: no canvas available for glyph rasterization');\n }\n const ctx = canvas.getContext('2d', { willReadFrequently: true }) as Canvas2D | null;\n if (!ctx) throw new Error('weasel DynamicGlyphAtlas: 2D context unavailable');\n\n function setFont(family: string, weight: number, style: 'normal' | 'italic'): void {\n ctx!.font = cssFontString(weight, style, family);\n ctx!.textBaseline = 'alphabetic';\n ctx!.textAlign = 'left';\n }\n\n return {\n faceMetrics(family, weight, style) {\n setFont(family, weight, style);\n const m = ctx!.measureText('Hg');\n return {\n ascent: m.fontBoundingBoxAscent ?? BAKE_SIZE * 0.8,\n descent: m.fontBoundingBoxDescent ?? BAKE_SIZE * 0.2,\n };\n },\n\n rasterize(family, weight, style, codepoint) {\n setFont(family, weight, style);\n const chStr = String.fromCodePoint(codepoint);\n const m = ctx!.measureText(chStr);\n const advance = m.width;\n const inkLeft = Math.ceil(m.actualBoundingBoxLeft ?? 0);\n const inkRight = Math.ceil(m.actualBoundingBoxRight ?? advance);\n const inkAscent = Math.ceil(m.actualBoundingBoxAscent ?? BAKE_SIZE * 0.8);\n const inkDescent = Math.ceil(m.actualBoundingBoxDescent ?? BAKE_SIZE * 0.2);\n const inkW = inkLeft + inkRight;\n const inkH = inkAscent + inkDescent;\n if (inkW <= 0 || inkH <= 0) {\n return { width: 0, height: 0, alpha: new Uint8ClampedArray(0), left: 0, top: 0, advance };\n }\n const w = inkW + 2 * PAD;\n const h = inkH + 2 * PAD;\n if (canvas.width < w || canvas.height < h) {\n canvas.width = Math.max(canvas.width, w);\n canvas.height = Math.max(canvas.height, h);\n setFont(family, weight, style); // resizing resets 2D state\n }\n ctx!.clearRect(0, 0, w, h);\n ctx!.fillStyle = '#fff';\n ctx!.fillText(chStr, PAD + inkLeft, PAD + inkAscent);\n const img = ctx!.getImageData(0, 0, w, h);\n const alpha = new Uint8ClampedArray(w * h);\n for (let i = 0; i < alpha.length; i++) alpha[i] = img.data[i * 4 + 3];\n return {\n width: w,\n height: h,\n alpha,\n left: -(inkLeft + PAD),\n top: inkAscent + PAD,\n advance,\n };\n },\n };\n}\n","/**\n * DynamicGlyphAtlas — runtime single-channel SDF glyphs for canvas-sourced\n * (installed machine) fonts. TinySDF technique: canvas fillText at 48 px →\n * Euclidean distance transform → shelf-packed R8 pages (1024², max 4).\n *\n * Baked MSDF atlases always win: this tier only serves families registered\n * via `registerCanvasFont` that have no baked entry (see resolveFontVariant).\n *\n * Faces expose a BmFont-shaped `font` whose charMap grows as glyphs are\n * requested, so `layoutRuns` consumes them through the same code path as\n * baked atlases. A char's advance is valid immediately (measureText); its\n * atlas rect fills in when the bake lands (width 0 / page -1 until then, so\n * layout advances the pen but emits no quad).\n */\n\nimport type { BmFont, BmFontChar } from '../FontAtlas';\nimport type { GlyphTextureSink } from '../textureSink';\n// `fallback.ts` imports nothing, so this edge cannot close a cycle; keep it\n// that way. (registerFont.ts imports both this module and fallback.ts.)\nimport { getFontFallbackPolicy } from '../fallback';\nimport { notifyGlyphReady, _clearGlyphReadySubscribers } from '../glyphReady';\nimport { ShelfPacker } from './shelfPack';\nimport { alphaToSdf } from './distanceTransform';\nimport {\n createCanvasRasterizer, BAKE_SIZE,\n type GlyphRasterizer, type RasterizedGlyph,\n} from './glyphRasterizer';\n\nexport const PAGE_SIZE = 1024;\nexport const MAX_PAGES = 4;\nexport const SDF_RADIUS = 8;\nexport const SDF_CUTOFF = 0.5;\nexport const DEFAULT_BAKE_BUDGET = 16;\n\nexport interface DynamicFace {\n family: string;\n weight: number;\n style: 'normal' | 'italic';\n /** BmFont-shaped view consumed by layoutRuns; charMap grows lazily. */\n font: BmFont;\n /** Char record for `cp`, measured on first request (advance always valid\n * immediately; atlas rect fills in when the bake lands). */\n requestGlyph(cp: number): BmFontChar;\n}\n\ninterface DynamicPage {\n data: Uint8Array;\n /** Bumps on every glyph blit; each blit appends a patch with seq = version. */\n version: number;\n patches: { seq: number; x: number; y: number; w: number; h: number }[];\n}\n\ninterface PendingBake { char: BmFontChar; raster: RasterizedGlyph; }\n\n/** One family served by the dynamic canvas-SDF tier. See `listCanvasFonts`. */\nexport interface CanvasFontEntry {\n family: string;\n /** `'explicit'` — a `registerCanvasFont` call; `'auto'` — the `'canvas'`\n * fallback policy enrolled it on a miss, and the enrollment lapses if\n * that policy changes. */\n enrollment: 'explicit' | 'auto';\n}\n\nconst canvasFamilies = new Set<string>();\n// Enrollment provenance. A family the `'canvas'` fallback policy enrolled on\n// its own is only canvas-served while that policy is in force; one a consumer\n// named via registerCanvasFont outranks every policy. Tracked here rather\n// than in fallback.ts so the two sets can only ever be mutated together —\n// every register / unregister / reset path is in this file.\nconst autoEnrolledFamilies = new Set<string>();\nlet faces = new Map<string, DynamicFace>();\nlet pages: DynamicPage[] = [];\nlet packer = new ShelfPacker(PAGE_SIZE, MAX_PAGES);\nlet pending: PendingBake[] = [];\nlet flushScheduled = false;\nlet budget = DEFAULT_BAKE_BUDGET;\nlet rasterizer: GlyphRasterizer | null = null;\n\nfunction getRasterizer(): GlyphRasterizer {\n if (!rasterizer) rasterizer = createCanvasRasterizer();\n return rasterizer;\n}\n\n/** Mark `family` as canvas-sourced: when no baked atlas covers it,\n * resolveFontVariant serves it from this dynamic atlas. */\nexport function registerCanvasFont(family: string): void {\n canvasFamilies.add(family);\n // An explicit call promotes a previously auto-enrolled family for good.\n autoEnrolledFamilies.delete(family);\n}\n\n/**\n * Will `family` be served by the dynamic canvas-SDF tier *right now*?\n *\n * Service, not membership — the answer depends on the fallback policy in\n * force and can change without any enrollment call:\n * - explicitly enrolled via `registerCanvasFont` → `true` under every\n * policy; a consumer naming a family outranks the policy.\n * - auto-enrolled by the `'canvas'` policy → `true` only while that policy\n * is still in force. The enrollment lapses rather than being discarded,\n * so returning to `'canvas'` makes it `true` again.\n * - never enrolled → `false`, including under `'canvas'` (that policy\n * enrolls lazily, on the first miss).\n *\n * The membership reading would answer `true` for an auto-enrolled family\n * under `'substitute'` / `'none'`, where nothing routes to this tier — a\n * caller predicting what renders would be told the opposite of the truth.\n * Mirrors `listFonts`, which reports the baked registry alone for the same\n * reason: \"enrolled\" is not \"will render\".\n */\nexport function isCanvasFont(family: string): boolean {\n if (!canvasFamilies.has(family)) return false;\n return !autoEnrolledFamilies.has(family) || getFontFallbackPolicy() === 'canvas';\n}\n\n/**\n * Every family the dynamic canvas-SDF tier will serve *right now* — the\n * enumeration companion to `isCanvasFont`, which answers one family at a\n * time. Same \"service, not membership\" rule: an auto-enrolled family appears\n * only while the `'canvas'` policy is in force.\n *\n * Exists so a consumer can offer these families in a font picker.\n * `listFonts` reports the baked registry alone, so without this the only\n * ways to present canvas-served families were to hard-code a list beside the\n * `registerCanvasFont` calls or to omit them — a UI inventing its own answer\n * about what will render.\n *\n * `enrollment` distinguishes a consumer's explicit `registerCanvasFont` from\n * a lazy enrollment the policy made on a miss; both render identically, but\n * only the explicit one survives a policy change.\n */\nexport function listCanvasFonts(): readonly CanvasFontEntry[] {\n const out: CanvasFontEntry[] = [];\n for (const family of canvasFamilies) {\n if (!isCanvasFont(family)) continue;\n out.push({ family, enrollment: autoEnrolledFamilies.has(family) ? 'auto' : 'explicit' });\n }\n return out.sort((a, b) => a.family.localeCompare(b.family));\n}\n\n/**\n * @internal Enroll `family` on behalf of the `'canvas'` fallback policy.\n * Distinct from `registerCanvasFont` only in provenance: the enrollment\n * lapses (see `isExplicitCanvasFont`) if the policy later changes.\n */\nexport function autoEnrollCanvasFont(family: string): void {\n if (canvasFamilies.has(family) && !autoEnrolledFamilies.has(family)) return;\n canvasFamilies.add(family);\n autoEnrolledFamilies.add(family);\n}\n\n/**\n * @internal Enrolled by a consumer's own `registerCanvasFont` call rather than\n * by the `'canvas'` policy. Only these are served by the dynamic tier\n * regardless of the policy in force; an auto-enrolled family falls back to\n * whatever the current policy says once that policy is no longer `'canvas'`.\n */\nexport function isExplicitCanvasFont(family: string): boolean {\n return canvasFamilies.has(family) && !autoEnrolledFamilies.has(family);\n}\n\n/** Remove a canvas family. Its faces are dropped; already-baked glyph\n * pixels stay in their pages (no eviction in v1). */\nexport function unregisterCanvasFont(family: string): void {\n canvasFamilies.delete(family);\n autoEnrolledFamilies.delete(family);\n for (const key of [...faces.keys()]) {\n if (faces.get(key)!.family === family) faces.delete(key);\n }\n}\n\n/**\n * Internal resolver helper — not part of the public barrel; consumers go\n * through registerCanvasFont + resolveFontVariant.\n */\nexport function getDynamicFace(\n family: string, weight: number, style: 'normal' | 'italic',\n): DynamicFace {\n const key = `${family}|${weight}|${style}`;\n const existing = faces.get(key);\n if (existing) return existing;\n\n const r = getRasterizer();\n const metrics = r.faceMetrics(family, weight, style);\n const base = Math.round(metrics.ascent);\n const font: BmFont = {\n info: { face: family, size: BAKE_SIZE },\n common: {\n lineHeight: Math.round(metrics.ascent + metrics.descent),\n base,\n scaleW: PAGE_SIZE,\n scaleH: PAGE_SIZE,\n },\n chars: [],\n kernings: [], // no kerning in v1 (measured-pair kerning is future work)\n charMap: new Map(),\n kerningMap: new Map(),\n };\n\n const face: DynamicFace = {\n family, weight, style, font,\n requestGlyph(cp: number): BmFontChar {\n const cached = font.charMap.get(cp);\n if (cached) return cached;\n const raster = r.rasterize(family, weight, style, cp);\n const char: BmFontChar = {\n id: cp, x: 0, y: 0, width: 0, height: 0,\n xoffset: raster.left,\n yoffset: base - raster.top,\n xadvance: raster.advance,\n page: -1,\n };\n font.charMap.set(cp, char);\n font.chars.push(char);\n if (raster.width === 0 || raster.height === 0) {\n char.page = 0; // blank glyph (space): nothing to bake\n return char;\n }\n if (budget > 0) {\n budget--;\n bake(char, raster);\n } else {\n pending.push({ char, raster });\n scheduleFlush();\n }\n return char;\n },\n };\n faces.set(key, face);\n return face;\n}\n\nfunction bake(char: BmFontChar, raster: RasterizedGlyph): void {\n const spot = packer.alloc(raster.width, raster.height);\n if (!spot) return; // pages full — packer warned once; glyph stays invisible\n const sdf = alphaToSdf(raster.alpha, raster.width, raster.height, SDF_RADIUS, SDF_CUTOFF);\n while (pages.length <= spot.page) {\n pages.push({ data: new Uint8Array(PAGE_SIZE * PAGE_SIZE), version: 0, patches: [] });\n }\n const page = pages[spot.page];\n for (let row = 0; row < raster.height; row++) {\n page.data.set(\n sdf.subarray(row * raster.width, (row + 1) * raster.width),\n (spot.y + row) * PAGE_SIZE + spot.x,\n );\n }\n page.version++;\n page.patches.push({ seq: page.version, x: spot.x, y: spot.y, w: raster.width, h: raster.height });\n char.x = spot.x;\n char.y = spot.y;\n char.width = raster.width;\n char.height = raster.height;\n char.page = spot.page;\n}\n\nfunction scheduleFlush(): void {\n if (flushScheduled) return;\n flushScheduled = true;\n setTimeout(flushPending, 0);\n}\n\nfunction flushPending(): void {\n flushScheduled = false;\n let n = 0;\n while (pending.length > 0 && n < DEFAULT_BAKE_BUDGET) {\n const job = pending.shift()!;\n if (job.char.page !== -1) continue; // already baked\n bake(job.char, job.raster);\n n++;\n }\n if (pending.length > 0) scheduleFlush();\n notifyGlyphReady();\n}\n\n/** Reset the per-frame synchronous bake budget. Called by\n * `WeaselRenderer.render()` at frame start; the headless\n * `renderSceneToPixels` path passes Infinity so print never defers. */\nexport function resetBakeBudget(n: number = DEFAULT_BAKE_BUDGET): void {\n budget = n;\n}\n\n/* ------------------------------------------------------------------ */\n/* GPU sync */\n/* ------------------------------------------------------------------ */\n\n/** Texture-cache key for a dynamic page (parallel to `textureCacheKey`). */\nexport function dynamicPageTextureId(page: number): string {\n return `weasel-dyn-sdf-page-${page}`;\n}\n\n// Per-GL-cache upload progress: last page version each cache has seen.\n// WeakMap-keyed so disposed renderers don't pin anything.\nconst uploadedVersions = new WeakMap<GlyphTextureSink, Map<number, number>>();\n\n/** Bring `cache`'s copy of page `pageIndex` up to date: full R8 upload the\n * first time, `texSubImage2D` patches after. Returns false if the page\n * doesn't exist yet. */\nexport function syncDynamicPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean {\n const page = pages[pageIndex];\n if (!page) return false;\n const id = dynamicPageTextureId(pageIndex);\n let seen = uploadedVersions.get(cache);\n if (!seen) {\n seen = new Map();\n uploadedVersions.set(cache, seen);\n }\n if (!cache.has(id)) {\n cache.uploadR8(id, PAGE_SIZE, PAGE_SIZE, page.data);\n seen.set(pageIndex, page.version);\n return true;\n }\n const last = seen.get(pageIndex) ?? 0;\n if (last >= page.version) return true;\n for (const patch of page.patches) {\n if (patch.seq <= last) continue;\n const tight = new Uint8Array(patch.w * patch.h);\n for (let row = 0; row < patch.h; row++) {\n const src = (patch.y + row) * PAGE_SIZE + patch.x;\n tight.set(page.data.subarray(src, src + patch.w), row * patch.w);\n }\n cache.subImageR8(id, patch.x, patch.y, patch.w, patch.h, tight);\n }\n seen.set(pageIndex, page.version);\n return true;\n}\n\n/* ------------------------------------------------------------------ */\n/* Test seams */\n/* ------------------------------------------------------------------ */\n\n/** @internal test seam — inject a fake rasterizer (jsdom has no canvas\n * metrics). Pass null to restore the lazy default. */\nexport function __setGlyphRasterizerForTests(r: GlyphRasterizer | null): void {\n rasterizer = r;\n}\n\n/** @internal test seam — inspect CPU-side pages. */\nexport function _getPagesForTests(): readonly DynamicPage[] {\n return pages;\n}\n\n/** @internal test seam — clear all dynamic-font state. */\nexport function _resetDynamicFontsForTests(): void {\n canvasFamilies.clear();\n autoEnrolledFamilies.clear();\n faces = new Map();\n pages = [];\n packer = new ShelfPacker(PAGE_SIZE, MAX_PAGES);\n pending = [];\n flushScheduled = false;\n budget = DEFAULT_BAKE_BUDGET;\n _clearGlyphReadySubscribers();\n rasterizer = null;\n}\n","/**\n * FontRegistry and registerFont() public API.\n *\n * Variants are keyed by (family, weight, style). registerFont() takes a\n * FontVariant alongside the family and the two URLs; the registry stores\n * entries in a two-level Map so resolveFontVariant() can iterate a family's\n * variants for the fallback chain.\n */\n\nimport { notifyGlyphReady } from './glyphReady';\nimport { parseBmFont, type BmFont } from './FontAtlas';\nimport type { GlyphTextureSink } from './textureSink';\nimport {\n isCanvasFont, isExplicitCanvasFont, autoEnrollCanvasFont, getDynamicFace,\n type DynamicFace,\n} from './dynamic/dynamicAtlas';\nimport {\n getFontFallbackPolicy, getDefaultFontFamily,\n claimFallbackWarning, _clearFallbackWarnings,\n} from './fallback';\n\nexport interface FontEntry {\n font: BmFont;\n bitmap: ImageBitmap;\n}\n\nexport interface FontVariant {\n weight?: number;\n style?: 'normal' | 'italic';\n}\n\ntype FontStyle = 'normal' | 'italic';\n\nlet registry = new Map<string, Map<string, FontEntry>>();\n\nfunction variantKey(weight: number, style: FontStyle): string {\n return `${weight}|${style}`;\n}\n\nfunction normalizeVariant(v: FontVariant): { weight: number; style: FontStyle } {\n return {\n weight: v.weight ?? 400,\n style: v.style ?? 'normal',\n };\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetFontRegistryForTests(): void {\n registry = new Map();\n _clearFallbackWarnings();\n}\n\n/** Exact lookup — does NOT walk the fallback chain. Use `resolveFontVariant` for that. */\nexport function getFont(\n family: string,\n weight: number = 400,\n style: FontStyle = 'normal',\n): FontEntry | null {\n return registry.get(family)?.get(variantKey(weight, style)) ?? null;\n}\n\nexport interface RegisteredFont {\n family: string;\n variants: readonly { weight: number; style: FontStyle }[];\n}\n\n/**\n * Enumerate the registry — what a font picker can honestly offer. Families\n * come back in registration order; variants sorted by weight, then style, so\n * the output is stable enough to assert against.\n */\nexport function listFonts(): readonly RegisteredFont[] {\n const out: RegisteredFont[] = [];\n for (const [family, variantMap] of registry) {\n const variants = [...variantMap.keys()]\n .map((key) => {\n const [w, s] = key.split('|') as [string, FontStyle];\n return { weight: Number(w), style: s };\n })\n .sort((a, b) => a.weight - b.weight || a.style.localeCompare(b.style));\n out.push({ family, variants });\n }\n return out;\n}\n\nexport async function registerFont(\n family: string,\n variant: FontVariant,\n metricsUrl: string,\n atlasUrl: string,\n): Promise<void> {\n const { weight, style } = normalizeVariant(variant);\n const key = variantKey(weight, style);\n\n if (registry.get(family)?.has(key)) return;\n\n try {\n const [metricsRes, atlasRes] = await Promise.all([\n fetch(metricsUrl),\n fetch(atlasUrl),\n ]);\n\n if (!metricsRes.ok) {\n throw new Error(`HTTP ${metricsRes.status} fetching metrics from ${metricsUrl}`);\n }\n if (!atlasRes.ok) {\n throw new Error(`HTTP ${atlasRes.status} fetching atlas from ${atlasUrl}`);\n }\n\n const [rawJson, blob] = await Promise.all([\n metricsRes.json(),\n atlasRes.blob(),\n ]);\n\n const font = parseBmFont(rawJson);\n const bitmap = await createImageBitmap(blob);\n\n // Re-read the registry here (not the pre-await snapshot): concurrent\n // registerFont() calls for other variants of the same family (e.g. the\n // 400/700 weights registered together in Promise.all) may have created\n // the family's Map while this call was awaiting its fetch. Reusing a\n // stale local reference would recreate the Map and silently drop\n // whichever variant's registerFont() resolved first.\n let familyMap = registry.get(family);\n if (!familyMap) {\n familyMap = new Map();\n registry.set(family, familyMap);\n }\n familyMap.set(key, { font, bitmap });\n // Same meaning the lazy tiers give it: text that was painting from a\n // fallback face — or painting nothing — can now paint from this one. The\n // early return above covers the already-registered case, so this fires\n // once per variant that actually lands.\n notifyGlyphReady();\n } catch (err) {\n throw new Error(\n `weasel registerFont(\"${family}\" ${weight}/${style}): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\n/**\n * Ensure the atlas for `(family, weight, style)` is uploaded to\n * `textureCache`. Cache key is `${family}|${weight}|${style}` so each\n * variant occupies its own texture slot.\n */\nexport function ensureFontTexture(\n family: string,\n weight: number,\n style: FontStyle,\n textureCache: GlyphTextureSink,\n): boolean {\n const entry = getFont(family, weight, style);\n if (!entry) return false;\n textureCache.upload(textureCacheKey(family, weight, style), entry.bitmap);\n return true;\n}\n\n/** The texture cache key used by `ensureFontTexture` for a given variant. */\nexport function textureCacheKey(family: string, weight: number, style: FontStyle): string {\n return `${family}|${weight}|${style}`;\n}\n\n/** Kept as a no-op for context-restore call sites; per-cache dedup handles it now. */\nexport function markAllFontsNotUploaded(): void {}\n\nexport interface ResolveResult {\n entry: FontEntry | null;\n /**\n * The (family, weight, style) triple that was actually matched. May differ\n * from the requested values when the resolver walked the fallback chain —\n * including `family`, when the cross-family policy substituted a default.\n *\n * This is the atlas identity: pass all three to `getFont` /\n * `textureCacheKey` and the lookup hits. Describing only weight and style\n * here once let a caller key its draw on the *requested* family, which\n * resolves to no atlas at all and paints nothing.\n *\n * The synthetic flags describe the gap between requested and resolved for\n * shader-side compensation. When `entry` is null, these mirror the\n * requested values.\n */\n resolved: { family: string; weight: number; style: FontStyle };\n synthetic: { bold: boolean; italic: boolean };\n /** Which tier resolved: a baked MSDF atlas, or the runtime canvas-SDF\n * dynamic atlas. Misses report 'atlas' (the default tier). */\n source: 'atlas' | 'canvas';\n /** Set only when source === 'canvas': the dynamic face whose BmFont-shaped\n * `font` layoutRuns consumes in place of `entry.font`. */\n dynamicFace?: DynamicFace;\n /**\n * Set when the requested family was not registered and the fallback policy\n * substituted a different one. Reported structurally so a UI can say\n * \"Inter — not loaded, showing Roboto\" instead of leaving the user to\n * wonder why the family control did nothing.\n */\n substituted?: { requested: string; resolved: string };\n}\n\nfunction missResolveResult(\n family: string, weight: number, style: FontStyle, suppressWarn = false,\n): ResolveResult {\n // Canvas-dynamic tier: reached only when the fallback chain selected no\n // baked variant, so any selected baked match always wins. Dynamic faces\n // rasterize the real weight/style — no synthetic flags.\n //\n // Explicit enrollment only. A family the `'canvas'` policy enrolled for\n // itself must not keep routing here after the policy changes, or `'none'`\n // could never restore the hard miss it documents once any family had been\n // auto-enrolled.\n if (isExplicitCanvasFont(family)) {\n return {\n entry: null,\n dynamicFace: getDynamicFace(family, weight, style),\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'canvas',\n };\n }\n\n const policy = getFontFallbackPolicy();\n\n if (policy === 'canvas') {\n // Auto-enroll: the browser probably has this family even though no atlas\n // was baked for it. Real typeface, canvas-SDF quality.\n autoEnrollCanvasFont(family);\n return {\n entry: null,\n dynamicFace: getDynamicFace(family, weight, style),\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'canvas',\n };\n }\n\n if (policy === 'substitute') {\n const fallback = getDefaultFontFamily() ?? firstRegisteredFamily();\n // Guard against recursing when the default family is itself unknown.\n // Canvas families never enter `registry`, so membership there alone would\n // reject `setDefaultFontFamily` pointed at one. `isCanvasFont` answers\n // \"served by the dynamic tier right now\", which is the question here: a\n // family the `'canvas'` policy auto-enrolled is not served under this\n // policy, so substituting *to* it would report a swap that paints nothing.\n if (fallback !== null && fallback !== family) {\n if (registry.has(fallback) || isCanvasFont(fallback)) {\n // Probing whether the fallback family renders, not resolving a\n // top-level request for it — suppress so this recursion can't also\n // land in the `fallback === family` branch below (the fallback\n // family's own default is itself) and double-warn for one miss.\n // Whichever of `warnMissingFamilyOnce` / `warnUnusableDefaultOnce`\n // fires below, based on this probe's result, is the single warning.\n const result = resolveFontVariantInternal(fallback, weight, style, true);\n // Renderable, not baked: a fallback served by the dynamic tier reports\n // `entry: null` with a dynamicFace, and testing entry alone threw it away.\n if (result.entry !== null || result.dynamicFace !== undefined) {\n if (!suppressWarn) warnMissingFamilyOnce(family, weight, style, fallback);\n return { ...result, substituted: { requested: family, resolved: fallback } };\n }\n }\n // Substitution was supposed to happen and produced nothing. Falling\n // through silently here is the invisible-text failure this whole policy\n // exists to eliminate, so say which of the two families to fix.\n if (!suppressWarn) warnUnusableDefaultOnce(family, weight, style, fallback);\n } else if (fallback === family && !suppressWarn) {\n // `fallback === family` above exists to stop the request from\n // substituting for itself — recursion into the same miss forever. But\n // when the family that can't serve this variant *is* the effective\n // default, that guard also throws away the one case it was most likely\n // to hit: a default registered at the wrong variant. Nothing renders\n // and, without this branch, nothing is logged either.\n // `suppressWarn` keeps this from firing when a *different* top-level\n // request's substitution probe happens to recurse into the default\n // family and find it can't serve either — that miss is reported by the\n // top-level caller via `warnUnusableDefaultOnce`, not from in here.\n if (registry.has(family)) {\n warnSelfUnusableDefaultOnce(family, weight, style);\n } else {\n // Unregistered, yet still the effective default: the default can only\n // have been named explicitly, because the implicit one is the first\n // *registered* family (and with nothing registered at all, `fallback`\n // is null and never equals `family`). So this is a real\n // misconfiguration — someone pointed setDefaultFontFamily at a family\n // that does not exist — and not the deliberately-silent case of an app\n // that simply hasn't loaded any fonts yet.\n warnUnregisteredDefaultOnce(family, weight, style);\n }\n }\n }\n\n return {\n entry: null,\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'atlas',\n };\n}\n\n/** Insertion order of the registry Map — the first family an app registered. */\nfunction firstRegisteredFamily(): string | null {\n for (const family of registry.keys()) return family;\n return null;\n}\n\n// Resolution runs per frame, so an unguarded warn would flood the console.\n// Keyed per (family, weight, style) variant, not per family: each variant is a\n// distinct thing the app asked for and failed to get. The claimed keys live in\n// fallback.ts so both reset seams clear them.\nfunction warnMissingFamilyOnce(\n family: string, weight: number, style: FontStyle, resolved: string,\n): void {\n if (!claimFallbackWarning(`substituted|${family}|${weight}|${style}`)) return;\n // Two distinct failures land here. Saying \"not registered\" for a family\n // that IS registered — just not in a variant the within-family chain can\n // reach — sends the reader hunting for a registerFont call that already\n // exists. Name the actual gap so the fix is the obvious one.\n const cause = registry.has(family)\n ? `has no variant matching ${weight}/${style}, and none of its registered ` +\n `variants are close enough for the within-family chain to substitute — ` +\n `rendering with \"${resolved}\" instead. Bake that variant with ` +\n `registerFont(\"${family}\", { weight: ${weight}, style: '${style}' }, …)`\n : `is not registered — rendering with \"${resolved}\" instead. ` +\n `Call registerFont(\"${family}\", …)`;\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) ${cause}. ` +\n `Advance widths will differ from the requested font. Use ` +\n `setFontFallbackPolicy('none') to make this a hard miss instead.`,\n );\n}\n\n/**\n * The substitute policy engaged and still came up empty. Distinct from\n * `warnMissingFamilyOnce`, which reports a *successful* swap: here nothing\n * renders, and the fix is almost always in the fallback family rather than\n * the requested one — so name it, and say which way it was chosen.\n */\nfunction warnUnusableDefaultOnce(\n family: string, weight: number, style: FontStyle, fallback: string,\n): void {\n if (!claimFallbackWarning(`unusable-default|${family}|${weight}|${style}`)) return;\n const origin = getDefaultFontFamily() === fallback\n ? 'set via setDefaultFontFamily'\n : 'the first registered family, since setDefaultFontFamily was never called';\n // Same policy-aware question as the substitution guard: a fallback the\n // `'canvas'` policy auto-enrolled has nothing serving it under this policy,\n // so \"has no variant\" would send the reader looking for a variant to bake\n // when the family has no atlas at all.\n const gap = registry.has(fallback) || isCanvasFont(fallback)\n ? `has no variant that can serve ${weight}/${style}`\n : 'is not registered either';\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) is not available, and ` +\n `the fallback family \"${fallback}\" (${origin}) ${gap} — this text will not ` +\n `render at all. Bake that variant with registerFont(\"${fallback}\", ` +\n `{ weight: ${weight}, style: '${style}' }, …), or point ` +\n `setDefaultFontFamily() at a family that covers it.`,\n );\n}\n\n/**\n * The requested family is also the effective default — there is nowhere left\n * to fall back to. Distinct from `warnUnusableDefaultOnce`, which names a\n * *different* fallback family to fix; here the requested and fallback\n * families are the same one, so repeating the name the way that message does\n * would read as nonsense (\"X is not available, and the fallback X … \").\n * Named once, with both facts — registered-but-wrong-variant, and also the\n * fallback — folded into a single sentence.\n */\nfunction warnSelfUnusableDefaultOnce(\n family: string, weight: number, style: FontStyle,\n): void {\n if (!claimFallbackWarning(`self-unusable-default|${family}|${weight}|${style}`)) return;\n const origin = getDefaultFontFamily() === family\n ? 'set via setDefaultFontFamily'\n : 'the first registered family, since setDefaultFontFamily was never called';\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) has no variant that can ` +\n `serve this request, and \"${family}\" is also the fallback family (${origin}) — ` +\n `there is nothing left to fall back to, so this text will not render at all. ` +\n `Bake that variant with registerFont(\"${family}\", { weight: ${weight}, ` +\n `style: '${style}' }, …), or point setDefaultFontFamily() at a different family.`,\n );\n}\n\n/**\n * The requested family is the explicitly set default, and that default names\n * a family that was never registered at all. Distinct from\n * `warnSelfUnusableDefaultOnce`, which reports a default that *is* registered\n * but not in a variant that can serve the request: there the fix is to bake a\n * variant, here the family has no atlas whatsoever, so pointing the reader at\n * a missing variant would send them looking for a `registerFont` call to\n * amend rather than one to write.\n */\nfunction warnUnregisteredDefaultOnce(\n family: string, weight: number, style: FontStyle,\n): void {\n if (!claimFallbackWarning(`unregistered-default|${family}|${weight}|${style}`)) return;\n console.warn(\n `weasel: font family \"${family}\" (${weight}/${style}) was never registered, and ` +\n `it is also the fallback family — setDefaultFontFamily(\"${family}\") names a ` +\n `family with no registered variants at all, so there is nothing left to fall ` +\n `back to and this text will not render at all. Call registerFont(\"${family}\", ` +\n `{ weight: ${weight}, style: '${style}' }, …), or point setDefaultFontFamily() ` +\n `at a family you have registered.`,\n );\n}\n\nfunction weightBucket(w: number): 'regular' | 'bold' {\n return w >= 600 ? 'bold' : 'regular';\n}\n\n/**\n * Resolve a `(family, weight, style)` request to a registered font entry,\n * walking the fallback chain when an exact match isn't available. Returns\n * synthetic flags describing the gap between requested and resolved so the\n * renderer can apply SDF-thicken / vertex-skew fakes.\n */\nexport function resolveFontVariant(\n family: string,\n weight: number,\n style: FontStyle,\n): ResolveResult {\n return resolveFontVariantInternal(family, weight, style, false);\n}\n\n/**\n * Escalate a *single codepoint* to the dynamic tier when the atlas that\n * resolved for the run has no glyph for it.\n *\n * `resolveFontVariant` answers at family granularity: it picks one tier for a\n * whole run. But a baked MSDF atlas covers a fixed charset, so a run served by\n * a perfectly good atlas can still contain a character that atlas never baked\n * — an em dash, a curly quote, anything outside the subset. The dynamic tier\n * rasterizes on demand from installed fonts and can serve exactly those.\n *\n * Returns a canvas-tier `ResolveResult` whose `dynamicFace` the caller drives\n * with `requestGlyph(cp)`, or `null` when escalation isn't available:\n *\n * - Policy `'none'` documents a miss as a *hard* miss. Quietly reaching for\n * another tier per codepoint would undo that, so it doesn't.\n * - No canvas to rasterize into (SSR, a jsdom test without one) makes the\n * dynamic tier constructible-but-broken; `getDynamicFace` throws and this\n * reports the miss instead of taking the caller down with it.\n *\n * Cheap to call per missing codepoint: faces are cached by variant and glyphs\n * by codepoint, so a repeat is two map lookups.\n */\nexport function resolveGlyphFallback(\n family: string,\n weight: number,\n style: FontStyle,\n): ResolveResult | null {\n if (getFontFallbackPolicy() === 'none') return null;\n try {\n return {\n entry: null,\n dynamicFace: getDynamicFace(family, weight, style),\n resolved: { family, weight, style },\n // The dynamic tier rasterizes the real weight and style, so there is\n // nothing for the shader to fake.\n synthetic: { bold: false, italic: false },\n source: 'canvas',\n };\n } catch {\n return null;\n }\n}\n\n/**\n * `suppressWarn` is set only by the recursive substitution probe in\n * `missResolveResult`, which resolves the fallback family purely to check\n * whether it renders. That probe is not itself a request anything asked\n * for, so it must not emit — or claim the warn-once key for — a message\n * about a resolution the caller never made; the caller's own top-level\n * `missResolveResult` call decides, from the probe's result, which single\n * warning (if any) describes the overall miss.\n */\nfunction resolveFontVariantInternal(\n family: string,\n weight: number,\n style: FontStyle,\n suppressWarn: boolean,\n): ResolveResult {\n const familyMap = registry.get(family);\n if (!familyMap || familyMap.size === 0) {\n return missResolveResult(family, weight, style, suppressWarn);\n }\n\n // 1. Exact match\n const exact = familyMap.get(variantKey(weight, style));\n if (exact) {\n return {\n entry: exact,\n resolved: { family, weight, style },\n synthetic: { bold: false, italic: false },\n source: 'atlas',\n };\n }\n\n // 2. Same style, nearest weight in same bucket (ties broken by higher weight)\n const requestedBucket = weightBucket(weight);\n let bestSameStyle: { entry: FontEntry; weight: number; distance: number } | null = null;\n for (const [key, entry] of familyMap) {\n const [wStr, s] = key.split('|') as [string, FontStyle];\n const w = Number(wStr);\n if (s !== style) continue;\n if (weightBucket(w) !== requestedBucket) continue;\n const distance = Math.abs(w - weight);\n if (\n bestSameStyle === null ||\n distance < bestSameStyle.distance ||\n (distance === bestSameStyle.distance && w > bestSameStyle.weight)\n ) {\n bestSameStyle = { entry, weight: w, distance };\n }\n }\n if (bestSameStyle) {\n return {\n entry: bestSameStyle.entry,\n resolved: { family, weight: bestSameStyle.weight, style },\n synthetic: { bold: false, italic: false },\n source: 'atlas',\n };\n }\n\n // 3. (family, 400, style)\n const sameStyleRegular = familyMap.get(variantKey(400, style));\n if (sameStyleRegular) {\n return {\n entry: sameStyleRegular,\n resolved: { family, weight: 400, style },\n synthetic: {\n bold: weight >= 600,\n italic: false,\n },\n source: 'atlas',\n };\n }\n\n // 4. (family, weight, 'normal') — same weight, no italic\n const sameWeightNormal = familyMap.get(variantKey(weight, 'normal'));\n if (sameWeightNormal) {\n return {\n entry: sameWeightNormal,\n resolved: { family, weight, style: 'normal' },\n synthetic: {\n bold: false,\n italic: style === 'italic',\n },\n source: 'atlas',\n };\n }\n\n // 4b. Nearest weight, normal style, same bucket\n let bestNormal: { entry: FontEntry; weight: number; distance: number } | null = null;\n for (const [key, entry] of familyMap) {\n const [wStr, s] = key.split('|') as [string, FontStyle];\n const w = Number(wStr);\n if (s !== 'normal') continue;\n if (weightBucket(w) !== requestedBucket) continue;\n const distance = Math.abs(w - weight);\n if (\n bestNormal === null ||\n distance < bestNormal.distance ||\n (distance === bestNormal.distance && w > bestNormal.weight)\n ) {\n bestNormal = { entry, weight: w, distance };\n }\n }\n if (bestNormal) {\n return {\n entry: bestNormal.entry,\n resolved: { family, weight: bestNormal.weight, style: 'normal' },\n synthetic: {\n bold: false,\n italic: style === 'italic',\n },\n source: 'atlas',\n };\n }\n\n // 5. (family, 400, 'normal') — last resort within family\n const regular = familyMap.get(variantKey(400, 'normal'));\n if (regular) {\n return {\n entry: regular,\n resolved: { family, weight: 400, style: 'normal' },\n synthetic: {\n bold: weight >= 600,\n italic: style === 'italic',\n },\n source: 'atlas',\n };\n }\n\n return missResolveResult(family, weight, style, suppressWarn);\n}\n","/**\n * The parsed-font seam for the outline tier.\n *\n * A face answers exactly one question — \"what does codepoint N look like?\" —\n * and answers it as **SVG path data in em space**: one unit is one em, y\n * grows downward, and the origin sits on the baseline at the glyph's pen\n * position. That is a complete description of the glyph independent of size,\n * zoom, and position, which is the whole reason the tier exists: tessellate\n * once, transform per instance.\n *\n * ### Why `d` and not a command stream\n *\n * `@weasel-js/font` is a Tier A leaf — `@weasel-js/core` depends on it and\n * never the reverse (see `leaf-purity.test.ts`), so this package cannot name\n * core's `PolygonPath`. The alternative to a string would be re-declaring\n * core's `PATH_M`/`PATH_L`/… opcodes over here and trusting two packages to\n * keep the same numbering forever. SVG `d` is the kit's documented language\n * for geometry crossing a boundary (`docs/conventions.md`), core already\n * ships `pathFromD`, and a glyph outline is exactly the case it describes.\n *\n * The string is also a natural cache key and costs nothing to hold: the\n * parse happens once per glyph, and core caches the *tessellation*, not the\n * text.\n */\n\n/** Weight/style pair identifying one face within a family. */\nexport type OutlineFontStyle = 'normal' | 'italic';\n\nexport interface OutlineFace {\n /** Font design units per em. Reported for diagnostics; `glyphD` has already\n * divided by it, so callers never need to. */\n unitsPerEm: number;\n /**\n * Em-space SVG path data for `cp`, or `null` when this face has no glyph\n * for the codepoint *or* the glyph has no contours (a space). Both answers\n * mean the same thing to a caller: there is nothing here to tessellate, so\n * fall through to whatever tier would have drawn it.\n */\n glyphD(cp: number): string | null;\n}\n\n/**\n * Turn font file bytes into a face. Async because the default implementation\n * dynamically imports its parser — see `opentypeParser.ts` for why that\n * matters — and because a caller may want to hand back a face that finishes\n * initializing off-thread.\n */\nexport type OutlineParser = (bytes: ArrayBuffer) => OutlineFace | Promise<OutlineFace>;\n\n/**\n * Decimal places kept when serializing em-space coordinates.\n *\n * Five is chosen against two constraints, not by taste. Below: 1e-5 em is\n * 0.01px on a 1000px-tall glyph, an order of magnitude under anything a\n * display can show. Above: `pathFromD`'s tokenizer splits argument runs on\n * *letters*, so a coordinate that JavaScript chooses to print in exponential\n * form (`1e-7`) would tokenize as the command `e`. Numbers stay in\n * positional notation down to 1e-6, so five decimals cannot produce one.\n */\nexport const OUTLINE_PRECISION = 5;\n","/**\n * TrueType/OpenType **collection** unpacking.\n *\n * `queryLocalFonts()` hands back the font *file* behind a face, and on macOS\n * a large share of the interesting ones — Helvetica, Times, Courier, Menlo —\n * are `.ttc` collections holding every weight and style in a single file with\n * one shared glyph store. A collection is not a font: its header is `ttcf`\n * rather than an sfnt version, so every parser worth using rejects it, and\n * opentype.js is no exception (it recognizes `\\0\\1\\0\\0`, `true`, `typ1`,\n * `OTTO`, `wOFF` and throws on anything else).\n *\n * The fix is mechanical, because a collection is just N table directories\n * over one pool of tables: pick the directory whose PostScript name matches\n * the face we were asked for, then re-emit it as a standalone sfnt with the\n * tables copied and the offsets rewritten. Nothing is re-encoded — the table\n * bytes are the same bytes — so the result parses identically to a font that\n * had shipped on its own.\n *\n * Kept separate from `opentypeParser.ts` so the collection handling is\n * testable without a parser, and so swapping the parser later doesn't drag\n * this along.\n *\n * ### Why this lives here rather than upstream or in a bigger library\n *\n * Decided 2026-07-31, after looking at both alternatives — recorded so this\n * doesn't get \"tidied up\" into one of them later.\n *\n * The upstream fix is genuinely small: `parseOpenTypeTableEntries` hardcodes\n * `p = 12`, so the only thing opentype.js cannot do is be *told* the table\n * directory starts somewhere other than byte zero. Table records already\n * carry absolute offsets and the parser already reads them from a\n * whole-buffer view, so a `dirOffset` parameter and a `ttcf` branch would be\n * about ten lines. (Typr takes an offset in `readFONT` and gets collections\n * for free — same capability, one better-chosen signature.) It would make a\n * good PR. It would also not help for a while: opentype.js sat untouched from\n * 2021 to 2026, and we would carry this file until a release landed anyway.\n *\n * fontkit handles `.ttc` and `.dfont` natively, and WOFF2 besides — but it is\n * ~5.6 MB unpacked across nine dependencies (brotli, unicode-trie, dfa, …).\n * That is a shaping engine, and all this tier wants is a glyph outline.\n *\n * This version has an advantage over both: it runs *before* anything parses,\n * so it is parser-agnostic and survives swapping opentype.js out.\n *\n * **Known gap: `.dfont`** (Datafork TrueType) is not unpacked — its sfnt\n * tables live inside a Macintosh resource map, which is a second container\n * format on top of the one above. `isDataForkFont` recognizes it and\n * `sfntFromCollection` throws by name, so the face degrades to the SDF tier\n * saying why; the registry's `warnOnce` carries the message. Not currently\n * reachable on macOS, whose system font directories are 204 `.ttf` / 128\n * `.ttc` / 38 `.otf` and no `.dfont`; it is a real format on older systems.\n */\n\nconst SFNT_HEADER_BYTES = 12;\nconst TABLE_RECORD_BYTES = 16;\n\nfunction tagAt(view: DataView, offset: number): string {\n return String.fromCharCode(\n view.getUint8(offset), view.getUint8(offset + 1),\n view.getUint8(offset + 2), view.getUint8(offset + 3),\n );\n}\n\n/** Is `bytes` a font collection rather than a single font? */\nexport function isFontCollection(bytes: ArrayBuffer): boolean {\n if (bytes.byteLength < 4) return false;\n return tagAt(new DataView(bytes), 0) === 'ttcf';\n}\n\n/** Every signature that opens a font file this tier can hand to a parser. */\nconst FONT_SIGNATURES: ReadonlySet<string> = new Set([\n '\\x00\\x01\\x00\\x00', 'true', 'typ1', 'OTTO', 'ttcf', 'wOFF', 'wOF2',\n]);\n\nconst RESOURCE_HEADER_BYTES = 16;\n\n/**\n * Is `bytes` a Datafork TrueType file (`.dfont`)?\n *\n * A `.dfont` is a bare Macintosh resource fork written to the data fork, so\n * unlike every other font format it opens with no signature — just the\n * resource header's four big-endian offsets. It is recognized by those\n * adding up, which is why the signature check has to run first: an sfnt's\n * header bytes are numbers too, and nothing stops them from coincidentally\n * being consistent.\n *\n * Detection only. Reading the `sfnt` resources out of the map is the rest of\n * the job and is not implemented — the point is that the face declines the\n * outline tier by name instead of dying on an unrecognized-signature message\n * that never mentions the format.\n */\nexport function isDataForkFont(bytes: ArrayBuffer): boolean {\n if (bytes.byteLength < RESOURCE_HEADER_BYTES) return false;\n const view = new DataView(bytes);\n if (FONT_SIGNATURES.has(tagAt(view, 0))) return false;\n const dataOffset = view.getUint32(0);\n const mapOffset = view.getUint32(4);\n const dataLength = view.getUint32(8);\n const mapLength = view.getUint32(12);\n return dataOffset >= RESOURCE_HEADER_BYTES\n && dataOffset + dataLength === mapOffset\n && mapOffset + mapLength <= bytes.byteLength;\n}\n\ninterface TableRecord { tag: string; offset: number; length: number; checksum: number }\n\nfunction readTableDirectory(view: DataView, dirOffset: number): TableRecord[] {\n const numTables = view.getUint16(dirOffset + 4);\n const records: TableRecord[] = [];\n for (let i = 0; i < numTables; i++) {\n const at = dirOffset + SFNT_HEADER_BYTES + i * TABLE_RECORD_BYTES;\n records.push({\n tag: tagAt(view, at),\n checksum: view.getUint32(at + 4),\n offset: view.getUint32(at + 8),\n length: view.getUint32(at + 12),\n });\n }\n return records;\n}\n\n/**\n * PostScript name (`name` table, nameID 6) of the sub-font at `dirOffset`,\n * or `null` when it carries none.\n *\n * A deliberately minimal reader: identifying which member of a collection to\n * unpack is the only thing needed here, and a full `name` parser would be a\n * second implementation of something the real parser does properly once the\n * font is standalone. PostScript names are ASCII by specification, so both\n * the Macintosh (single-byte) and Windows (UTF-16BE) encodings reduce to\n * \"take every byte that isn't a zero pad\".\n */\nfunction postScriptNameAt(view: DataView, records: readonly TableRecord[]): string | null {\n const name = records.find((r) => r.tag === 'name');\n if (!name) return null;\n const base = name.offset;\n const count = view.getUint16(base + 2);\n const stringOffset = view.getUint16(base + 4);\n for (let i = 0; i < count; i++) {\n const rec = base + 6 + i * 12;\n if (view.getUint16(rec + 6) !== 6) continue; // nameID 6 === PostScript name\n const length = view.getUint16(rec + 8);\n const offset = view.getUint16(rec + 10);\n let out = '';\n for (let b = 0; b < length; b++) {\n const code = view.getUint8(base + stringOffset + offset + b);\n if (code !== 0) out += String.fromCharCode(code);\n }\n if (out.length > 0) return out;\n }\n return null;\n}\n\n/** Re-emit the table directory at `dirOffset` as a standalone sfnt buffer. */\nfunction extractFont(source: ArrayBuffer, view: DataView, dirOffset: number): ArrayBuffer {\n const records = readTableDirectory(view, dirOffset);\n // Tables are copied 4-byte aligned, which is what the format requires and\n // what every checksum in the directory was computed over.\n const padded = (n: number): number => (n + 3) & ~3;\n let total = SFNT_HEADER_BYTES + records.length * TABLE_RECORD_BYTES;\n for (const r of records) total += padded(r.length);\n\n const out = new ArrayBuffer(total);\n const dst = new DataView(out);\n const dstBytes = new Uint8Array(out);\n const srcBytes = new Uint8Array(source);\n\n dst.setUint32(0, view.getUint32(dirOffset)); // sfntVersion\n dst.setUint16(4, records.length);\n // searchRange / entrySelector / rangeShift are a binary-search hint over the\n // table records. Recomputed rather than copied: the member directory's\n // values describe its own table count, which is usually but not always the\n // same, and a wrong hint is a parser walking off the end of the directory.\n const entrySelector = Math.floor(Math.log2(records.length));\n const searchRange = 2 ** entrySelector * 16;\n dst.setUint16(6, searchRange);\n dst.setUint16(8, entrySelector);\n dst.setUint16(10, records.length * 16 - searchRange);\n\n let cursor = SFNT_HEADER_BYTES + records.length * TABLE_RECORD_BYTES;\n records.forEach((r, i) => {\n const at = SFNT_HEADER_BYTES + i * TABLE_RECORD_BYTES;\n for (let b = 0; b < 4; b++) dst.setUint8(at + b, r.tag.charCodeAt(b));\n dst.setUint32(at + 4, r.checksum);\n dst.setUint32(at + 8, cursor);\n dst.setUint32(at + 12, r.length);\n dstBytes.set(srcBytes.subarray(r.offset, r.offset + r.length), cursor);\n cursor += padded(r.length);\n });\n return out;\n}\n\n/**\n * Standalone font bytes for one member of a collection.\n *\n * `postScriptName` selects the member; when it is absent or matches nothing,\n * the first member is returned. Falling back rather than failing is\n * deliberate — the name comes from `FontData.postscriptName`, and a browser\n * that reports it differently from the `name` table should degrade to \"some\n * weight of the right family\", not to no text at all. The caller sees which\n * it got via the returned `matched` flag.\n *\n * Passing a single font through is a no-op, so this is safe to call on any\n * bytes.\n */\nexport function sfntFromCollection(\n bytes: ArrayBuffer,\n postScriptName?: string,\n): { bytes: ArrayBuffer; matched: boolean } {\n if (isDataForkFont(bytes)) {\n throw new Error(\n 'Datafork TrueType (.dfont) is not supported — the outline tier reads sfnt '\n + 'tables and a .dfont holds them inside a Macintosh resource map.',\n );\n }\n if (!isFontCollection(bytes)) return { bytes, matched: true };\n\n const view = new DataView(bytes);\n const numFonts = view.getUint32(8);\n if (numFonts === 0) throw new Error('font collection contains no fonts');\n\n const offsets: number[] = [];\n for (let i = 0; i < numFonts; i++) offsets.push(view.getUint32(12 + i * 4));\n\n if (postScriptName) {\n for (const dirOffset of offsets) {\n const records = readTableDirectory(view, dirOffset);\n if (postScriptNameAt(view, records) === postScriptName) {\n return { bytes: extractFont(bytes, view, dirOffset), matched: true };\n }\n }\n }\n return { bytes: extractFont(bytes, view, offsets[0]), matched: !postScriptName };\n}\n","/**\n * The default `OutlineParser`: opentype.js, loaded on demand.\n *\n * ### Why the dynamic import\n *\n * opentype.js is ~49 kB gzipped and it is the *only* runtime dependency this\n * package has. Charging that to every consumer of `@weasel-js/font` — most of\n * whom render text through a baked atlas and never touch an outline — would\n * be a poor trade for a tier that only engages above a size threshold. The\n * tier is inherently asynchronous anyway (font bytes arrive over `fetch` or\n * `queryLocalFonts`), so there is no synchronous path to slow down: the\n * import rides along with the load that was already going to happen, and a\n * bundler splits it into a chunk nobody fetches until large text appears.\n *\n * The other candidate was Typr — smaller, but unmaintained and untyped, and\n * this is a runtime dependency of a published package.\n *\n * ### Coordinate convention\n *\n * `glyph.getPath(0, 0, 1)` already emits exactly what `OutlineFace` promises:\n * font units divided by the em, y flipped to grow downward, and the origin on\n * the baseline at the pen position. That is opentype.js's canvas-rendering\n * convention, and it happens to be the kit's world convention too, so no\n * transform is applied here — a flip would have to be undone downstream.\n */\n\nimport { OUTLINE_PRECISION, type OutlineFace, type OutlineParser } from './OutlineFace';\nimport { sfntFromCollection } from './sfnt';\n\ntype OpenType = typeof import('opentype.js');\n\nlet modulePromise: Promise<OpenType> | null = null;\n\n/** Load (once) and cache the parser module. */\nfunction loadOpenType(): Promise<OpenType> {\n modulePromise ??= import('opentype.js');\n return modulePromise;\n}\n\n/**\n * Wrap a parsed opentype.js font.\n *\n * `charToGlyphIndex` reports 0 — `.notdef` — for a codepoint the font does\n * not cover. That is a real glyph (the tofu box) and it renders, so returning\n * its outline would silently substitute a box for a character the atlas tier\n * might have been able to serve. Report the miss instead and let the caller's\n * fallback ladder run.\n */\nfunction faceFor(font: import('opentype.js').Font): OutlineFace {\n return {\n unitsPerEm: font.unitsPerEm,\n glyphD(cp: number): string | null {\n const index = font.charToGlyphIndex(String.fromCodePoint(cp));\n if (!index) return null;\n const d = font.glyphs.get(index).getPath(0, 0, 1).toPathData(OUTLINE_PRECISION);\n // A glyph with no contours (a space) serializes to the empty string.\n // Same answer as a missing glyph from the caller's point of view:\n // nothing to tessellate.\n return d.length > 0 ? d : null;\n },\n };\n}\n\n/**\n * Parse font file bytes into a face. Accepts single fonts and collections\n * (`.ttc`); `postScriptName` picks the member of a collection, and is\n * ignored for a single font.\n */\nexport function createOpenTypeParser(postScriptName?: string): OutlineParser {\n return async (bytes: ArrayBuffer): Promise<OutlineFace> => {\n const opentype = await loadOpenType();\n const { bytes: single } = sfntFromCollection(bytes, postScriptName);\n return faceFor(opentype.parse(single));\n };\n}\n\n/** The parser used when a registration supplies none of its own. */\nexport const openTypeParser: OutlineParser = createOpenTypeParser();\n\n/** @internal Test seam — drop the memoized module so a test can re-import. */\nexport function _resetOpenTypeModuleForTests(): void {\n modulePromise = null;\n}\n","/**\n * The outline tier's registry: which faces have real font bytes behind them,\n * and what a given glyph looks like.\n *\n * ### What this tier is for\n *\n * A distance field is a *sampled* representation. The baked MSDF atlas is\n * generated from outlines and holds up well; the dynamic canvas-SDF tier\n * (`../dynamic/`) reconstructs its field from a 48px raster, so magnifying it\n * past a few times the bake size exposes the raster as contour wobble — bumps\n * one bake-texel apart, ±2–3px at 8×. No bake size fixes that, it only moves\n * the size at which it appears (`glyphRasterizer.ts` measures the tradeoff:\n * error is *minimized at* the bake size, so raising it spoils the 12–32px\n * range where most text lives).\n *\n * Above a size threshold the answer is to stop sampling: parse the font, take\n * the glyph's outline, and hand it to the path renderer the kit already has.\n * Exact at every zoom, and — because a glyph becomes an ordinary path — it\n * takes strokes and non-solid fills for free.\n *\n * Below the threshold the atlas still wins, and not narrowly: outlines carry\n * no hinting and no stem darkening, so body text at 12–16px rendered from\n * them looks *worse* than a platform rasterizer that puts stems on the pixel\n * grid. Two tiers with a size threshold is the standard hybrid, not a\n * compromise.\n *\n * ### Metric neutrality (the load-bearing invariant)\n *\n * This tier replaces how a glyph is *painted*, never where it sits. Advances,\n * kerning, line breaking and baselines all keep coming from whichever tier\n * resolved the run — the baked atlas or the canvas face. That is what lets\n * the threshold be a rendering decision: zooming past it swaps glyph geometry\n * with the layout untouched, so text cannot reflow under the user's cursor,\n * and `measureTextBounds` / `textLineBoxes` need to know nothing about\n * outlines at all.\n *\n * The cost is that a face whose real advances disagree with the atlas's\n * inherits the atlas's. For the bundled Inter that is exact (the subset ships\n * from the same source the atlas was baked from), and for a machine font the\n * outline bytes and the canvas metrics come from the same file.\n *\n * ### Availability\n *\n * Everything here degrades rather than blocks. Bytes arrive over `fetch` or\n * `queryLocalFonts` — asynchronous, and in the local case gated behind a\n * permission the user can refuse — so `glyphOutline` answers `null` until a\n * face is loaded and forever if it failed, and the caller falls back down the\n * ladder to SDF. A family must never render *nothing* because the outline\n * tier could not get bytes.\n */\n\nimport { notifyGlyphReady } from '../glyphReady';\nimport type { OutlineFace, OutlineFontStyle, OutlineParser } from './OutlineFace';\nimport { openTypeParser } from './opentypeParser';\n\n/**\n * Where a face's bytes come from. A URL is fetched; a buffer or blob is used\n * as-is; a thunk is called at first use, which is what lets `queryLocalFonts`\n * results be registered eagerly and read lazily — the permission prompt and\n * the (potentially many megabytes of) blob only happen for a family the\n * document actually sets text in.\n */\nexport type OutlineSource =\n | string\n | ArrayBuffer\n | Blob\n | (() => ArrayBuffer | Blob | Promise<ArrayBuffer | Blob>);\n\nexport interface OutlineVariant {\n weight?: number;\n style?: OutlineFontStyle;\n}\n\nexport interface OutlineFontOptions {\n /** Override the default opentype.js parser. Mostly a test seam; also the\n * hook for a consumer who already has a font parser in their bundle. */\n parser?: OutlineParser;\n}\n\n/** Load state of one registered face. */\nexport type OutlineStatus = 'idle' | 'loading' | 'ready' | 'failed';\n\ninterface FaceSlot {\n family: string;\n weight: number;\n style: OutlineFontStyle;\n source: OutlineSource;\n parser: OutlineParser;\n status: OutlineStatus;\n face: OutlineFace | null;\n /** Memoized `glyphD` answers, including the `null` misses — a codepoint the\n * face lacks is asked for on every frame that draws it, and re-deriving\n * the miss means re-running the cmap lookup forever. */\n glyphs: Map<number, string | null>;\n}\n\nlet slots = new Map<string, FaceSlot>();\n\nfunction slotKey(family: string, weight: number, style: OutlineFontStyle): string {\n return `${family}|${weight}|${style}`;\n}\n\nfunction normalize(v: OutlineVariant): { weight: number; style: OutlineFontStyle } {\n return { weight: v.weight ?? 400, style: v.style ?? 'normal' };\n}\n\n/**\n * Register font file bytes for one face, so text set in it can render from\n * outlines above the size threshold.\n *\n * Registration is cheap and synchronous: nothing is fetched, and the parser\n * is not even imported, until a frame actually asks for a glyph from this\n * face. Registering a face that is never drawn large costs one map entry.\n *\n * The variant must match exactly what the layout tier resolved — this is\n * deliberately *not* a fallback chain. Painting 400-weight outlines at\n * 700-weight advances, or upright outlines where the shader was going to\n * fake an oblique, looks worse than the SDF it replaced; a miss here falls\n * back down the ladder instead, which is always safe.\n */\nexport function registerFontOutlines(\n family: string,\n variant: OutlineVariant,\n source: OutlineSource,\n opts: OutlineFontOptions = {},\n): void {\n const { weight, style } = normalize(variant);\n slots.set(slotKey(family, weight, style), {\n family, weight, style, source,\n parser: opts.parser ?? openTypeParser,\n status: 'idle',\n face: null,\n glyphs: new Map(),\n });\n}\n\n/** Drop a registration. Glyphs already handed out stay valid — they are\n * plain path data — but nothing further resolves from this face. */\nexport function unregisterFontOutlines(family: string, variant: OutlineVariant = {}): void {\n const { weight, style } = normalize(variant);\n slots.delete(slotKey(family, weight, style));\n}\n\n/**\n * Is an outline face registered for this exact variant? Answers `true` while\n * the bytes are still loading — the question is \"could this face serve\n * outlines\", which is what a caller deciding whether to *ask* wants; use\n * `outlineStatus` for the narrower \"can it right now\".\n */\nexport function hasFontOutlines(\n family: string, weight = 400, style: OutlineFontStyle = 'normal',\n): boolean {\n return slots.has(slotKey(family, weight, style));\n}\n\n/** Load state of a registered face; `null` when nothing is registered. */\nexport function outlineStatus(\n family: string, weight = 400, style: OutlineFontStyle = 'normal',\n): OutlineStatus | null {\n return slots.get(slotKey(family, weight, style))?.status ?? null;\n}\n\n/** Every registered outline face and its load state — the enumeration a\n * debug overlay or font picker needs, mirroring `listFonts`. */\nexport function listFontOutlines(): readonly {\n family: string; weight: number; style: OutlineFontStyle; status: OutlineStatus;\n}[] {\n return [...slots.values()]\n .map(({ family, weight, style, status }) => ({ family, weight, style, status }))\n .sort((a, b) => a.family.localeCompare(b.family) || a.weight - b.weight\n || a.style.localeCompare(b.style));\n}\n\n/**\n * Em-space SVG path data for one glyph, or `null` when this tier cannot serve\n * it — no registration, bytes still loading, load failed, no such glyph, or a\n * glyph with no contours.\n *\n * Synchronous by construction: it is called from the per-frame glyph walk, so\n * it can only ever report what is already in hand. The first call for an\n * unloaded face starts the load and returns `null`; when the load lands,\n * `notifyGlyphReady` asks the canvas to draw again and the second frame gets\n * the outline. Same shape as the dynamic tier's deferred bakes.\n */\n/**\n * Close every contour of a glyph's path data.\n *\n * A glyph contour is closed by definition — neither TrueType nor CFF has any\n * other kind — but not every serializer writes the `Z` that says so, and the\n * omission is invisible until someone *strokes* the outline: a fill closes the\n * contour implicitly (earcut joins last point to first), while a stroke\n * follows exactly the path it is handed and leaves the closing edge unpainted,\n * with a cap at each loose end. That showed up as glyphs missing part of their\n * outline along long straight edges, which is where a contour's seam usually\n * falls.\n *\n * Normalized here rather than in one parser because the same hole is open to\n * every consumer-supplied `OutlineParser`, and a contract that says \"closed\"\n * is cheaper to guarantee than to document.\n */\nexport function closeContours(d: string): string {\n let out = '';\n let start = 0;\n for (let i = 1; i < d.length; i++) {\n const c = d[i];\n if (c !== 'M' && c !== 'm') continue;\n out += closeOne(d.slice(start, i));\n start = i;\n }\n return out + closeOne(d.slice(start));\n}\n\nfunction closeOne(contour: string): string {\n const trimmed = contour.trimEnd();\n if (trimmed.length === 0) return '';\n const last = trimmed[trimmed.length - 1];\n return last === 'Z' || last === 'z' ? trimmed : `${trimmed}Z`;\n}\n\nexport function glyphOutline(\n family: string,\n weight: number,\n style: OutlineFontStyle,\n cp: number,\n): string | null {\n const slot = slots.get(slotKey(family, weight, style));\n if (!slot) return null;\n if (slot.status === 'idle') {\n void beginLoad(slot);\n return null;\n }\n if (slot.status !== 'ready') return null;\n\n const cached = slot.glyphs.get(cp);\n if (cached !== undefined) return cached;\n let d: string | null = null;\n try {\n const raw = slot.face!.glyphD(cp);\n d = raw === null ? null : closeContours(raw);\n } catch (err) {\n // A parser that throws on one glyph should cost that glyph, not the face.\n warnOnce(`glyph|${slotKey(family, weight, style)}`,\n `weasel: outline face \"${family}\" (${weight}/${style}) could not produce a glyph ` +\n `for U+${cp.toString(16).toUpperCase().padStart(4, '0')} — ` +\n `${err instanceof Error ? err.message : String(err)}. Falling back to the SDF tier.`);\n }\n slot.glyphs.set(cp, d);\n return d;\n}\n\nasync function beginLoad(slot: FaceSlot): Promise<void> {\n slot.status = 'loading';\n try {\n slot.face = await slot.parser(await readSource(slot.source));\n slot.status = 'ready';\n // The frame that asked for these glyphs has long since drawn. Without\n // this the outlines sit in memory until something else invalidates the\n // canvas, which on a static document is \"never\".\n notifyGlyphReady();\n } catch (err) {\n slot.status = 'failed';\n warnOnce(`load|${slotKey(slot.family, slot.weight, slot.style)}`,\n `weasel registerFontOutlines(\"${slot.family}\" ${slot.weight}/${slot.style}): ` +\n `${err instanceof Error ? err.message : String(err)}. Large text in this face ` +\n `keeps rendering from the SDF tier.`);\n }\n}\n\nasync function readSource(source: OutlineSource): Promise<ArrayBuffer> {\n const resolved = typeof source === 'function' ? await source() : source;\n if (typeof resolved === 'string') {\n const res = await fetch(resolved);\n if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${resolved}`);\n return res.arrayBuffer();\n }\n if (resolved instanceof ArrayBuffer) return resolved;\n return resolved.arrayBuffer();\n}\n\n// Loading and glyph lookup both run off the render loop, so an unguarded warn\n// would repeat every frame.\nconst warned = new Set<string>();\n\nfunction warnOnce(key: string, message: string): void {\n if (warned.has(key)) return;\n warned.add(key);\n console.warn(message);\n}\n\n/** @internal Test seam — registry and warn-once keys are module state. */\nexport function _resetFontOutlinesForTests(): void {\n slots = new Map();\n warned.clear();\n}\n","/**\n * Local Font Access → outline registrations.\n *\n * The dynamic canvas-SDF tier can render any installed family without ever\n * seeing its bytes: it asks canvas 2D to `fillText` and takes the pixels. The\n * outline tier cannot — it needs the actual font file — and the only web API\n * that hands one over is `window.queryLocalFonts()`, which is Chromium-only\n * and gated behind a permission prompt that requires a user gesture.\n *\n * So this is the one part of the tier a consumer has to *ask* for, from a\n * click. Everything downstream treats a refusal as ordinary: no\n * registrations, `glyphOutline` keeps answering `null`, and large text keeps\n * rendering from the SDF tier exactly as it does today. That ladder is\n * load-bearing rather than defensive — Safari and Firefox have no equivalent\n * API at all, so \"denied\" is the permanent state for most of the web.\n *\n * Registration is eager but reading is lazy: every matching face gets a thunk\n * that calls `FontData.blob()` at first glyph request. A machine can easily\n * have hundreds of faces and a `.ttc` runs to tens of megabytes, so\n * downloading them all at enable time would be worse than not offering the\n * tier.\n */\n\nimport type { OutlineFontStyle } from './OutlineFace';\nimport { createOpenTypeParser } from './opentypeParser';\nimport { registerFontOutlines } from './outlineRegistry';\n\n/**\n * The slice of the Local Font Access API this module uses. Declared here\n * rather than pulled from a lib: `queryLocalFonts` is not in TypeScript's DOM\n * types, and the alternative — casting `window` to `any` at the call site —\n * would lose the shape of what comes back.\n */\ninterface LocalFontData {\n postscriptName: string;\n fullName: string;\n family: string;\n style: string;\n blob(): Promise<Blob>;\n}\n\ntype LocalFontWindow = typeof globalThis & {\n queryLocalFonts?: (opts?: { postscriptNames?: string[] }) => Promise<LocalFontData[]>;\n};\n\n/** Is the Local Font Access API present at all? False on every non-Chromium\n * browser and in any non-secure context. */\nexport function canQueryLocalFonts(): boolean {\n return typeof globalThis !== 'undefined'\n && typeof (globalThis as LocalFontWindow).queryLocalFonts === 'function';\n}\n\n/**\n * CSS weights for the weight words that appear in a `style` string.\n *\n * Ordered longest-first where one name contains another (\"ExtraBold\" before\n * \"Bold\", \"DemiBold\" before \"Bold\"), because the match is a substring scan\n * and \"Bold\" would otherwise claim every heavier face for 700.\n */\nconst WEIGHT_WORDS: readonly (readonly [string, number])[] = [\n ['extrablack', 950], ['ultrablack', 950],\n ['extrabold', 800], ['ultrabold', 800],\n ['extralight', 200], ['ultralight', 200],\n ['semibold', 600], ['demibold', 600],\n ['semilight', 350],\n ['black', 900], ['heavy', 900],\n ['bold', 700],\n ['medium', 500],\n ['light', 300],\n ['thin', 100], ['hairline', 100],\n ['book', 400], ['regular', 400], ['normal', 400],\n];\n\n/**\n * Parse a `FontData.style` string — \"Regular\", \"Bold Italic\", \"Condensed\n * ExtraLight Oblique\" — into the CSS variant the kit's registry is keyed by.\n *\n * Anything unrecognized lands on 400/normal, which is what the string\n * \"Regular\" would have produced anyway. A wrong guess here costs a face that\n * registers under a variant nothing asks for; it cannot mis-paint anything,\n * because resolution is an exact-match lookup that simply misses.\n */\nexport function parseFontStyle(style: string): { weight: number; style: OutlineFontStyle } {\n const s = style.toLowerCase();\n const italic = s.includes('italic') || s.includes('oblique');\n const weight = WEIGHT_WORDS.find(([word]) => s.includes(word))?.[1] ?? 400;\n return { weight, style: italic ? 'italic' : 'normal' };\n}\n\nexport interface LocalFontOutlinesOptions {\n /**\n * Restrict registration to these families. Defaults to every installed\n * family. Worth passing when the consumer already knows its font menu:\n * a machine with 900 faces otherwise gets 900 registry entries, all inert\n * but all held.\n */\n families?: readonly string[];\n}\n\nexport interface LocalFontOutlinesResult {\n /** Families that now have at least one outline face, in menu order. */\n families: readonly string[];\n /** Total faces registered across those families. */\n faces: number;\n}\n\n/**\n * Ask for permission to read installed fonts, and register outline sources\n * for the faces it returns. **Must be called from a user gesture** — the\n * permission prompt requires one, and without it the call rejects.\n *\n * Rejects when the API is missing or the user denies; both are ordinary\n * outcomes and neither leaves the registry in a partial state, so a caller\n * that simply ignores the rejection still has a working SDF tier.\n */\nexport async function enableLocalFontOutlines(\n opts: LocalFontOutlinesOptions = {},\n): Promise<LocalFontOutlinesResult> {\n const query = (globalThis as LocalFontWindow).queryLocalFonts;\n if (typeof query !== 'function') {\n throw new Error(\n 'weasel enableLocalFontOutlines: this browser has no Local Font Access API ' +\n '(queryLocalFonts). Large text keeps rendering from the SDF tier.',\n );\n }\n\n const wanted = opts.families ? new Set(opts.families) : null;\n const data = await query.call(globalThis);\n\n const families = new Set<string>();\n let faces = 0;\n for (const font of data) {\n if (wanted && !wanted.has(font.family)) continue;\n const variant = parseFontStyle(font.style);\n registerFontOutlines(font.family, variant, () => font.blob(), {\n // The PostScript name is how a member is picked out of a `.ttc`\n // collection, which is what most macOS system families ship as.\n parser: createOpenTypeParser(font.postscriptName),\n });\n families.add(font.family);\n faces++;\n }\n return { families: [...families].sort((a, b) => a.localeCompare(b)), faces };\n}\n","/**\n * GLSL ES 3.0 sources for the built-in MSDF text shader.\n *\n * Vertex inputs (interleaved, stride 20 bytes = 5 × float):\n * a_position vec2 screen-space x,y of the glyph quad vertex\n * a_uv vec2 atlas UV (0..1)\n * a_baselineY float line baseline Y in screen space (for synth-italic skew)\n *\n * Uniforms:\n * u_proj mat3 screen → clip projection\n * u_model mat3 cumulative group transform\n * u_atlas sampler2D the MSDF atlas texture (bound to TEXTURE0)\n * u_color vec4 text color (straight RGBA)\n * u_alpha float group alpha multiplier\n * u_colorMatrix mat4 color transform applied to u_color before alpha modulation\n * u_colorBias vec4 bias added after the matrix (identity = zero bias)\n *\n * Output: PREMULTIPLIED alpha — `vec4(color.rgb * a, a)` per conventions §2.\n * Blend func: ONE / ONE_MINUS_SRC_ALPHA.\n *\n * MSDF channel layout: msdf-bmfont-xml outputs R,G,B channels as independent\n * signed-distance fields covering different edge directions. The true SDF\n * value is the median of R,G,B; this recovers sharp outlines while averaging\n * out single-channel aliasing artifacts.\n *\n * Antialiasing (`aaWidth`, both shaders): the smoothstep band must be one\n * *screen* pixel wide, so it is derived per-fragment from `fwidth(sdfVal)` —\n * the rate the field changes between adjacent fragments. That single quantity\n * already folds in font size, zoom, and DPR: minify the glyph and the field\n * changes faster, so the band widens in field units to stay one pixel on\n * screen; magnify it and the band narrows.\n *\n * A *constant* band cannot be correct at more than one scale, and this shader\n * used one (0.05) until 2026-07-29. At 16px text the band collapsed to well\n * under a pixel and glyph edges quantized to hard stair-steps; at display\n * sizes the same constant read mushy. `fwidth` is core in GLSL ES 3.00, so\n * no extension guard is needed. The `max()` floor keeps a degenerate\n * derivative (flat field, or a driver returning 0) from producing a\n * zero-width band, which would be the aliased behavior all over again.\n */\n\nexport const TEXT_VERT_SRC = /* glsl */ `#version 300 es\nin vec2 a_position;\nin vec2 a_uv;\nin float a_baselineY;\nuniform mat3 u_proj;\nuniform mat3 u_model;\nuniform float u_synthItalic;\nout vec2 v_uv;\nvoid main() {\n // Synthetic italic: shift x by (a_baselineY - a_position.y) * tan(angle).\n // Above-baseline vertices (lower y in screen coords) lean further right.\n vec2 skewed = vec2(\n a_position.x + (a_baselineY - a_position.y) * tan(u_synthItalic),\n a_position.y\n );\n vec3 screen = u_model * vec3(skewed, 1.0);\n vec3 clip = u_proj * vec3(screen.xy, 1.0);\n gl_Position = vec4(clip.xy, 0.0, 1.0);\n v_uv = a_uv;\n}\n`;\n\nexport const TEXT_FRAG_SRC = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 v_uv;\nuniform sampler2D u_atlas;\nuniform vec4 u_color;\nuniform float u_alpha;\nuniform float u_synthBold;\nuniform mat4 u_colorMatrix;\nuniform vec4 u_colorBias;\nout vec4 outColor;\n\nfloat median(float r, float g, float b) {\n return max(min(r, g), min(max(r, g), b));\n}\n\nvoid main() {\n vec3 sdf = texture(u_atlas, v_uv).rgb;\n float sdfVal = median(sdf.r, sdf.g, sdf.b);\n // Screen-space AA band — see the file header. Half of fwidth spans ~1px.\n float aaW = max(0.5 * fwidth(sdfVal), 0.0005);\n // u_synthBold shifts the SDF threshold to thicken strokes when the\n // resolver fell back from a missing bold variant to the regular atlas.\n float threshold = 0.5 - u_synthBold;\n float msdfAlpha = smoothstep(threshold - aaW, threshold + aaW, sdfVal);\n vec4 src = vec4(u_color.rgb, u_color.a);\n vec4 mapped = clamp(u_colorMatrix * src + u_colorBias, 0.0, 1.0);\n float a = mapped.a * msdfAlpha * u_alpha;\n outColor = vec4(mapped.rgb * a, a);\n}\n`;\n\n/**\n * Single-channel sibling of TEXT_FRAG_SRC for runtime canvas-SDF glyphs\n * (DynamicGlyphAtlas R8 pages): the R channel IS the distance field, so no\n * median. Threshold semantics (0.5 edge, u_synthBold shift) match the MSDF\n * shader because the bake encodes the edge at ~128.\n *\n * Accepted trade: corner rounding away from the bake size, mildest near it.\n * `glyphRasterizer.ts` carries the measurements and the reason neither a\n * larger bake nor extra taps would improve the small-text end.\n */\nexport const TEXT_FRAG_R8_SRC = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 v_uv;\nuniform sampler2D u_atlas;\nuniform vec4 u_color;\nuniform float u_alpha;\nuniform float u_synthBold;\nuniform mat4 u_colorMatrix;\nuniform vec4 u_colorBias;\nout vec4 outColor;\n\nvoid main() {\n float sdfVal = texture(u_atlas, v_uv).r;\n // Screen-space AA band — see the file header. Half of fwidth spans ~1px.\n float aaW = max(0.5 * fwidth(sdfVal), 0.0005);\n float threshold = 0.5 - u_synthBold;\n float sdfAlpha = smoothstep(threshold - aaW, threshold + aaW, sdfVal);\n vec4 src = vec4(u_color.rgb, u_color.a);\n vec4 mapped = clamp(u_colorMatrix * src + u_colorBias, 0.0, 1.0);\n float a = mapped.a * sdfAlpha * u_alpha;\n outColor = vec4(mapped.rgb * a, a);\n}\n`;\n\nexport const TEXT_SDF_UNIFORMS = [\n 'u_proj', 'u_model', 'u_atlas', 'u_color', 'u_alpha',\n 'u_synthBold', 'u_synthItalic', 'u_colorMatrix', 'u_colorBias',\n] as const;\n\nexport const TEXT_SDF_ATTRIBUTES = ['a_position', 'a_uv', 'a_baselineY'] as const;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/font",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "MSDF font atlases, glyph metrics, and runtime glyph rasterization for @weasel-js/core. Registry, kerning-aware glyph layout, and the SDF text shader source.",
5
5
  "license": "MIT",
6
6
  "type": "module",