/** * Cached single-line text measurement, shared by the Text shader's rasterizer and its * bounding-box computeBounds in the Design Editor. * * Everything is measured at a fixed 100px reference size: width scales linearly with font * size (letter-spacing is em-based), so resizing text is pure math — no re-measure, and the * cache key excludes font size entirely. * * Cache entries are keyed on the font's live availability (document.fonts.check), so when a * webfont finishes loading the next measure misses the stale fallback-font entry and re-measures * with the real font — even across separate bundle instances of this module (the editor and the * renderer each carry their own cache, but document.fonts is page-global). */ export type TextTransformMode = 'none' | 'uppercase' | 'lowercase' | 'capitalize'; export interface TextMeasureSpec { text: string; fontFamily: string; fontWeight: number; italic: boolean; letterSpacingEm: number; textTransform: TextTransformMode; } export interface TextMeasureResult { /** Advance width of the run at 100px font size. */ widthPer100: number; /** Font ascent above the alphabetic baseline at 100px font size. */ ascentPer100: number; /** Font descent below the alphabetic baseline at 100px font size. */ descentPer100: number; /** * INK extents — the actual glyph bounding box of THIS run (actualBoundingBox*), not the * font box. This is what "the box hugs the text" means: a caps-only run has near-zero ink * descent while its font descent stays fixed. Falls back to the font box when unavailable. * Consumers: Text's computeBounds (tight layout stacking / overlay box). */ inkAscentPer100: number; inkDescentPer100: number; } /** Applies the text-transform mode. Used before both measuring and rasterizing. */ export declare function applyTextTransform(text: string, mode: TextTransformMode | string | undefined): string; export declare function bumpMeasureGeneration(): void; /** Whether canvas letter-spacing is supported (Chrome 99+/Safari 17.4+/FF 97+). */ export declare function supportsCanvasLetterSpacing(): boolean; /** * Measures a single-line run at the 100px reference size. Cached; safe to call per-frame. * Falls back to a rough estimate when no 2D context is available (SSR). */ export declare function measureText(spec: TextMeasureSpec): TextMeasureResult; /** * Greedy word wrap — pure, measurer-injected (unit-testable without a canvas). * Explicit lines are wrapped independently; a word longer than the max overflows on its own * line (Figma behaviour — no mid-word breaking). maxWidthPer100 <= 0 disables wrapping. */ export declare function wrapLines(text: string, maxWidthPer100: number, measureLine: (line: string) => number): string[]; export interface TextBlockMeasure { /** Final lines (explicit breaks + wrapping), post-textTransform. */ lines: string[]; /** Advance width of each line at the 100px reference. */ lineWidthsPer100: number[]; /** Widest line advance. */ widthPer100: number; /** Font-box ascent/descent (shared by all lines). */ ascentPer100: number; descentPer100: number; /** Ink extents of the FIRST line's top and the LAST line's bottom. */ inkAscentFirstPer100: number; inkDescentLastPer100: number; /** Baseline-to-baseline distance: lineHeightEm × 100. */ pitchPer100: number; /** Font-box block: ascent + (n−1)·pitch + descent. */ blockHeightPer100: number; /** Ink block: inkAscentFirst + (n−1)·pitch + inkDescentLast. */ inkHeightPer100: number; } /** * Measures a multi-line block at the 100px reference. Wrap points depend only on the * width÷fontSize ratio, so a caller passes `maxWidthPer100 = maxWidthPx × 100 / fontSizePx` * and the whole block stays linear in fontSize — the same scaling trick single-line * measurement has always used. Per-line measurement goes through the cached measureText. */ export declare function measureTextBlock(spec: TextMeasureSpec, lineHeightEm: number, maxWidthPer100?: number): TextBlockMeasure; //# sourceMappingURL=textMeasure.d.ts.map