26 lines
1.0 KiB
TypeScript
26 lines
1.0 KiB
TypeScript
import type { Font, Measure } from "./types.js";
|
|||
|
|
|
||
|
|
export const fontString = (font: Font) =>
|
||
|
|
`${font.italic ? "italic " : ""}${font.weight === "bold" ? "bold " : ""}${font.size}px ${font.family}`;
|
||
|
|
|
||
|
|
// A bounded per-surface cache replaces the original global memoizee cache.
|
||
|
|
// Font loading creates a new measure/layout so fallback metrics cannot persist.
|
||
|
|
export function canvasMeasure(context: CanvasRenderingContext2D, maximum = 12000): Measure {
|
||
|
|
const cache = new Map<string, ReturnType<Measure>>();
|
||
|
|
return (text, font) => {
|
||
|
|
const key = `${fontString(font)}/${text}`;
|
||
|
|
const cached = cache.get(key);
|
||
|
|
if (cached) return cached;
|
||
|
|
context.font = fontString(font);
|
||
|
|
const metrics = context.measureText(text);
|
||
|
|
const value = {
|
||
|
|
width: Math.max(0, metrics.width),
|
||
|
|
ascent: Math.max(metrics.actualBoundingBoxAscent || 0, font.size * 0.85),
|
||
|
|
descent: Math.max(metrics.actualBoundingBoxDescent || 0, font.size * 0.22),
|
||
|
|
};
|
||
|
|
if (cache.size >= maximum) cache.clear();
|
||
|
|
cache.set(key, value);
|
||
|
|
return value;
|
||
|
|
};
|
||
|
|
}
|