import { ColumnLayout, Page, PageMargins } from '../../contracts/src/index.js';
export type NormalizedColumns = ColumnLayout & {
    width: number;
};
export type ConstraintBoundary = {
    y: number;
    columns: ColumnLayout;
};
export type PageState = {
    page: Page;
    cursorY: number;
    columnIndex: number;
    topMargin: number;
    contentBottom: number;
    constraintBoundaries: ConstraintBoundary[];
    activeConstraintIndex: number;
    trailingSpacing: number;
    lastParagraphStyleId?: string;
    lastParagraphContextualSpacing: boolean;
    /** Border hash of the last paragraph for between-border group detection. */
    lastParagraphBorderHash?: string;
    /** Tracks the maximum cursorY reached across all columns on this page.
     *  Used when starting a mid-page region so the new section begins below
     *  all column content, not just the current column's cursor. */
    maxCursorY: number;
    /**
     * SD-3049: Page-level footnote reserve already baked into `contentBottom`
     * via `getActiveBottomMargin`. The block-aware break decision compares
     * `footnoteDemandThisPage` against this; only the excess shrinks the body.
     */
    pageFootnoteReserve: number;
    /**
     * SD-3049: Accumulated measured body height of footnote refs anchored on
     * fragments already committed to this page (and column-wide). Used by the
     * paragraph break decision so the body packs tight to footnote demand
     * instead of relying solely on the post-hoc page-level reserve.
     */
    footnoteDemandThisPage: number;
    /**
     * SD-2656: Number of distinct footnote refs anchored on this page so far.
     * Drives the slicer's band-overhead computation (separator + per-extra-ref
     * gap + safety margin), which must match the planner's reserve formula.
     */
    footnoteRefsThisPage: number;
    /**
     * SD-2656: ordered list of footnote anchors committed to this page (by
     * document/PM order). The body slicer pushes a new entry when it accepts a
     * candidate line that introduces a new anchor. The list drives the ordered-
     * cluster demand formula:
     *   demand = sum(fullHeight of cluster[0..N-1]) + firstLineHeight(cluster[N-1])
     * i.e. all anchors except the last must fit fully; only the last may split.
     * Identified by refId so callers can dedupe and walk in document order.
     */
    footnoteAnchorsThisPage: Array<{
        pmPos: number;
        refId: string;
        fullHeight: number;
        firstLineHeight: number;
    }>;
};
export type PaginatorOptions = {
    margins: {
        left: number;
        right: number;
    };
    getActiveTopMargin(): number;
    getActiveBottomMargin(): number;
    getActiveHeaderDistance(): number;
    getActiveFooterDistance(): number;
    getActivePageSize(): {
        w: number;
        h: number;
    };
    getDefaultPageSize(): {
        w: number;
        h: number;
    };
    getActiveColumns(): ColumnLayout;
    getCurrentColumns(): NormalizedColumns;
    createPage(number: number, pageMargins: PageMargins, pageSizeOverride?: {
        w: number;
        h: number;
    }): Page;
    onNewPage?: (state: PageState) => void;
    /**
     * SD-3049: per-page footnote reserve (the value already added to
     * `getActiveBottomMargin`). Returned by index for the page about to be
     * created. Defaults to 0 when not provided.
     */
    getFootnoteReserveForPage?: (pageIndex: number) => number;
};
export declare function createPaginator(opts: PaginatorOptions): {
    readonly pages: Page[];
    readonly states: PageState[];
    readonly startNewPage: () => PageState;
    readonly ensurePage: () => PageState;
    readonly advanceColumn: (state: PageState) => PageState;
    readonly columnX: (columnIndex: number) => number;
    readonly getActiveColumnsForState: (state: PageState) => ColumnLayout;
    readonly getPageByNumber: (pageNumber: number) => PageState;
    readonly pruneTrailingEmptyPages: () => void;
};
//# sourceMappingURL=paginator.d.ts.map