import type * as Template from '../../serial/serial-types'; import { TextFrame } from '../../controllers/sprites/TextFrame'; import { DecomposedTransform } from '../../util/layout'; import { Box, TextBounding } from '../../serial/builders'; import { ConvertSettings } from '../types'; import { AssetCollector } from '../assets'; import { EffectiveTextStyle } from './style'; import { TextRun } from './split'; export declare function loadTextLayout(): Promise; /** Build one text element from runs (uniform runs collapse to plaintext). */ export declare function textElementFromRuns(id: string, runs: TextRun[], box: Box, align: number, justify: boolean, verticalAlign: number, lineHeightPercent: number, transform: DecomposedTransform, bounding?: TextBounding): Template.Elements.Text; export declare const HANGING_BASELINE_FRACTION = 0.8; /** Canvas `fontBoundingBoxAscent` for a style at a given size (default the style's * own) — the metric InDesign's Ascent first-baseline and core both read. 0 if no * canvas / unmeasurable. */ export declare function fontAscent(core: typeof import('@bluepic/core/text'), style: EffectiveTextStyle, fontSize?: number): number; /** Canvas `fontBoundingBoxDescent` for a style — the descent 'font' bounding places * below the baseline and adds to the line advance. Subtracted out of the emitted * lineHeight so the physical leading still equals InDesign's. 0 if no canvas. */ export declare function fontDescent(core: typeof import('@bluepic/core/text'), style: EffectiveTextStyle, fontSize?: number): number; export declare const BAUCHBINDE_MIN_WEIGHT_RATIO = 0.5; /** * Build the "Bauchbinde" highlight bar for a text element whose base run carries a * thick offset underline — an iterated rectangle bound to `.lines[i]`, one bar * per rendered line, behind the text. Returns null unless the run is underlined * with a bar-thick stroke and the font ascent is measurable (needs a canvas). * * `scale` is the merged-fit factor (split path) so the bar's weight/offset and the * measured ascent all track the shrunken text; the emitted rectangle centres on the * baseline+offset (baseline = line top + ascent) with height = the underline weight. */ export declare function lineBackgroundBar(core: typeof import('@bluepic/core/text') | null, targetTextId: string, style: EffectiveTextStyle, scale: number, pad: number, leftAnchorX: number | null): Template.Elements.Rectangle | null; /** * Build the paragraph-shading block for a text element whose paragraph carries * `ParagraphShadingOn` — a line-bound background rectangle spanning the frame width * behind the whole element (see makeParagraphShadingRectangle). Returns null when the * paragraph has no shading. `box` is the frame box (the shading's horizontal span). */ export declare function paragraphShadingBlock(targetTextId: string, style: EffectiveTextStyle, box: Box): Template.Elements.Rectangle | null; /** * Build the paragraph-rule line(s) (RuleAbove/RuleBelow) for a text element — column-width * rectangles positioned relative to the first/last line's baseline. Needs the measured font * ascent (baseline = line top + ascent), so no-op without a canvas. `scale` tracks the * merged-fit shrink so weight/offset/ascent all follow the shrunken text. */ export declare function paragraphRuleBlocks(core: typeof import('@bluepic/core/text') | null, targetTextId: string, style: EffectiveTextStyle, scale: number, box: Box): Template.Elements.Rectangle[]; /** A layout probe over the merged frame: `(lineHeight%, bounding, blockTopY, maxHeight)`. * Vertical justify uses it to read the natural (huge maxHeight = un-shrunk) block. */ export type ProbeLayout = (lineHeight: number, bounding: TextBounding, y: number, maxHeight: number) => import('@bluepic/core/text').TextLayoutResult; /** actualBoundingBox ascent/descent of ONE rendered line's text at the base style — the * real ink extent (content-dependent), used to anchor vertical justify on InDesign's grid. */ export declare function lineActualMetrics(core: typeof import('@bluepic/core/text'), style: EffectiveTextStyle, text: string): { ascent: number; descent: number; }; /** Binary-search the lineHeight % whose natural (un-shrunk) block height equals `target` * — the widest line spread that still fits a frame without core shrinking the font. */ export declare function fitLineHeightForBlockHeight(probe: ProbeLayout, bounding: TextBounding, y: number, target: number): number; /** * Reproduce InDesign `VerticalJustification="JustifyAlign"` as ONE Bluepic text * element: the lines are spread to a uniform baseline gap that fills the frame, with * the first baseline on InDesign's grid (frameTop + the first line's actual ascent). * * Core has no vertical-justify mode and ALWAYS shrinks text that would overflow its * box, so we widen the line advance (lineHeight) to the justified gap and place the * box ourselves. Two knobs, from {@link ConvertSettings}: * - bounding `'font'` (default; alphabetic baseline at exactly fontBoundingBoxAscent — * no approximation constant, consistent with every other text element), `'fontSize'` * (advance = fontSize×lineHeight, first baseline via the 0.8·ascent hanging guess) or * `'actual-outer'` (block auto-anchors on the first line's real ink). Only the first * baseline's box-top offset differs; the advance is measured, so gaps are identical. * - fit `'grow'` (box a few px TALLER than the frame so the last line's descenders * overflow it like InDesign and the font is never shrunk — gap exact) or `'contain'` * (box = frame height; descenders kept inside, gap ~3–5% tighter). * * The baseline advance is linear in lineHeight with slope fontSize/100 in BOTH bounding * modes (verified), so one calibration probe fixes the constant offset and lets us solve * lineHeight for a target gap. Returns null (caller falls through to the normal path) * for a single line, a frame too short to spread into, or unusable measurements. */ export declare function buildVerticalJustifyElement(id: string, runs: TextRun[], box: Box, align: number, justify: boolean, transform: DecomposedTransform, core: typeof import('@bluepic/core/text'), base: EffectiveTextStyle, settings: ConvertSettings, probe: ProbeLayout): Template.Elements.Text | null; /** * Convert a text frame's story into one or more Bluepic text elements. * * Bluepic connects text elements to input fields, so a frame that stacks * several statements can become one element per statement. How eagerly that * happens is controlled by `settings.textSplittingHeuristic` — see * {@link splitRunsIntoChunks} for the split rules and {@link * ConvertIDML2SerialOptions.textSplittingHeuristic} for the modes. * * Geometry: the ORIGINAL merged frame is laid out ONCE via @bluepic/core/text * (the renderer's own fitting + line stacking, so positions are exactly what * bluepic-core will render). Each chunk's box then starts at its first line's * y, ends where the next emitted chunk starts (the last one at the frame * bottom), keeps the full frame width, and is top-anchored (verticalAlign 0) * — so every line renders exactly where the merged frame would have put it, * and together the boxes tile the frame's vertical extent. Line-x math is * width-invariant (offset = (max - line) * align), so alignment is preserved * per chunk. All chunks keep the MERGED element's lineHeight ratio — IDML * per-paragraph leading is (as before) not preserved — which keeps each * chunk's own rendering self-consistent with the merged layout above. * * The normal path emits `bounding: 'font'` (the system default): line advances * are (fontBoundingBox height) × lineHeight and the first line's alphabetic * baseline lands at frameTop + fontAscent = InDesign's "Ascent", so no box shift * is needed. 'font''s extra descent is subtracted out of the emitted lineHeight so * the physical leading still equals InDesign's. Without a canvas (fonts unmeasured) * the path falls back to the render-font-independent `'fontSize'`. * * When splitting is off, unnecessary (single statement) or unavailable (no * canvas), the result is a single element equal to the pre-split output — * except that U+2028 is now always normalized to '\n' (core only breaks * lines on '\n'; a raw U+2028 would render as a glyph, not a break). */ export declare function buildTextElements(frame: TextFrame, box: Box, singleElementTransform: DecomposedTransform, collector: AssetCollector, id: string, settings: ConvertSettings): Promise; /** * A text frame can also carry a fill/stroke (it's a graphic frame too). When it * does, emit a background rectangle under the text — mirroring idml2svg. The * cyan-filled "square" in 4-pages.idml is actually a filled, empty text frame. */ export declare function textFrameElement(frame: TextFrame, transform: DecomposedTransform, collector: AssetCollector, settings: ConvertSettings): Promise; //# sourceMappingURL=layout.d.ts.map