import { FlowBlock, Layout, Measure, HeaderFooterLayout, ParagraphBlock, ParagraphMeasure } from '../../contracts/src/index.js'; import { FontMeasureContext } from '../../../shared/font-system/src/index.js'; import { LayoutOptions, HeaderFooterConstraints } from '../../layout-engine/src/index.js'; import { computeDirtyRegions } from './diff.js'; import { MeasureCache } from './cache.js'; import { HeaderFooterBatch } from './layoutHeaderFooter.js'; export type HeaderFooterMeasureFn = (block: FlowBlock, constraints: { maxWidth: number; maxHeight: number; }) => Promise; export type HeaderFooterLayoutResult = { kind: 'header' | 'footer'; type: keyof HeaderFooterBatch; layout: HeaderFooterLayout; blocks: FlowBlock[]; measures: Measure[]; /** Effective layout width when table grid widths exceed section content width (SD-1837). */ effectiveWidth?: number; }; /** * SD-3432: the footnote reserve fixed point of a completed layout run, used to * warm-start the next run's convergence loop. The seed is ONLY a starting * vector — every run re-validates it through the full convergence machinery * (pass-1 relayout + plan stability + grow/tighten + widow + trials), so a * stale or wrong seed costs extra passes, never correctness. Captured only * when the run ended on an EXACT fixed point (plan === applied reserves), so * an unchanged document warm-validates in a single relayout. * * Guards carried with the vector (fontSignature / measurement constraints) * exist purely to discard pathological starting vectors after zoom or font * changes; they carry no document identity (no footnote ids, no content * hashes — see the SD-3418 post-mortem for why identity keys are forbidden). */ export type FootnoteReserveSeed = { reserves: number[]; separatorSpacingBefore: number | undefined; fontSignature: string; measurementWidth: number; measurementHeight: number; }; export type IncrementalLayoutResult = { layout: Layout; measures: Measure[]; dirty: ReturnType; headers?: HeaderFooterLayoutResult[]; footers?: HeaderFooterLayoutResult[]; /** * Extra blocks/measures that should be added to the painter's lookup table. * Used for rendering non-body fragments injected into the layout (e.g., footnotes). */ extraBlocks?: FlowBlock[]; extraMeasures?: Measure[]; /** * SD-3432: next-run warm-start seed for the footnote convergence loop. * Null when this run did not end on an exact footnote fixed point (or laid * out no footnotes) — the next run then starts cold. */ footnoteReserveSeed?: FootnoteReserveSeed | null; }; export declare const measureCache: MeasureCache; export declare function incrementalLayout(previousBlocks: FlowBlock[], _previousLayout: Layout | null, nextBlocks: FlowBlock[], options: LayoutOptions, measureBlock: (block: FlowBlock, constraints: { maxWidth: number; maxHeight: number; }) => Promise, headerFooter?: { headerBlocks?: HeaderFooterBatch; footerBlocks?: HeaderFooterBatch; headerBlocksByRId?: Map; footerBlocksByRId?: Map; constraints: HeaderFooterConstraints; measure?: HeaderFooterMeasureFn; }, previousMeasures?: Measure[] | null, fontRuntime?: { fontContext?: FontMeasureContext; previousFontSignature?: string; }, warmStart?: { footnoteReserveSeed?: FootnoteReserveSeed | null; }): Promise; /** * Normalizes a margin value, using a fallback for undefined or non-finite values. * Prevents NaN content sizes when margin properties are partially defined. * * @param value - The margin value to normalize (may be undefined) * @param fallback - The default margin value to use if value is invalid * @returns The normalized margin value (guaranteed to be finite) */ export declare const normalizeMargin: (value: number | undefined, fallback: number) => number; /** * Walks table blocks (including nested tables) and computes `contentMeasures` for any * `textboxShape` drawings found in table cells. Stores results directly on the block so * the painter can read them without a `DrawingFragment` (table-cell drawings have none). */ export declare function hydrateTableTextboxMeasures(blocks: FlowBlock[], remeasure: (block: ParagraphBlock, maxWidth: number) => ParagraphMeasure): void; /** * Resolves the maximum measurement constraints (width and height) needed for measuring blocks * across all sections in a document. * * This function scans the entire document (including all section breaks) to determine the * widest column configuration and tallest content area that will be encountered during layout. * The result is used for cache invalidation and backward-compatible comparison (see * `canReusePreviousMeasures`). Actual per-block measurement uses `computePerSectionConstraints`. * * Algorithm: * 1. Start with base content width/height from options.pageSize and options.margins * 2. Calculate base column width from options.columns (if multi-column) * 3. Scan all sectionBreak blocks to find maximum column width and content height * 4. For each section: compute content area, calculate column width, track maximum * 5. Return the widest column width and tallest content height found * * Column width calculation: * - Single column: contentWidth (no gap subtraction) * - Multi-column: (contentWidth - totalGap) / columnCount * - Total gap = gap * (columnCount - 1) * * @param options - Layout options containing default page size, margins, and columns * @param blocks - Optional array of flow blocks to scan for section breaks * If not provided, only base constraints from options are used * @returns Object containing: * - measurementWidth: Maximum column width in pixels (guaranteed positive) * - measurementHeight: Maximum content height in pixels (guaranteed positive) * * @throws Error if resolved constraints are non-positive (indicates invalid configuration) * * @example * ```typescript * // Document with two sections: single column and 2-column * const options = { * pageSize: { w: 612, h: 792 }, // Letter size * margins: { top: 72, right: 72, bottom: 72, left: 72 }, * columns: { count: 1, gap: 0 } * }; * const blocks = [ * // ... content blocks ... * { * kind: 'sectionBreak', * columns: { count: 2, gap: 48 }, * // ... other section properties ... * } * ]; * const constraints = resolveMeasurementConstraints(options, blocks); * // Returns: { measurementWidth: 468, measurementHeight: 648 } * // 468px = (612 - 72 - 72) width, single column (wider than 2-column: 234px) * // All blocks measured at 468px will fit in both sections * ``` */ export declare function resolveMeasurementConstraints(options: LayoutOptions, blocks?: FlowBlock[]): { measurementWidth: number; measurementHeight: number; }; //# sourceMappingURL=incrementalLayout.d.ts.map