@weasel-js/font 0.7.0
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/LICENSE +21 -0
- package/README.md +38 -0
- package/dist/index.d.ts +395 -0
- package/dist/index.js +825 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/FontAtlas.ts","../src/fallback.ts","../src/dynamic/shelfPack.ts","../src/dynamic/distanceTransform.ts","../src/dynamic/glyphRasterizer.ts","../src/dynamic/dynamicAtlas.ts","../src/registerFont.ts","../src/textSdf.ts"],"names":["policy"],"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 * 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 { 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\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;\nconst subscribers = new Set<() => void>();\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 * @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 for (const cb of subscribers) cb();\n}\n\n/** Subscribe to deferred-bake completion (fires after each flush batch).\n * `<SceneCanvas>` subscribes and requests a redraw, mirroring\n * `subscribeImageReady`. Returns an unsubscribe. */\nexport function subscribeGlyphReady(cb: () => void): () => void {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\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 subscribers.clear();\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 { 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 } 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 * 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
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weasel-js/font",
|
|
3
|
+
"version": "0.7.0",
|
|
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
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"author": "orochi235",
|
|
19
|
+
"homepage": "https://orochi235.github.io/weasel/",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/orochi235/weasel.git",
|
|
23
|
+
"directory": "packages/font"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/orochi235/weasel/issues"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"build": "tsup"
|
|
42
|
+
}
|
|
43
|
+
}
|