import { FlowBlock, Layout, Measure, HeaderFooterLayout } from '../../contracts/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; }; 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[]; }; export declare const measureCache: MeasureCache; /** * Performs incremental layout of document blocks with header/footer support. * * This function orchestrates the complete layout pipeline including: * - Dirty region detection and selective cache invalidation * - Block measurement with caching * - Header/footer pre-layout to prevent body content overlap * - Document pagination with header/footer height awareness * - Page number token resolution with convergence iteration * - Final header/footer layout with section-aware numbering * * The function supports two modes for header/footer specification: * 1. **Variant-based** (headerBlocks/footerBlocks): Headers/footers organized by variant type * ('default', 'first', 'even', 'odd'). Used for single-section documents or when all * sections share the same header/footer variants. * 2. **Relationship ID-based** (headerBlocksByRId/footerBlocksByRId): Headers/footers organized * by relationship ID. Used for multi-section documents where each section may have unique * headers/footers referenced by their relationship IDs. * * Both modes can coexist - the function will extract header/footer heights from both sources * to ensure body content doesn't overlap with header/footer content. * * @param previousBlocks - Previous version of flow blocks (used for dirty region detection) * @param _previousLayout - Previous layout result (currently unused, reserved for future optimization) * @param nextBlocks - Current version of flow blocks to layout * @param options - Layout options including page size, margins, columns, and section metadata * @param measureBlock - Async function to measure a block's dimensions given constraints * @param headerFooter - Optional header/footer configuration with two modes: * - headerBlocks/footerBlocks: Variant-based headers/footers organized by type * ('default', 'first', 'even', 'odd'). Use this for simple documents with consistent * headers/footers across all sections. * - headerBlocksByRId/footerBlocksByRId: Relationship ID-based headers/footers organized * by unique relationship ID (Map). Use this for complex multi-section * documents where each section references specific headers/footers by their relationship IDs. * - constraints: Header/footer layout constraints (width, height) * - measure: Optional custom measurement function for header/footer blocks * @returns Layout result containing: * - layout: Final paginated document layout with page breaks and positioning * - measures: Measurements for all blocks (parallel to nextBlocks array) * - dirty: Dirty region information indicating which blocks changed * - headers: Optional array of header layout results (one per variant type) * - footers: Optional array of footer layout results (one per variant type) * @throws Error if measurement constraints are invalid (non-positive width or height) * * @example * ```typescript * // Single-section document with variant-based headers * const result = await incrementalLayout( * previousBlocks, * previousLayout, * nextBlocks, * { pageSize: { w: 612, h: 792 }, margins: { top: 72, right: 72, bottom: 72, left: 72 } }, * measureBlock, * { * headerBlocks: { * default: [headerBlock1, headerBlock2], * first: [firstPageHeaderBlock] * }, * constraints: { width: 468, height: 72 } * } * ); * ``` * * @example * ```typescript * // Multi-section document with relationship ID-based headers * const headersByRId = new Map([ * ['rId1', [section1HeaderBlock]], * ['rId2', [section2HeaderBlock]] * ]); * const result = await incrementalLayout( * previousBlocks, * previousLayout, * nextBlocks, * { pageSize: { w: 612, h: 792 }, sectionMetadata: [...] }, * measureBlock, * { * headerBlocksByRId: headersByRId, * constraints: { width: 468, height: 72 } * } * ); * ``` */ 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): 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; /** * 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