/** * Float planning (issue #49 §7 — Placement, revised model). * * In the revised model a resource is *incorporated by reference*: the first * time it is mentioned (an inline `:ref` or a `::resource` directive, whichever * comes first in reading order) the engine floats it to the first free slot * after that reference — the bottom of the referencing column, the top or * bottom of the next empty column, or a band of the next page. The author * never places it twice and the running text flows past the reference * uninterrupted. * * This module is the pure planning half: it resolves each resource's placement * and produces the ordered list of floats with the content-block index of their * first reference. The geometry (reserving page bands, positioning the float * blocks, deferring overflow to the next page) lives in the build pipeline, * which owns the VDT and measurement context. */ import type { ContentBlock } from '../parse/index.js'; import type { Dimension, FloatShrinkMode, ResolvedTextWrapConfig, Resource, ResourceType, ResourceFloatPosition, ResourceFloatSpan, ResourceRotation } from '../types.js'; import { floatColumnCount } from '../defaults/shared.js'; export { floatColumnCount }; /** Resolved placement for a resource: never `undefined` fields (but * `rotate`, absent for an upright resource). */ export interface ResolvedPlacement { position: ResourceFloatPosition; span: ResourceFloatSpan; rotate?: ResourceRotation; /** Fraction of the slot width the float takes (`1` = the whole width). */ widthFraction: number; align: 'left' | 'center' | 'right'; /** The caption in the side column (`placement.captionSide: true`). A * caption beside the picture inside its slot (`'inside'`, #1106) is not * this: the slot keeps its whole width (`widthFraction` 1) and the * resource layout narrows the picture (see `captionInsideShare`). */ captionSide: boolean; /** Adjacent columns a column float takes (1 = its own column). */ columns: number; /** How the float is scaled to the room of a slot (`placement.shrink`, * #626) and the smallest scale it takes; `'never'` for anything but an * upright picture. */ shrink: FloatShrinkMode; minScale: number; /** The float may head the page or column its citing line lands on * (`placement.citingPage`, #633): a `'top'` or `'auto'` float of a * column or page span, not turned. */ citingPage: boolean; } /** A planned float: the resource, its resolved placement, and the index of the * content block where it is first referenced (its anchor). `'auto'` takes * the first free slot after the reference (top or bottom); `'top'` / * `'bottom'` restrict the search to that kind of slot. */ export interface PlannedFloat { /** Where the flow stood when the float's first reference was reached: * the top of the citing block, on that page, when the float is * enqueued; for a float cited in running text, restamped with the page * and top of the citing line once that line is placed (the float takes * no slot before it). A `span: 'side'` float stacks no higher than this * on that page — beside the text that cites it. */ refPageIndex?: number; refY?: number; resourceId: string; firstBlockIdx: number; position: 'auto' | 'top' | 'bottom'; span: ResourceFloatSpan; /** Set turned a quarter turn on the page (always page-span; takes a * whole page). */ rotate?: ResourceRotation; /** Width fraction, alignment and side caption of the placement (see * `ResourcePlacement`). */ widthFraction?: number; align?: 'left' | 'center' | 'right'; captionSide?: boolean; /** A `span: 'column'` float across this many adjacent columns (absent * for one; see `ResourcePlacement.columns`). */ columns?: number; /** A picture scaled to the room of a slot rather than moved on * (`placement.shrink`, #626), never below `minScale`. Absent for * `'never'`. */ shrink?: 'page' | 'slot'; minScale?: number; /** Text wraps round the float (#627, `placement.wrap`): a one-column * float at the head or foot of its column, at this side of it; the * column's first or last lines run beside it. `widthFraction` and * `align` then say where it stands. Absent for a float that keeps its * band whole. */ wrap?: 'left' | 'right'; /** The wrap's own gap (`placement.wrapGap`, else `layout.wrap.gap`). */ wrapGap?: Dimension; /** The text runs round the picture's shape (`wrap: 'contour'`, #883). */ contour?: true; /** May head the page (page span) or column (column span) where its * citing line lands (`placement.citingPage`, #633): the build proposes * that slot after a pass and keeps it while the citing line stays * there. Absent for a float that takes the first free slot after its * reference. */ citingPage?: true; /** For the rest of a table split across pages: the first model row still * to place (the header rows are repeated above it). Absent (or `0`) for * a whole resource. */ startRow?: number; /** For the rest of a split table: where its previous slice went. The * rest never lands before it in reading order — on that page only a * later column's slot will do (a page-span rest waits for the next * page). */ notBefore?: { pageIndex: number; columnIndex: number; }; /** A floated `:::callout` (`placement: 'top' | 'bottom'`): the content * index of its opening fence. `resourceId` is then the synthetic * `callout:`; the build keeps the box's layouter by index. */ callout?: { startIdx: number; }; } /** The document default for `placement.shrink` (`layout.floatShrink`). */ export interface FloatShrinkDefault { mode: FloatShrinkMode; minScale: number; } /** Resolve a resource's placement: own `placement` → its type's * `defaultPlacement` → the built-in default (`auto` / `column`). A rotated * resource is always a page-span float; an inline (`here`) embed is never * rotated, and neither is any resource of a vertical flow (`noRotation`: * every resource there stands upright, in its own span). `shrink` falls * back to `shrinkDefault` (`layout.floatShrink`), and applies to upright * pictures only (bitmap, SVG, video poster). */ export declare function resolveResourcePlacement(resource: Resource, type: ResourceType | undefined, noRotation?: boolean, shrinkDefault?: FloatShrinkDefault, /** `layout.floatsAtCitingPage`: the default of `placement.citingPage`. */ citingPageDefault?: boolean): ResolvedPlacement; /** * Walk the parsed blocks in reading order and produce the floats to place, in * first-reference order. A resource is floated when its resolved position is * `'top'` or `'bottom'`; `'here'` resources are left for inline `::resource` * placement and are not returned here. Resources with an unknown id or type are * skipped (the warnings phase surfaces those). * * `incorporated` holds the resources the preceding content already took in * (the ids of `continuation.resourceNumbers`): a chapter laid out after the * one that first cited a figure only refers to it — the figure was placed * there, and is not placed again (EF-57). */ export declare function computeFloatPlan(blocks: ContentBlock[], resources: Resource[], resourceTypes: ResourceType[], incorporated?: ReadonlySet, /** A vertical flow: no resource is turned (see `resolveResourcePlacement`). * Either for the whole document or per content block (the block a * resource is first referred to in: a styled section may set its own * writing mode). */ noRotation?: boolean | ((blockIdx: number) => boolean), /** `layout.floatShrink`: the default of `placement.shrink`. */ shrinkDefault?: FloatShrinkDefault, /** `layout.wrap`: how a float that asks for text wrap is set (#627); * absent, no float wraps. */ wrapSettings?: ResolvedTextWrapConfig, /** `layout.floatsAtCitingPage` (#633). */ citingPageDefault?: boolean): PlannedFloat[]; /** The set of resource ids that float (so the build loop can skip their inline * `::resource` placement). Derived from a {@link computeFloatPlan} result, * plus the floating resources of `incorporated` (placed by the preceding * content: a `::resource` for one of them is just another reference). */ export declare function floatedResourceIds(plan: PlannedFloat[], incorporated?: ReadonlySet, resources?: Resource[], resourceTypes?: ResourceType[]): Set; //# sourceMappingURL=floatPlacement.d.ts.map