@weasel-js/text 1.2.0 → 1.3.0-pre.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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/layout/layoutRuns.ts","../src/layout/layoutCache.ts"],"names":["kerningBefore","group","strokeKey"],"mappings":"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","file":"chunk-4OGXTESY.js","sourcesContent":["/**\n * Runs-aware MSDF layout. Walks `ResolvedRun[]` codepoint-by-codepoint,\n * switching atlas per run via `resolveFontVariant`, applying kerning\n * (using the left glyph's atlas table and size scaling, including across\n * run boundaries), and bucketing emitted quads by\n * `(family, resolvedWeight, resolvedStyle, syntheticBold, syntheticItalic, fillKey)`\n * so the renderer issues one draw call per atlas+color group.\n *\n * A run's `letterSpacing` (world units, so it does not scale with `fontSize`)\n * is added to the advance *after* every character of that run, including the\n * last one on a line — the CSS `letter-spacing` rule, chosen so a DOM overlay\n * rendering the same text can be made to agree. Trailing tracking therefore\n * widens the measured line width and counts toward wrapping. Spaces are\n * tracked like any other character; a newline is not (it consumes no advance).\n * One caveat against CSS: tracking is applied per *code point*, not per\n * grapheme cluster, so `e` + U+0301 takes tracking twice where CSS would\n * space the cluster once.\n *\n * Word wrap is applied when `maxWidth` is finite: words are committed to\n * a new line when they would exceed the current line width. Forced line\n * breaks are emitted for `\\n` codepoints. Every run on a line shares one\n * baseline, sunk to clear the tallest run's ascent, so mixing sizes or faces\n * aligns them the way inline text aligns everywhere else; line height is\n * `max(fontSize * lineHeight)` across the line.\n *\n * A run may also sit off that shared baseline: `ResolvedRun.baselineShift`\n * displaces it, which is what `script: 'super' | 'sub'` resolves to. The shift\n * moves the run's glyphs, its outline geometry and its own decoration rules\n * together, and deliberately does not feed back into the line's baseline or\n * its height — a superscript rides on the line rather than reflowing it.\n *\n * Underline, strikethrough and overline come out on a second channel,\n * `decorations` —\n * solid rectangles, not textured glyphs, so they cannot ride in a group's\n * `quads` (which upload UVs into an MSDF program). They are accumulated\n * during the same per-line pen walk that emits quads, *not* reconstructed\n * from quad extents afterwards: quads exist only for glyphs with ink, so a\n * decorated span's spaces would punch holes in a rule derived from them.\n *\n * ### Coordinates are origin-relative\n *\n * Every coordinate out of here is measured from the text's own top-left, not\n * from anywhere on the page: the caller translates. That is what lets a text\n * node be dragged without re-laying out — `layoutCache` would otherwise need\n * the position in its key and miss on every frame of the drag — and it costs\n * the caller one addition per vertex, inside loops already walking every one.\n * Alignment, wrapping, tracking and decoration placement all read widths and\n * pen deltas, never an absolute coordinate, so the translation is exact.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\nimport {\n resolveFontVariant, resolveGlyphFallback, glyphOutline,\n type ResolveResult, type BmFontChar, type BmFont,\n} from '@weasel-js/font';\nimport type { ResolvedRun } from '../runs/resolveRuns';\n\n/** One textured glyph quad, origin-relative — see the header. */\nexport interface LaidOutQuad {\n x0: number; y0: number; x1: number; y1: number;\n u0: number; v0: number; u1: number; v1: number;\n /** Y coordinate of the line's baseline (penY at quad emission). Used by the\n * synthetic-italic vertex skew so above-baseline vertices lean right. */\n baselineY: number;\n}\n\n/**\n * One glyph the renderer should tessellate rather than sample: real font\n * outline geometry, for text large enough that a distance field shows the\n * raster it was reconstructed from.\n *\n * `d` is em-space SVG path data straight from `@weasel-js/font`'s outline\n * registry — 1 unit is 1 em, y grows down, origin on the baseline at the pen.\n * Placing it is therefore a uniform scale by `scale` (world units per em)\n * followed by a translate to `(x, baselineY)`, and nothing here depends on\n * zoom: the same glyph at the same size in two places is the same geometry\n * twice, which is what lets the renderer tessellate it once and cache it.\n *\n * `key` identifies that cached tessellation — face identity plus codepoint,\n * assembled here because layout is where both are in hand.\n */\nexport interface LaidOutOutlineGlyph {\n d: string;\n key: string;\n /** Pen position: where em-space x = 0 lands, in world units. */\n x: number;\n /** The line's baseline: where em-space y = 0 lands, in world units. */\n baselineY: number;\n /** World units per em — the run's `fontSize`, since em space is unit-scale. */\n scale: number;\n}\n\nexport interface LaidOutGroup {\n /** Resolved atlas family — may differ from the requested family when the\n * cross-family fallback policy substituted a default. This is what the\n * renderer looks the atlas up by, so it must be the one that resolves. */\n family: string;\n /** Resolved variant — matches the registered atlas and the texture-cache key. */\n weight: number;\n style: 'normal' | 'italic';\n /** Gap between the request and the resolved match. Drives shader uniforms. */\n synthetic: { bold: boolean; italic: boolean };\n /** Which glyph source (and therefore shader/texture) this group binds:\n * baked MSDF atlas, the runtime canvas-SDF dynamic atlas, or tessellated\n * font outlines. */\n source: 'atlas' | 'canvas' | 'outline';\n /** Dynamic-atlas page index for 'canvas' groups; 0 for the others. */\n page: number;\n fill: FillStyle;\n /** Outline painted over the glyphs, or absent for none. Only ever set on\n * an `'outline'` group — the SDF tiers have no geometry to stroke, which\n * is why a stroke pulls its run onto the outline tier at any size. A run\n * the tier cannot serve at all (no registered outlines, synthetic bold)\n * stays on the atlas and carries the request no further. */\n stroke?: Stroke;\n /** Textured glyph quads. Always empty for an `'outline'` group. */\n quads: LaidOutQuad[];\n /** Outline geometry. Always empty for an `'atlas'` / `'canvas'` group —\n * the two channels are exclusive, since the group is also the draw call\n * and one draw call binds one program. */\n glyphs: LaidOutOutlineGlyph[];\n}\n\n/**\n * One decoration rule: an axis-aligned solid rectangle in the same world\n * space as `LaidOutQuad`. Never spans a line break, and carries the fill of\n * the run(s) it decorates so the rule follows the text colour.\n */\nexport interface LaidOutDecoration {\n kind: 'underline' | 'strikethrough' | 'overline';\n x0: number; y0: number; x1: number; y1: number;\n fill: FillStyle;\n}\n\n/**\n * One laid-out line's box, in the same world space as `LaidOutQuad`.\n *\n * `[x0, x1]` is the line's own advance width *after* alignment — not the\n * wrap box — so a short centered line reports the span it actually occupies.\n * `[y0, y1]` is the full line box: `y0` is the pen's line top, `y1` is\n * `y0 + max(fontSize * lineHeight)` over the line's runs. Ink can escape it\n * vertically (a tall accent, a deep descender at a lineHeight below ~1.06 —\n * see the `bounds` note at the end of `layoutRuns`); this is the typographic\n * box, not an ink bounding box.\n *\n * Emitted for every line including empty ones, so indices line up with the\n * wrap. An empty line has `x0 === x1`.\n */\nexport interface LaidOutLineBox {\n x0: number; y0: number; x1: number; y1: number;\n /** The line's baseline, for callers placing carets or rules against it. */\n baselineY: number;\n /**\n * Caret stops along this line, left to right: `caretXs[i]` is the left edge\n * of the i-th advance cell, and the last entry is the line's right edge, so\n * there is always one more stop than cell. Post-alignment, in the same\n * origin-relative space as `x0`/`x1`.\n *\n * A cell spans one code point *plus* the kerning that precedes the next\n * one, which is why a caret snapped to these midpoints lands where the\n * glyphs actually were painted rather than where an unkerned re-measure\n * would put them.\n */\n caretXs: number[];\n /**\n * Source offset for each stop in `caretXs`, as a UTF-16 index into the\n * runs' concatenated text. Not contiguous: a code point the face cannot\n * serve occupies no cell, and the wrap swallows the break between lines.\n */\n caretIndices: number[];\n}\n\nexport interface LaidOutRuns {\n groups: LaidOutGroup[];\n /** Decoration rules, in line order; within a span, underline then\n * strikethrough then overline. Empty when nothing is decorated. */\n decorations: LaidOutDecoration[];\n /** Per-line boxes in layout order. Lets a caller reason about where the\n * text actually sits inside its wrap box without re-running the wrap —\n * `textLineBoxes` builds the text silhouette from these, so picking and\n * painting cannot drift apart. */\n lines: LaidOutLineBox[];\n bounds: { width: number; height: number };\n}\n\n/**\n * Decoration placement and weight, as fractions of the run's `fontSize`.\n * Offsets are the *top* edge of the rule, measured down from the baseline —\n * so the two rules that sit above it are negative.\n *\n * DERIVED, NOT MEASURED. `BmFont` exposes only `info.size`, `common.base`\n * and `common.lineHeight` — a BMFont JSON carries no decoration metrics at\n * all. A future HarfBuzz / OpenType path would read the real\n * `underlinePosition` and `underlineThickness` off the `post` table and\n * retire these numbers.\n *\n * Pinned by `tests/visual/text-decoration.spec.ts`, which measures the gap\n * between the two rules rather than a golden image — `text.spec.ts`'s 5%\n * tolerance cannot see one of these move.\n */\nconst UNDERLINE_OFFSET = 0.10;\nconst STRIKETHROUGH_OFFSET = -0.30;\nconst OVERLINE_OFFSET = -0.90;\nconst DECORATION_THICKNESS = 0.05;\n\n/**\n * Where a run's advances, kerning and baseline come from.\n *\n * The two tiers that carry metrics express them in different units — a baked\n * atlas in its own bake-size units, a parsed face in ems — so the walk reads\n * both through this instead of naming a `BmFont`. `size` is the divisor that\n * takes either into world units (`fontSize / size`), which is why an em-space\n * face reports `size: 1`.\n *\n * Not exported: a consumer supplies metrics by registering a font, not by\n * implementing this. It exists so the atlas is one case rather than the only\n * one.\n */\ninterface MetricsSource {\n /** Units per em of whatever `advance`, `base` and `kernOf` are measured in. */\n size: number;\n /** Line top to baseline, in those same units. */\n base: number;\n /** Advance for `cp`, or null when this source has no glyph for it. */\n advanceOf(cp: number): number | null;\n kernOf(left: number, right: number): number;\n}\n\nfunction atlasMetrics(font: BmFont): MetricsSource {\n return {\n size: font.info.size,\n base: font.common.base,\n advanceOf: (cp) => font.charMap.get(cp)?.xadvance ?? null,\n kernOf: (l, r) => font.kerningMap.get(l)?.get(r) ?? 0,\n };\n}\n\nexport interface LayoutRunsOpts {\n maxWidth: number;\n lineHeight: number;\n align: 'left' | 'center' | 'right';\n /**\n * World-space `fontSize` at or above which glyphs are emitted as outline\n * geometry, when the resolved face has outlines registered. Omit (the\n * default) to keep every glyph on its SDF tier.\n *\n * A *world* size rather than a screen size because layout knows nothing\n * about the view; `drawText` divides its screen-pixel threshold by the view\n * scale before calling, so zooming in lowers the world size that qualifies.\n *\n * This is a rendering switch only. Advances, kerning, wrapping and\n * baselines are identical either way — the outline tier changes what a\n * glyph looks like, never where it sits — so measurement callers\n * (`measureTextBounds`, `textLineBoxes`) leave it unset and still agree\n * with the paint, and crossing the threshold cannot reflow text.\n *\n * Ignored for a family that resolved to the outline tier because it has no\n * atlas: there the geometry is not an upgrade over another rendering, it is\n * the only one, so the threshold has nothing to choose between.\n */\n outlineMinSize?: number;\n}\n\ninterface LayoutContext {\n groups: Map<string, LaidOutGroup>;\n}\n\nfunction fillKey(p: FillStyle): string {\n if ('color' in p) return `s:${p.color}:${p.opacity ?? 1}`;\n // Non-solid paints (gradients/patterns) defeat grouping in this slice —\n // every occurrence gets its own group. Acceptable in v1 since per-run\n // non-solid fills are rare; revisit if it bites perf.\n return `nx:${Math.random()}`;\n}\n\n/**\n * Whether two runs' fills paint the same, for the purpose of merging their\n * decoration rules. Solid paints compare by value so two separate runs set\n * to the same colour merge; anything else compares by reference, because a\n * gradient/pattern has no cheap structural equality and a false positive\n * would paint one run's rule with another's paint.\n */\nfunction sameFill(a: FillStyle, b: FillStyle): boolean {\n if (a === b) return true;\n if ('color' in a && 'color' in b) {\n return a.color === b.color && (a.opacity ?? 1) === (b.opacity ?? 1);\n }\n return false;\n}\n\n/** Whether a stroke puts ink down — an omitted or zero-width one does not,\n * and must not pull a run onto the outline tier for nothing. A screen-pixel\n * width is positive on the same terms; what it resolves to in world units\n * depends on a scale this layer does not have and does not need. */\nfunction strokePaints(s: Stroke | undefined): boolean {\n if (s === undefined) return false;\n const w = s.width ?? 1;\n return (typeof w === 'number' ? w : w.px) > 0;\n}\n\n/**\n * Draw state for a stroke, for grouping. Two runs stroked the same way share\n * a draw call; anything else has to split, since the group *is* the draw call\n * and one call paints one ribbon. Paint identity reuses `fillKey`, so a\n * gradient-stroked run gets its own group the same way a gradient-filled one\n * does.\n */\nfunction strokeKey(s: Stroke | undefined): string {\n if (!s) return '-';\n const width = s.width ?? 1;\n return [\n // A screen-pixel width keys apart from the equal world-unit number: the two\n // resolve to different ribbons, so they must not share a draw call.\n fillKey(s.paint), typeof width === 'number' ? width : `px${width.px}`,\n s.join ?? 'miter', s.cap ?? 'butt',\n s.miterLimit ?? '', s.align ?? 'center', (s.dash ?? []).join(','),\n ].join(':');\n}\n\nfunction groupKey(\n family: string,\n weight: number,\n style: 'normal' | 'italic',\n synthetic: { bold: boolean; italic: boolean },\n fill: FillStyle,\n stroke: Stroke | undefined,\n source: 'atlas' | 'canvas' | 'outline',\n page: number,\n): string {\n return `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${fillKey(fill)}|${strokeKey(stroke)}|${source}|${page}`;\n}\n\n/**\n * `source` overrides the tier the resolver picked. Passed for outline glyphs,\n * which are served by a face the *resolver* still reports as atlas or canvas\n * — the tier decision for a glyph is made per glyph, at its size, not per run\n * — and which must not share a draw call with the quads around them.\n */\nfunction getOrCreateGroup(\n ctx: LayoutContext,\n run: ResolvedRun,\n resolved: ResolveResult,\n page: number,\n sourceOverride?: 'outline',\n): LaidOutGroup {\n // The *resolved* family, not the requested one. The renderer looks this up\n // with an exact `getFont`, so a group tagged with a family that has no atlas\n // uploads no texture and draws no quads — the run would occupy layout space\n // and paint nothing. It also has to be in the key: two requested families\n // substituting to different atlases must not collide, and two substituting to\n // the same atlas should merge into one draw call.\n const atlasFamily = resolved.resolved.family;\n const resolvedWeight = resolved.resolved.weight;\n const resolvedStyle = resolved.resolved.style;\n const source = sourceOverride ?? resolved.source;\n const key = groupKey(\n atlasFamily,\n resolvedWeight,\n resolvedStyle,\n resolved.synthetic,\n run.fill,\n source === 'outline' ? run.stroke : undefined,\n source,\n page,\n );\n let g = ctx.groups.get(key);\n if (!g) {\n g = {\n family: atlasFamily,\n weight: resolvedWeight,\n style: resolvedStyle,\n synthetic: { ...resolved.synthetic },\n source,\n page,\n fill: run.fill,\n // Only the outline tier can paint it, and carrying it on an SDF group\n // would be a promise the renderer cannot keep.\n ...(source === 'outline' && run.stroke !== undefined ? { stroke: run.stroke } : {}),\n quads: [],\n glyphs: [],\n };\n ctx.groups.set(key, g);\n }\n return g;\n}\n\n/**\n * A glyph, plus which tier ended up serving it. `resolved`/`font` differ from\n * the run's own when a codepoint escalated to the dynamic tier — the caller\n * has to carry both, because they pick the group (shader, texture, page) and\n * the scale divisor (`font.info.size`).\n */\ninterface GlyphHit {\n glyph: BmFontChar;\n font: BmFont;\n resolved: ResolveResult;\n}\n\n/**\n * Find a glyph for `cp`, escalating past the run's atlas when it has none.\n *\n * Until 2026-07-29 a miss drew codepoint 63 — a literal `?`. That fabricates\n * a character the author never wrote and is indistinguishable from one they\n * did, which is how the committed text baseline came to read \"Themed editing\n * ? magenta caret\" for a whole commit without anyone noticing. Every real\n * text stack draws `.notdef` (tofu) precisely because it can't be mistaken\n * for content; the BmFont atlas format has no such glyph to draw, so the\n * order here is: the atlas, then the dynamic tier (which rasterizes from\n * installed fonts and can usually serve the character for real), then\n * nothing — with a warning naming the codepoint.\n */\nfunction resolveGlyph(\n run: ResolvedRun,\n font: BmFont,\n resolved: ResolveResult,\n cp: number,\n): GlyphHit | null {\n const direct = font.charMap.get(cp);\n if (direct) return { glyph: direct, font, resolved };\n\n const fallback = resolveGlyphFallback(run.fontFamily, run.fontWeight, run.fontStyle);\n const face = fallback?.dynamicFace;\n if (fallback && face) {\n const glyph = face.requestGlyph(cp);\n // A face that can't measure the codepoint reports a zero advance and no\n // ink — nothing worth switching groups for, so fall through to the warn.\n if (glyph.xadvance > 0 || glyph.width > 0) {\n return { glyph, font: face.font, resolved: fallback };\n }\n }\n\n warnMissingGlyphOnce(resolved.resolved.family, cp);\n return null;\n}\n\n// Layout runs per frame, so an unguarded warn would flood the console. Keyed\n// per (family, codepoint): one message per character the app actually can't\n// draw, however many times it appears.\nconst warnedMissingGlyphs = new Set<string>();\n\nfunction warnMissingGlyphOnce(family: string, cp: number): void {\n const key = `${family}|${cp}`;\n if (warnedMissingGlyphs.has(key)) return;\n warnedMissingGlyphs.add(key);\n const ch = String.fromCodePoint(cp);\n console.warn(\n `weasel layoutRuns: no glyph for U+${cp.toString(16).toUpperCase().padStart(4, '0')} ` +\n `(${JSON.stringify(ch)}) in \"${family}\", and the dynamic tier could not ` +\n `rasterize it — skipping the character. Bake it into the atlas, or call ` +\n `registerCanvasFont(\"${family}\") to serve missing codepoints from ` +\n `installed fonts.`,\n );\n}\n\n/** @internal Test seam — the warn-once keys are module state. */\nexport function _resetMissingGlyphWarningsForTests(): void {\n warnedMissingGlyphs.clear();\n}\n\nexport function layoutRuns(\n runs: readonly ResolvedRun[],\n opts: LayoutRunsOpts,\n): LaidOutRuns {\n const ctx: LayoutContext = { groups: new Map() };\n\n // Per-glyph entry produced by walking runs codepoint-by-codepoint.\n // Position (x) is filled in during the line-fitting pass.\n interface Entry {\n run: ResolvedRun;\n /** The atlas to sample, or null on the outline tier, which has none. */\n font: BmFont | null;\n metrics: MetricsSource;\n glyph: BmFontChar | null;\n cp: number;\n advance: number; // xadvance in world units (already scaled)\n tracking: number; // run letterSpacing, added after this glyph (world units)\n kerningBefore: number; // kerning gap consumed before this glyph\n isSpace: boolean;\n isNewline: boolean;\n resolved: ResolveResult;\n fontSize: number;\n /** UTF-16 offset of this code point in the runs' concatenated text. */\n srcIndex: number;\n /** One past it — `srcIndex + 2` for an astral code point. */\n srcEnd: number;\n }\n\n // 1. Flatten all runs into entries with per-glyph data, computing\n // kerning using the left glyph's atlas+scale across run boundaries.\n const entries: Entry[] = [];\n let prevCp: number | undefined;\n let prevMetrics: MetricsSource | undefined;\n let prevFontSize: number | undefined;\n let srcIndex = 0;\n\n for (const run of runs) {\n const resolved = resolveFontVariant(run.fontFamily, run.fontWeight, run.fontStyle);\n const outlineFace = resolved.outlineFace;\n const font = resolved.entry?.font ?? resolved.dynamicFace?.font ?? null;\n // Em space is unit-scale, so a parsed face divides by 1 and its advances\n // are already the world-unit fractions of `fontSize` layout wants.\n const metrics: MetricsSource | undefined = outlineFace\n ? { size: 1, base: outlineFace.ascender, advanceOf: (cp) => outlineFace.advanceOf(cp), kernOf: (l, r) => outlineFace.kernOf(l, r) }\n : font ? atlasMetrics(font) : undefined;\n if (!metrics) {\n // Skipped, but its characters still occupy source offsets — dropping\n // them here would shift every later run's caret indices left.\n srcIndex += run.text.length;\n prevCp = undefined; prevMetrics = undefined; prevFontSize = undefined;\n continue;\n }\n const scale = run.fontSize / metrics.size;\n // World units — deliberately not scaled by fontSize, so the same tracking\n // opens the same visual gap whatever size the run is set at.\n const tracking = run.letterSpacing;\n\n for (const ch of [...run.text]) {\n const cp = ch.codePointAt(0)!;\n const isNewline = cp === 10;\n const isSpace = cp === 32;\n const srcStart = srcIndex;\n const srcEnd = srcIndex + ch.length;\n srcIndex = srcEnd;\n\n if (isNewline) {\n entries.push({\n run, font, metrics,\n glyph: { id: cp, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },\n // A newline consumes no advance, so it takes no tracking either.\n cp, advance: 0, tracking: 0, kerningBefore: 0, isSpace: false, isNewline: true,\n resolved, fontSize: run.fontSize, srcIndex: srcStart, srcEnd,\n });\n prevCp = undefined; prevMetrics = undefined; prevFontSize = undefined;\n continue;\n }\n\n if (isSpace) {\n // Use atlas glyph if present; otherwise synthesize a zero-size entry\n // with a reasonable advance so spaces still participate in line-fitting.\n const spaceGlyph = resolved.dynamicFace ? resolved.dynamicFace.requestGlyph(32) : font?.charMap.get(32) ?? null;\n const spaceAdvance = spaceGlyph ? spaceGlyph.xadvance : metrics.advanceOf(32);\n const advance = spaceAdvance !== null\n ? spaceAdvance * scale\n : run.fontSize * 0.25;\n let kerningBefore = 0;\n if (prevCp !== undefined && prevMetrics !== undefined && prevFontSize !== undefined) {\n kerningBefore = prevMetrics.kernOf(prevCp, cp) * (prevFontSize / prevMetrics.size);\n }\n entries.push({\n run, font, metrics,\n glyph: spaceGlyph ?? { id: 32, x: 0, y: 0, width: 0, height: 0, xoffset: 0, yoffset: 0, xadvance: 0, page: 0 },\n cp, advance, tracking, kerningBefore, isSpace: true, isNewline: false,\n resolved, fontSize: run.fontSize, srcIndex: srcStart, srcEnd,\n });\n prevCp = cp; prevMetrics = metrics; prevFontSize = run.fontSize;\n continue;\n }\n\n // The outline tier has no atlas to miss in and no tier below it to\n // escalate to: the face either has the glyph or the run does not\n // contain it. Its advance is the only thing needed to place it.\n if (outlineFace) {\n const adv = metrics.advanceOf(cp);\n if (adv === null) {\n prevCp = cp; prevMetrics = metrics; prevFontSize = run.fontSize;\n continue;\n }\n let kerningBefore = 0;\n if (prevCp !== undefined && prevMetrics !== undefined && prevFontSize !== undefined) {\n kerningBefore = prevMetrics.kernOf(prevCp, cp) * (prevFontSize / prevMetrics.size);\n }\n entries.push({\n run, font: null, metrics, glyph: null, cp,\n advance: adv * scale,\n tracking, kerningBefore, isSpace, isNewline: false,\n resolved, fontSize: run.fontSize, srcIndex: srcStart, srcEnd,\n });\n prevCp = cp; prevMetrics = metrics; prevFontSize = run.fontSize;\n continue;\n }\n\n const hit = resolved.dynamicFace\n ? { glyph: resolved.dynamicFace.requestGlyph(cp), font: font!, resolved }\n : resolveGlyph(run, font!, resolved, cp);\n if (!hit) {\n prevCp = cp; prevMetrics = metrics; prevFontSize = run.fontSize;\n continue;\n }\n // An escalated codepoint is served by a different atlas with its own\n // bake size, so its scale — and the group it lands in — are its own.\n const glyphFont = hit.font;\n const glyphMetrics = glyphFont === font ? metrics : atlasMetrics(glyphFont);\n const glyphScale = run.fontSize / glyphFont.info.size;\n\n let kerningBefore = 0;\n if (prevCp !== undefined && prevMetrics !== undefined && prevFontSize !== undefined) {\n kerningBefore = prevMetrics.kernOf(prevCp, cp) * (prevFontSize / prevMetrics.size);\n }\n\n entries.push({\n run, font: glyphFont, metrics: glyphMetrics, glyph: hit.glyph, cp,\n advance: hit.glyph.xadvance * glyphScale,\n tracking,\n kerningBefore,\n isSpace, isNewline: false,\n resolved: hit.resolved, fontSize: run.fontSize, srcIndex: srcStart, srcEnd,\n });\n\n prevCp = cp; prevMetrics = glyphMetrics; prevFontSize = run.fontSize;\n }\n }\n\n // 2. Walk entries, accumulating lines bounded by maxWidth when finite.\n // A \"word\" is a maximal run of non-space, non-newline entries; after\n // each word, decide whether it fits on the current line.\n interface Line {\n entries: Entry[];\n width: number;\n height: number;\n /** Set only for a line with no entries at all — the newline that closed\n * it, which is the only carrier of the style a blank line should take\n * its height and baseline from. */\n blank?: Entry;\n }\n const lines: Line[] = [];\n let cur: Line = { entries: [], width: 0, height: 0 };\n\n function commitLine(): void {\n lines.push(cur);\n cur = { entries: [], width: 0, height: 0 };\n }\n\n let i = 0;\n while (i < entries.length) {\n const e = entries[i];\n if (e.isNewline) {\n // A blank line still advances the pen. `cur.height` is only raised when\n // an entry is pushed, so without this a line holding nothing but the\n // newline itself measured zero and `\"a\\n\\nb\"` painted `b` directly\n // under `a` — the blank line vanished instead of opening a gap. The\n // newline's own run supplies the style, since no other entry can.\n if (cur.entries.length === 0) {\n cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);\n cur.blank = e;\n }\n commitLine(); i++; continue;\n }\n if (e.isSpace) {\n if (cur.entries.length > 0) {\n cur.entries.push(e);\n cur.width += e.kerningBefore + e.advance + e.tracking;\n cur.height = Math.max(cur.height, e.fontSize * opts.lineHeight);\n }\n i++;\n continue;\n }\n // Accumulate the upcoming word: entries up to next space/newline/EOR.\n let j = i;\n let wordWidth = 0;\n while (j < entries.length && !entries[j].isSpace && !entries[j].isNewline) {\n const w = entries[j];\n wordWidth += w.kerningBefore + w.advance + w.tracking;\n j++;\n }\n if (Number.isFinite(opts.maxWidth) && cur.width + wordWidth > opts.maxWidth && cur.entries.length > 0) {\n commitLine();\n }\n for (let k = i; k < j; k++) {\n const w = entries[k];\n const kerningBefore = cur.entries.length === 0 ? 0 : w.kerningBefore;\n cur.entries.push({ ...w, kerningBefore });\n cur.width += kerningBefore + w.advance + w.tracking;\n cur.height = Math.max(cur.height, w.fontSize * opts.lineHeight);\n }\n i = j;\n }\n if (cur.entries.length > 0) commitLine();\n\n /**\n * Em-space outline for `e`, or `null` to leave it on its SDF tier.\n *\n * Every `null` here is a rung of the fallback ladder, and they are all\n * ordinary: the caller never opted in, the glyph is too small to be worth\n * it, the face has no outlines registered, its bytes are still loading or\n * failed to load, or the font simply has no such glyph. The tier is an\n * upgrade applied where it is available, never a requirement.\n */\n function outlineFor(e: Entry): string | null {\n const r0 = e.resolved.resolved;\n // An outline-only run has no atlas behind it. Every gate below trades one\n // rendering for another; here there is no other, so gating would paint\n // nothing at all.\n if (e.resolved.source === 'outline') {\n return glyphOutline(r0.family, r0.weight, r0.style, e.cp);\n }\n const min = opts.outlineMinSize;\n if (min === undefined) return null;\n // A stroked run ignores the size gate. The gate is there because the SDF\n // tiers reconstruct small text better than tessellated geometry does —\n // but they have no geometry to stroke, so leaving a stroked run below the\n // threshold drops the outline the consumer asked for rather than trading\n // one correct rendering for another.\n if (e.fontSize < min && !strokePaints(e.run.stroke)) return null;\n // Synthetic bold is an SDF threshold shift, and a path has no threshold.\n // Emboldening geometry properly means offsetting the outline — the same\n // problem as stroke-to-fill, which the kit does not solve yet — and\n // painting the regular weight instead would make text get *lighter* as\n // you zoom past the threshold. Leave it with the tier that can fake it.\n // Synthetic italic is not in the same position: a shear is exact on\n // geometry, and the renderer applies it.\n if (e.resolved.synthetic.bold) return null;\n const r = e.resolved.resolved;\n return glyphOutline(r.family, r.weight, r.style, e.cp);\n }\n\n // 3. Lay out each line: apply alignment, then emit quads (and the\n // decoration rules that span them).\n const decorations: LaidOutDecoration[] = [];\n\n /** An open decoration span: contiguous entries on one line that agree on\n * which rules to draw, what colour to draw them, and the metrics that set\n * their placement. `x1` grows as the pen walks. */\n interface DecoSpan {\n underline: boolean;\n strikethrough: boolean;\n overline: boolean;\n fill: FillStyle;\n fontSize: number;\n baselineY: number;\n x0: number;\n x1: number;\n }\n let span: DecoSpan | null = null;\n\n function flushSpan(): void {\n const s = span;\n span = null;\n if (!s || s.x1 <= s.x0) return;\n const thickness = s.fontSize * DECORATION_THICKNESS;\n if (s.underline) {\n const y0 = s.baselineY + s.fontSize * UNDERLINE_OFFSET;\n decorations.push({ kind: 'underline', x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });\n }\n if (s.strikethrough) {\n const y0 = s.baselineY + s.fontSize * STRIKETHROUGH_OFFSET;\n decorations.push({ kind: 'strikethrough', x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });\n }\n if (s.overline) {\n const y0 = s.baselineY + s.fontSize * OVERLINE_OFFSET;\n decorations.push({ kind: 'overline', x0: s.x0, y0, x1: s.x1, y1: y0 + thickness, fill: s.fill });\n }\n }\n\n const lineBoxes: LaidOutLineBox[] = [];\n let penY = 0;\n let maxLineWidth = 0;\n const finiteWidth = Number.isFinite(opts.maxWidth) ? opts.maxWidth : 0;\n for (const line of lines) {\n const alignShift = (() => {\n if (opts.align === 'left') return 0;\n // With a finite box, distribute the slack within `maxWidth` (x = 0 is\n // the box's left edge). With no box (infinite maxWidth), anchor on the\n // line's own width instead — x = 0 is the text's midpoint ('center')\n // or right edge ('right'). This matches the canvas-2D `renderLabel`\n // anchor model so point-anchored labels center on x in both backends.\n if (!Number.isFinite(opts.maxWidth)) {\n return opts.align === 'center' ? -line.width / 2 : -line.width;\n }\n const slack = finiteWidth - line.width;\n return opts.align === 'center' ? slack / 2 : slack;\n })();\n const lineX0 = alignShift;\n // One baseline for the whole line, sunk far enough below the line top to\n // clear the tallest run's ascent — so runs set at different sizes sit on\n // it together instead of each hanging from the line top at its own depth.\n // A blank line has no entry to measure, so it falls back to the newline\n // that closed it.\n //\n // Deliberately computed from *unshifted* ascents: a run's `baselineShift`\n // moves it off this baseline, so letting the shift feed back into the\n // baseline it is measured against would drag the rest of the line with it.\n let lineAscent = 0;\n for (const e of line.entries) {\n lineAscent = Math.max(lineAscent, e.metrics.base * (e.fontSize / e.metrics.size));\n }\n if (line.entries.length === 0 && line.blank) {\n lineAscent = line.blank.metrics.base\n * (line.blank.fontSize / line.blank.metrics.size);\n }\n const lineBaselineY = penY + lineAscent;\n\n const caretXs: number[] = [];\n const caretIndices: number[] = [];\n\n let penX = lineX0;\n for (const e of line.entries) {\n penX += e.kerningBefore;\n caretXs.push(penX);\n caretIndices.push(e.srcIndex);\n // One step per character: the glyph's advance plus its run's tracking.\n // Every branch below moves the pen by exactly this, so glyph positions\n // stay in step with the line width accumulated above.\n const step = e.advance + e.tracking;\n const scale = e.fontSize / e.metrics.size;\n // Positive raises, and y grows down, so the shift subtracts. Everything\n // below places against `baselineY` and so follows the run up or down —\n // its glyphs, its outline geometry and its decoration rules alike.\n const baselineY = lineBaselineY - e.run.baselineShift;\n\n // Extend or (re)open the decoration span *before* the no-ink bail-out\n // below, so a decorated span's spaces stay under the rule. `step`\n // includes this glyph's trailing tracking, so the rule covers it — the\n // CSS rule, and the same span the line width already accounts for.\n if (e.run.underline || e.run.strikethrough || e.run.overline) {\n if (\n span !== null\n && span.underline === e.run.underline\n && span.strikethrough === e.run.strikethrough\n && span.overline === e.run.overline\n && span.fontSize === e.fontSize\n && span.baselineY === baselineY\n && sameFill(span.fill, e.run.fill)\n ) {\n // Absorbs the kerning gap we just stepped over, so a run join\n // between two identically-decorated runs shows no seam.\n span.x1 = penX + step;\n } else {\n flushSpan();\n span = {\n underline: e.run.underline,\n strikethrough: e.run.strikethrough,\n overline: e.run.overline,\n fill: e.run.fill,\n fontSize: e.fontSize,\n baselineY,\n x0: penX,\n x1: penX + step,\n };\n }\n } else {\n flushSpan();\n }\n\n // Outline tier, decided per glyph at its own size. Checked before the\n // no-ink bail-out below, not after: a dynamic-tier glyph still waiting\n // for its bake has `page < 0` and no atlas rect, but its outline is\n // available right now — there is no reason to draw nothing while the\n // exact geometry is in hand.\n const outlineD = outlineFor(e);\n if (outlineD !== null) {\n const group = getOrCreateGroup(ctx, e.run, e.resolved, 0, 'outline');\n group.glyphs.push({\n d: outlineD,\n key: `${e.resolved.resolved.family}|${e.resolved.resolved.weight}|${e.resolved.resolved.style}|${e.cp}`,\n x: penX,\n baselineY,\n // Em space is unit-scale, so world units per em is just the size.\n scale: e.fontSize,\n });\n penX += step;\n continue;\n }\n\n // Nothing to paint — a zero-advance glyph (e.g. a combining mark), a\n // zero-area one (a space in either source), or a dynamic glyph not baked\n // yet (page < 0). Advance the pen anyway so the line doesn't reflow when\n // a bake lands, and so a space's tracking still separates its neighbors.\n if (e.font === null || e.glyph === null\n || e.advance === 0 || e.glyph.width === 0 || e.glyph.page < 0) {\n penX += step;\n continue;\n }\n const group = getOrCreateGroup(ctx, e.run, e.resolved, e.glyph.page);\n const atlasW = e.font.common.scaleW;\n const atlasH = e.font.common.scaleH;\n const qx0 = penX + e.glyph.xoffset * scale;\n // `yoffset` is measured from the line top in the atlas's own frame, so\n // it is relative to that frame's baseline (`metrics.base`) — which is\n // what lets a run hang off the line's shared baseline rather than off\n // the line top, where its own ascent would place it.\n const qy0 = baselineY + (e.glyph.yoffset - e.metrics.base) * scale;\n const qx1 = qx0 + e.glyph.width * scale;\n const qy1 = qy0 + e.glyph.height * scale;\n const u0 = e.glyph.x / atlasW;\n const v0 = e.glyph.y / atlasH;\n const u1 = (e.glyph.x + e.glyph.width) / atlasW;\n const v1 = (e.glyph.y + e.glyph.height) / atlasH;\n // `baselineY` (computed above) is the typographic baseline, not the top\n // of the line box — above-baseline vertices have y < baselineY so the\n // synthetic italic skew leans them right.\n group.quads.push({ x0: qx0, y0: qy0, x1: qx1, y1: qy1, u0, v0, u1, v1, baselineY });\n penX += step;\n }\n // A rule never crosses a line break: close the span at end of line.\n flushSpan();\n // Closing stop: the line's right edge, at the offset just past its last\n // character. A blank line has no cell to close, so its only stop is the\n // newline that made it.\n const lastCell = line.entries[line.entries.length - 1];\n caretXs.push(penX);\n caretIndices.push(lastCell ? lastCell.srcEnd : (line.blank?.srcIndex ?? 0));\n lineBoxes.push({\n x0: lineX0,\n y0: penY,\n x1: lineX0 + line.width,\n y1: penY + line.height,\n baselineY: lineBaselineY,\n caretXs,\n caretIndices,\n });\n maxLineWidth = Math.max(maxLineWidth, line.width);\n penY += line.height;\n }\n\n // `bounds` measures line boxes only — a decoration rule, and a run shifted\n // off the baseline, can both fall outside it.\n // An underline's bottom sits `base * scale + (UNDERLINE_OFFSET +\n // DECORATION_THICKNESS) * fontSize` below the line top, so it escapes the\n // last line once `lineHeight` drops below roughly `base / info.size + 0.15`\n // (≈1.06 for the bundled atlases). An overline escapes the first line the\n // other way for a face whose `base / info.size` is under 0.90, and a\n // `baselineShift` escapes by however far it exceeds the slack around it.\n // None of these are reachable at the 1.2 default with the bundled atlases\n // and the `script` presets, so `measureTextBounds` and\n // `verticalAlign: 'bottom'` are left as they are; tightening the box would\n // move every existing text bound.\n return {\n groups: [...ctx.groups.values()],\n decorations,\n lines: lineBoxes,\n bounds: { width: maxLineWidth, height: penY },\n };\n}\n","/**\n * Laid-out text, memoized. The one entry point every caller of `layoutRuns`\n * should reach for — the renderer's paint path, the silhouette measurement\n * behind picking, and caret hit-testing all answer from this.\n *\n * `layoutRuns` is the most expensive derivation in the kit — it walks every\n * codepoint, resolves a face per run, measures, wraps, aligns, and places a\n * quad or an outline glyph for each — and `drawText` ran it per text command\n * per frame. Measured: 2.0 ms/frame for 200 short labels, 9.3 for 1000, and\n * 25.9 for 200 wrapped paragraphs. That last figure is the entire frame\n * budget spent before a single triangle is drawn.\n *\n * ### Two keys, tried in order\n *\n * **Array identity**, in a `WeakMap`, the same contract as the renderer's\n * `WeakMap<Path, Mesh>` — and for the same reason: it makes the entry\n * collectable with whatever owns the runs, and the lookup is a pointer\n * compare. `kit:text`'s memoized `paint` hands back a stable array per node,\n * so the renderer stays on this path.\n *\n * **Structure**, in a bounded `Map`, for every caller that cannot. Measuring\n * a pose resolves its style and allocates a fresh `ResolvedRun[]` each call,\n * so an identity-keyed cache could never hold anything for it — and those\n * callers run on the drag path, once per pose change. Serializing the runs is\n * real work, unlike the pointer compare above, which is exactly why it is\n * second: an identity hit never pays for it.\n *\n * Under either, one entry per distinct `(maxWidth, lineHeight, align, outline\n * threshold)`. Position is deliberately absent: `layoutRuns` emits\n * origin-relative geometry and `drawText` translates at upload, so a text node\n * dragged across the page keeps hitting the same entry.\n *\n * ### The outline threshold\n *\n * `outlineMinSize` is derived from the view scale, so recording it literally\n * would miss on every frame of a zoom. It reaches `layoutRuns` through exactly\n * one comparison (`run.fontSize < min`, in `outlineFor`), so what the layout\n * actually depends on is *which runs clear the bar* — and since the test is a\n * plain `>=` on size, that set is pinned by how many of the distinct run sizes\n * clear it. Recording that count is exact, not a quantization: the entry stays\n * valid across every zoom that doesn't cross a glyph size, and is dropped the\n * moment one does.\n *\n * ### What the keys cannot see\n *\n * The font set. A face landing changes metrics with no change to the runs, so\n * every lookup compares `glyphGeneration()` — advanced by the dynamic SDF\n * atlas, by the outline registry, and by `registerFont` — and drops\n * everything when it moves. That is the same escape hatch `nodeMemo`'s\n * generation counter provides, for the same class of failure. Polled rather\n * than subscribed: a module-load subscription is a cross-package side effect\n * that makes importing this package require the font package's whole surface.\n *\n * ### Bounds\n *\n * Variants per runs array are capped and evicted wholesale, matching\n * `outlineMeshCache`: the refill cost is one layout, and the bookkeeping would\n * cost more than the misses it avoids. The structural map cannot ride an\n * array's lifetime the way the `WeakMap` does — nothing collects its keys —\n * so it is a real LRU with a hard entry count.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\nimport { glyphGeneration } from '@weasel-js/font';\nimport type { ResolvedRun } from '../runs/resolveRuns';\nimport { layoutRuns, type LaidOutRuns, type LayoutRunsOpts } from './layoutRuns';\n\n/** Distinct option combinations held for one runs array before the whole set\n * is dropped. Sized for \"a few views onto the same text\". */\nexport const LAYOUT_CACHE_VARIANT_LIMIT = 8;\n\n/** Layouts held against the structural key before the least recently used one\n * is dropped. Sized for \"every text node visible on a page, plus slack\". */\nexport const LAYOUT_CACHE_STRUCTURAL_LIMIT = 64;\n\ninterface Entry {\n /** `generation` when this entry was filled. */\n generation: number;\n byVariant: Map<string, LaidOutRuns>;\n}\n\nlet cache = new WeakMap<readonly ResolvedRun[], Entry>();\nlet structural = new Map<string, LaidOutRuns>();\nlet structuralGeneration = -1;\n\n/**\n * How many of the distinct font sizes present in `runs` reach `min`. Equal\n * counts mean the same set of runs cleared the threshold, since the count is\n * monotonic in `min` over a fixed `runs` — which is what makes this a key.\n * Stroked runs need no term of their own: a stroke pulls its run onto the\n * outline tier at every `min`, so it cannot distinguish two of them.\n *\n * Returns `-1` for an absent threshold, which must stay distinct from \"a\n * threshold no run reaches\": measurement callers leave it unset, and\n * conflating the two would let a measurement answer a paint.\n */\nfunction outlineBucket(runs: readonly ResolvedRun[], min: number | undefined): number {\n if (min === undefined) return -1;\n const sizes = new Set<number>();\n for (const r of runs) sizes.add(r.fontSize);\n let n = 0;\n for (const size of sizes) if (size >= min) n++;\n return n;\n}\n\nfunction variantKey(runs: readonly ResolvedRun[], opts: LayoutRunsOpts): string {\n return `${opts.maxWidth}|${opts.lineHeight}|${opts.align}|${outlineBucket(runs, opts.outlineMinSize)}`;\n}\n\nlet nextRefId = 1;\nconst refIds = new WeakMap<object, number>();\n\n/** A stable id for a paint this key cannot serialize. */\nfunction refId(o: object): number {\n let id = refIds.get(o);\n if (id === undefined) {\n id = nextRefId++;\n refIds.set(o, id);\n }\n return id;\n}\n\n/**\n * A paint's contribution to the key. Solid paints compare by value, so two\n * runs set to the same colour share a layout; gradients and patterns get a\n * per-object id, because they have no cheap structural equality and a false\n * positive would hand back groups carrying the wrong paint.\n *\n * `layoutRuns`'s own `fillKey` mints a fresh random string for the non-solid\n * case — fine for grouping within one layout, useless as a cache key.\n */\nfunction paintKey(p: FillStyle): string {\n return 'color' in p ? `s${p.color}:${p.opacity ?? 1}` : `#${refId(p)}`;\n}\n\nfunction strokeKey(s: Stroke | undefined): string {\n if (!s) return '-';\n const width = s.width ?? 1;\n return [\n paintKey(s.paint), typeof width === 'number' ? width : `px${width.px}`,\n s.join ?? 'miter', s.cap ?? 'butt',\n s.miterLimit ?? '', s.align ?? 'center', (s.dash ?? []).join(','),\n ].join(':');\n}\n\n/**\n * Everything about `runs` that changes the layout. The two author-supplied\n * strings are length-prefixed so neither can forge a neighbouring field by\n * containing a separator; every other term is a number, an enum, or a paint\n * already escaped by `paintKey`.\n */\nfunction runsKey(runs: readonly ResolvedRun[]): string {\n let out = '';\n for (const r of runs) {\n out += `${r.text.length}:${r.text}|${r.fontFamily.length}:${r.fontFamily}`\n + `|${r.fontSize}|${r.fontWeight}|${r.fontStyle}|${r.letterSpacing}`\n + `|${r.underline ? 1 : 0}${r.strikethrough ? 1 : 0}${r.overline ? 1 : 0}`\n + `|${r.baselineShift}`\n + `|${paintKey(r.fill)}|${strokeKey(r.stroke)}|`;\n }\n return out;\n}\n\n/**\n * Lay `runs` out, reusing the previous result when nothing it depends on has\n * changed. Drop-in for `layoutRuns` — same arguments, same return value.\n *\n * **The result is shared and must be treated as immutable.** `drawText`\n * applies position and `verticalAlign` while packing vertices rather than by\n * shifting the layout, precisely so it can be handed the same object every\n * frame.\n */\nexport function cachedLayoutRuns(\n runs: readonly ResolvedRun[],\n opts: LayoutRunsOpts,\n): LaidOutRuns {\n // Polled, not subscribed. A module-level `subscribeGlyphReady` would be a\n // load-time side effect reaching into another package — it made importing\n // the renderer require the font package's whole surface, breaking any\n // consumer that partially mocks it.\n const fonts = glyphGeneration();\n let entry = cache.get(runs);\n if (entry === undefined) {\n entry = { generation: fonts, byVariant: new Map() };\n cache.set(runs, entry);\n } else if (entry.generation !== fonts) {\n entry.generation = fonts;\n entry.byVariant.clear();\n }\n\n const key = variantKey(runs, opts);\n const hit = entry.byVariant.get(key);\n if (hit !== undefined) return hit;\n\n if (structuralGeneration !== fonts) {\n structural.clear();\n structuralGeneration = fonts;\n }\n const structuralK = `${runsKey(runs)}\u0000${key}`;\n let laid = structural.get(structuralK);\n if (laid !== undefined) {\n // Re-insert to move it to the young end; `Map` iterates in insertion order,\n // which is what makes `keys().next()` the least recently used.\n structural.delete(structuralK);\n } else {\n laid = layoutRuns(runs, opts);\n if (structural.size >= LAYOUT_CACHE_STRUCTURAL_LIMIT) {\n structural.delete(structural.keys().next().value!);\n }\n }\n structural.set(structuralK, laid);\n\n if (entry.byVariant.size >= LAYOUT_CACHE_VARIANT_LIMIT) entry.byVariant.clear();\n entry.byVariant.set(key, laid);\n return laid;\n}\n\n/** Test helper. Do not call from product code. */\nexport function _resetLayoutCacheForTests(): void {\n cache = new WeakMap();\n structural = new Map();\n structuralGeneration = -1;\n}\n"]}