/**
* 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;