/** * HTML/SVG string generation for text elements * Used by both rendering (html-element.tsx) and measurement */ import { StyledSegment } from './text.js'; import { TextElementLike } from './text-types.js'; export declare function getDir(string: string): "rtl" | "ltr"; /** * Base direction of a text ELEMENT, from its (possibly rich-text) source. * * `getDir` classifies a plain string; this is the rule for applying it to an * element, and it has to be shared. Every renderer that lays an element out — * the canvas, and the pdf/svg/html exporters — must agree on which way a given * element reads, and `getDir` is a RATIO over the characters, so whether the * markup was stripped first genuinely changes the answer. The rule was written * out by hand at each site that had one — and svg-export and html-export had * none at all, which is why they exported right-to-left designs with a * left-to-right base. */ export declare function getElementDir(text: string): "rtl" | "ltr"; export declare function normalizeStrokeLineJoin(join?: string): 'round' | 'miter' | 'bevel'; /** * Generate a curve path for text */ export declare function getCurvePath(width: number, height: number, power: number, absLineHeight: number): string; /** * Create SVG gradient definition */ export declare function createSVGGradientDef(gradientColor: string, id: string, /** * Span the gradient across this box in USER SPACE instead of each referencing * element's own bounding box. Curved text is drawn as one `` PER GLYPH * (see curveTextToSvg), and the default objectBoundingBox units would then * resolve against a single letter — giving every letter its own full colour * ramp instead of one ramp across the run. Pass the curve's bounds — the LINE * BOX render-tag reports, not the tight ink — in the coordinate space the * glyphs are drawn in. */ box?: { x: number; y: number; width: number; height: number; }): string; /** * A 2D context to measure text with: OffscreenCanvas first, so a worker * measures too, else a canvas element through the platform seam. The one * resolution order every measurer in the workspace takes. */ export declare function createMeasureCtx(): CanvasRenderingContext2D; export declare function getMeasureCtx(): CanvasRenderingContext2D; /** * Style declarations (camelCase keys, ready for a style object or kebab * conversion) that clip height-bound text with a trailing ellipsis — shared * by the canvas markup (getHtml) and html-export's text node so the two * cannot drift. Returns null when clipping doesn't apply: no height, or * curved text (a curve has no "last visible line"). * * Why this is not a "simple CSS" solution: * - `text-overflow: ellipsis` works ONLY for single-line truncation and does * not produce a multi-line "… on the last visible line" behavior. * - There is no fully standardized, cross-browser multiline ellipsis CSS * today. The practical CSS-only approach is `-webkit-line-clamp` * (Chromium/Safari; Firefox support varies by version/flags). * - The alternative is JS: measure laid-out text and truncate manually, but * that is heavier, font-metrics dependent, and can easily diverge from * canvas text layout. */ export declare function getEllipsisClampStyles(el: { height?: number; fontSize: number; lineHeight?: number | string; curveEnabled?: boolean; }): Record | null; /** * Whether this environment can measure text at all — ask before laying text * out, don't find out by catching. * * Plain Node (and jsdom without a canvas backend) cannot, and for curved text * that is a SUPPORTED mode, not a failure: the exporter emits the legacy * `` instead (svg-export tests/node-env.test.ts). Driving that branch * off a thrown error made an expected outcome look like a crash — a stack * trace on every CI run — and made genuine failures indistinguishable from it. * * Mirrors render-tag's own `createFallbackMeasureCtx`: OffscreenCanvas first, * then a document canvas. jsdom is why the context is null-CHECKED rather than * assumed — there `document` exists and `createElement('canvas')` succeeds, but * `getContext('2d')` returns null without the native `canvas` package, and * render-tag then threw `Cannot set properties of null` deeper in. * * Not cached: render-tag allocates the same probe per call anyway, and a cached * answer would outlive the environment a test is simulating. */ export declare function canMeasureText(): boolean; /** Measure `text` with a CSS `font` shorthand on the shared canvas. */ export declare function measureCanvasText(font: string, text: string): TextMetrics; /** * Calculate the dimensions of curved text using Konva TextPath. * For multi-font text, adjusts letter spacing so Konva's single-font * measurement matches the actual multi-font advance width. */ export declare function getCurveTextDimensions(element: TextElementLike, precomputed?: { normalizedText: string; segments: StyledSegment[]; }): { width: number; height: number; }; export declare function getCurveTextHeight(element: TextElementLike): number; /** * The ELEMENT box a curved text exports into: its stored height when measured, * else the curve's own ink dimensions (the unmeasured-height contract in * ./text-types — a zero box collapses the SVG viewport and background rect * sized off it). Pass `precomputed` when the caller already normalized and * segmented the text, which every renderer does — otherwise measuring * re-parses and re-measures the whole string. * * Note this is the exporters' rule, not the canvas's: the canvas (and `getHtml` * below) always lays curves out in `getCurveTextDimensions`, ignoring the * stored height. Converging the two would move every curved-text snapshot, so * it is its own change. * * MEASURING THROWS without a canvas backend (Konva): the unmeasured branch is * unusable in plain Node. A renderer that must survive there takes * `resolveCurveBoxHeightOrOneLine` below. The throw is not swallowed here on * purpose: a renderer that expects a DOM should hear about it rather than * silently export mis-sized curves. */ export declare function resolveCurveBoxHeight(element: TextElementLike, precomputed?: { normalizedText: string; segments: StyledSegment[]; }): number; /** * `resolveCurveBoxHeight` for the exporters, which run in plain Node: an * unmeasured curve that cannot be measured there is one line tall. */ export declare function resolveCurveBoxHeightOrOneLine(element: TextElementLike, precomputed?: { normalizedText: string; segments: StyledSegment[]; }): number; /** * The shared curve-text layout preamble: parse the (already normalized) rich * text into styled segments and derive what every curve renderer needs — the * effective curve font size (largest segment size wins) and the tspan * defaults. Shared by getHtml's curve branch and the svg/html exporters so * the canvas and the converters can't drift on these rules; each caller * builds its own path (their width/height inputs deliberately differ) and * tspans from the result. */ export declare function getCurveSegmentLayout(opts: { normalizedText: string; fontSize: number; fontFamily: string; fontWeight?: string; fontStyle?: string; fill?: string; }): { segments: StyledSegment[]; curveFontSize: number; segDefaults: { fontWeight: string; fontStyle: string; fill: string; fontFamily: string; fontSize: number; }; }; export declare function segmentsToTspans(segments: StyledSegment[], defaults: { fontWeight: string; fontStyle: string; fill: string; fontFamily: string; fontSize: number; }, options?: { omitColors?: boolean; }): string; export type TextOverflow = 'resize' | 'ellipsis' | 'change-font-size'; export declare const DEFAULT_EXPORT_TEXT_OVERFLOW: TextOverflow; export interface GetHtmlOptions { fontFamily?: string; color?: string; forEditor?: boolean; textOverflow?: TextOverflow; text?: string; textIsNormalized?: boolean; } /** * Make per-span solid colors win over a gradient fill/stroke. The gradient * technique puts `-webkit-text-fill-color: transparent` on the wrapper, and * that property is INHERITED — so a descendant carrying only `color` still * paints transparent and shows the clipped gradient instead of its color. * Marking color-carrying descendants with `-webkit-text-fill-color: * currentColor` restores the CSS-default "fill = color" for exactly those * runs (Chrome and render-tag agree). Descendants with their own * background-image (a per-span gradient) are left alone — they manage their * own clip/fill styles. */ export declare function markInlineColorsForClip(html: string): string; export declare function getQuillEditorHtml(element: TextElementLike, fontFamilyOverride?: string): string; /** * The trailing letter-space CSS hangs off the end of every line, in px. * * `letter-spacing` applies after EVERY character, the last one included, so a * line's advance is `ink + n·ls` — and aligning THAT is what pushes centered * text `ls/2` off center and leaves an `ls` gap on right-aligned text. Add this * to the width a renderer lays out in and every alignment corrects at once: the * padded line inside `width + overhang` sits where the trimmed line would * inside `width`. * * Only where a width is actually resolved — `getHtml`'s div, edit mode's box, * html-export's markup, the viewport pdf-export passes for markup carrying no * width of its own. On a render-tag viewport whose html already declares a * width it does nothing: the explicit width wins, and `drawLayout` ignores its * `width` entirely when handed a ctx. * * Approximate for a line that ENDS in a span with its own font size, and it * over-corrects RTL in Gecko, which puts the space on the trailing side where * Blink and WebKit put it on the physical right. Returning 0 restores the * previous rendering everywhere. */ export declare function letterSpacingOverhang(letterSpacing: number, fontSize: number): number; /** * The width a renderer should LAY OUT a text element in: its box plus the * trailing letter-space, floored at 1. * * The floor is why this is a function. `letterSpacing` reaches -0.5em, so a * narrow box at a large font size goes negative (`width: 10, fontSize: 100` * gives -40) and render-tag's `layout()` throws on it. */ export declare function textLayoutWidth(width: number, letterSpacing: number, fontSize: number): number; /** * Generate HTML string for text element rendering * Handles regular text, curved text (SVG), gradients, and strokes */ export declare function getHtml(element: TextElementLike, { fontFamily, color, forEditor, textOverflow, text, textIsNormalized, }?: GetHtmlOptions): string; /** * Resolve string lineHeight to number. * Uses only the primary fontFamily; for multi-font rich text, strut * propagation in normalizeRichTextHtml() compensates for single-child * paragraphs, but multi-span lines with different font metrics may * show subtle vertical spacing variance. */ export declare function resolveLineHeight({ fontFamily, fontSize, lineHeight, }: { fontFamily: string; fontSize: number; lineHeight: number | string; }): number;