import { BodyElement, Chart, Comment, DocumentInfo, Numbering, Section, SectionProperties, ShapeFill, StyleSheet } from '../core/document-model/index.js'; import { FontRegistry } from '../core/font/index.js'; import { SubstituteKey } from '../core/fonts/index.js'; import { Hyphenator } from '../core/hyphenation/index.js'; import { Pt, ResourceStore } from '../core/ir/index.js'; import { LaidOutDocument, PageItem } from './page-doc.js'; import { AttachedFile } from '../pdf/embedded-file.js'; import { SignaturePlaceholder } from '../pdf/signature.js'; import { PdfEncryptOptions } from '../pdf/encryption.js'; import { StructTreeBuilder } from '../pdf/struct-tree.js'; /** * PDF/A conformance string: part 1 (ISO 19005-1, PDF 1.4) / 2 (ISO 19005-2) / 3 * (ISO 19005-3, both PDF 1.7); conformance level a (tagged) / b (visual) / * u (Unicode — only 2/3). */ export type PdfALevel = 'PDF/A-1b' | 'PDF/A-1a' | 'PDF/A-2b' | 'PDF/A-2u' | 'PDF/A-2a' | 'PDF/A-3b' | 'PDF/A-3u' | 'PDF/A-3a'; /** A {@link PdfALevel} decomposed into the part, level, and the apparatus they imply. */ export interface PdfAProfile { readonly part: 1 | 2 | 3; readonly level: 'a' | 'b' | 'u'; /** Whether the level mandates a tagged PDF (level `a`). */ readonly tagged: boolean; /** PDF version the part pins: part 1 → 1.4, else 1.7. */ readonly version: '1.4' | '1.7'; } /** * E-PARITY: renderer-compatibility profile for the line-height model. * `'ream'` (default) — Ream's flat 1.2× leading; byte-identical to before. * `'word'` — leading from the font's OS/2 usWin metrics (the GDI cell box). * `'libreoffice'` — leading from hhea (or OS/2 typo when USE_TYPO_METRICS is set). * * Opt-in: a profile emulates that renderer's vertical rhythm for closer visual * parity. It never changes default (`'ream'`) output. */ export type LayoutProfile = 'ream' | 'word' | 'libreoffice'; /** * The full option set the layout engine and PDF emitter consume: the resolved * font registry, the style/numbering tables, the section model and page * geometry, the after-body apparatus (footnotes, endnotes, comments), and every * PDF-output toggle (PDF/A, tagged, PDF/UA, encryption, attachments, * signatures). Only `registry` and `styles` are required; everything else falls * back to a sensible default. */ export interface StyledRenderOptions { /** The resolved font set every run draws with (the guaranteed fallback registry). */ readonly registry: FontRegistry; /** Renderer-compatibility profile for the line-height model (default `'ream'`). */ readonly layoutProfile?: LayoutProfile; /** * Per-run font resolution: when supplied, each text run picks the registry of * its declared family (sans→arimo / serif→tinos / mono→cousine via the run's * `w:ascii`) instead of always using `registry`. Absent ⇒ single-family (every * run uses `registry`), byte-identical to before. `registry` remains the * guaranteed fallback for math/chart/default glyphs and any missing family. */ readonly registriesByFamily?: ReadonlyMap; /** * The document's OWN embedded fonts (`word/fonts/*.odttf`, de-obfuscated), * keyed by normalized font name. A run whose `w:ascii` matches one renders with * the real font — glyph-exact, no substitution. Highest priority. */ readonly embeddedFonts?: ReadonlyMap; /** §18.8 / ECMA-376 style table the cascade resolves against. */ readonly styles: StyleSheet; /** §17.9 numbering definitions; applied to list paragraphs before layout. */ readonly numbering?: Numbering; /** * Single-section legacy entry-point. If `sections` is set it takes precedence * and `section` is ignored. */ readonly section?: SectionProperties; /** * ECMA-376 §17.6 — ordered list of sections. Each section's `endIndex` is the * exclusive bound into the body array (section N covers * `body[sections[N-1].endIndex..sections[N].endIndex)`). */ readonly sections?: ReadonlyArray
; /** * §17.6.5 — the pitch of the document grid the text is laid on. Set by the * layout itself, per section (see {@link SectionRenderCtx.options}); callers * state it on the section, not here. */ readonly gridLinePitchPt?: Pt; /** Header/footer body content keyed by relationship id. */ readonly headersFooters?: ReadonlyMap>; /** * §17.11 notes content by id. Footnotes render in a reserved band at the * bottom of the referencing page; endnotes flow after the body. */ readonly footnotes?: ReadonlyMap>; readonly endnotes?: ReadonlyMap>; /** * §17.13.4 review comments by id; rendered as superscript markers in text and * a list after the body (after endnotes), each with its author/date. */ readonly comments?: ReadonlyMap; /** * CM2b — also emit each comment as a native PDF `/Text` (sticky-note) * annotation at its marker. Opt-in and interactive-only: suppressed under * PDF/A and tagged output (where it would need annotation/appearance * conformance), since the clickable marker + Comments section already carry * the content there. */ readonly commentAnnotations?: boolean; /** Content-addressed binary store; image nodes reference it by {@link ResourceId}. */ readonly resources?: ResourceStore; /** * Parsed charts keyed by relationship id (`ChartBlock.chartRelId`). Supplied by * the converter, which resolves the chart parts from the package. */ readonly charts?: ReadonlyMap; readonly pageWidth?: number; readonly pageHeight?: number; readonly marginLeft?: number; readonly marginRight?: number; readonly marginTop?: number; readonly marginBottom?: number; /** * Optional Liang hyphenator. When set, each word token is split at allowed * hyphenation positions and offered to Knuth-Plass as potential break points * (with a small disincentive). Improves justified paragraph rags. */ readonly hyphenator?: Hyphenator; /** * Optional `/Info` dictionary metadata (ISO 32000-1 §14.3.3). Unset fields are * omitted; if any field is set a PDF `/Info` entry is emitted. */ readonly info?: DocumentInfo; /** * When set, emit a PDF/A-conformant file: an OutputIntent with an embedded * sRGB ICC profile, document XMP `/Metadata` (the pdfaid identifier), `/ID`, * and subset-tagged fonts with a `/CIDSet`. The profile picks the rest: * part 1 → PDF 1.4 + flattened image alpha (no transparency); * part 2/3 → PDF 1.7 + preserved transparency (image `/SMask` + page group); * part 3 → may carry embedded associated files (see `attachments`); * level a → tagged (logical structure); b → visual; u → b + Unicode mapping. */ readonly pdfA?: PdfALevel; /** * Emit a tagged PDF (ISO 32000-1 §14.8): a `/StructTreeRoot` describing reading * order, marked content (BDC/EMC + MCID) on body text, and `/Artifact` marking * of page decoration. Implied by `pdfA: 'PDF/A-1a'`. Independent of PDF/A * otherwise (a plain tagged PDF is useful on its own). */ readonly tagged?: boolean; /** * PDF/UA-1 (ISO 14289-1): implies tagged; the XMP carries `pdfuaid:part=1` and * the document always gets a title (AT announces it). Combines freely with * `pdfA` level-a profiles. */ readonly pdfUA?: boolean; /** * Document natural language (BCP 47, e.g. `"en-US"`, `"ru-RU"`) for the * tagged-PDF catalog `/Lang` (§14.9.2). Defaults to `"en-US"`. The docx * converter fills this from the document's default `w:lang`. */ readonly language?: string; /** * ECMA-376 §17.15.1.35 `w:doNotExpandShiftReturn` — when set, a justified * line that ends at a soft line break (`w:br`) is drawn at its natural width * instead of being stretched out to the measure. */ readonly doNotExpandShiftReturn?: boolean; /** * ECMA-376 §17.2.1 `w:background` — the colour every page is painted before * anything else is drawn on it. Absent ⇒ the paper's own white. */ readonly pageBackgroundColorHex?: string; /** §17.2.1 — the background's gradient or picture, when it has one. */ readonly pageBackgroundFill?: ShapeFill; /** * ECMA-376 §17.15.1.38 `w:gutterAtTop` — the binding space belongs to the * TOP margin, not the left. */ readonly gutterAtTop?: boolean; /** * §7.6 PDF encryption (AES-256, R6). Only honoured on the ASYNC conversion * path (WebCrypto); mutually exclusive with `pdfA` (ISO 19005 forbids * `/Encrypt`) and with signatures (v1). */ readonly encrypt?: PdfEncryptOptions; /** * Files to embed as associated files (catalog `/AF` + `/Names` * `/EmbeddedFiles`). Only emitted for plain PDF and PDF/A-3 (PDF/A-1/2 forbid * arbitrary embedded files); ignored for PDF/A-1/2. The docx/xlsx converters * can embed the source document automatically via `embedSource`. */ readonly attachments?: ReadonlyArray; /** * Emit an (invisible) signature field + signature dictionary with placeholder * `/ByteRange` and `/Contents` (ISO 32000 §12.8). The result is an UNSIGNED * PDF; pass it to `signPdf()` to fill the placeholder with a real PKCS#7 * signature. */ readonly signaturePlaceholder?: SignaturePlaceholder; } export type { DocumentInfo } from '../core/document-model/index.js'; /** A4 page width in points (the page-geometry fallback). */ export declare const A4_WIDTH = 595; /** A4 page height in points (the page-geometry fallback). */ export declare const A4_HEIGHT = 842; /** * How far a space may be squeezed below its natural width — the shrink the * line breaker offers when it weighs a line, and therefore the shrink the * emitter owes it back when it draws one. */ export declare const GLUE_SHRINK_RATIO = 0.3; /** * §17.13.6.2 — a bookmark's GoTo destination: the page (0-based) and the y-up * top of the anchoring paragraph's first line. */ export interface BookmarkPosition { readonly pageIdx: number; readonly yTopPt: number; } /** A comment's `/Text` annotation payload: the author and the flattened body (CM2b). */ export interface CommentNote { readonly author?: string; readonly contents: string; } /** * PDF-only companion the same layout pass produces (oop-design A13): the * logical-structure tree, per-section geometry (the emit fallback page), and the * parsed PDF/A profile. Consumed only by `emitStyledPdf`; the SVG writer never * sees it. */ export interface PdfLayoutAux { readonly structBuilder: StructTreeBuilder | undefined; readonly sectionCtxs: ReadonlyArray; readonly pdfaProfile: PdfAProfile | undefined; readonly tagged: boolean; readonly bookmarks: ReadonlyMap; /** * CM2b — comment marker anchor (`comment-${n}`) → the note the emitter attaches * as a `/Text` annotation. Present only when `commentAnnotations` was requested. */ readonly commentNotes?: ReadonlyMap; } /** * What {@link layoutStyledDocument} actually returns: the PageDoc with the PDF * companion riding on `pdf`. Assignable to the narrow `LaidOutDocument`, so * PageDoc-only consumers (`writeSvg`) take it as-is. */ export interface LaidOutPdfDocument extends LaidOutDocument { readonly pdf: PdfLayoutAux; } /** * Layout phase (the FlowDoc→PageDoc transform of ir-design §7): body → * positioned pages (PageItems), font/image resources, logical structure. Drives * the whole pass — numbering and note numbering, per-section geometry, per-block * layout, the footnote/endnote/comment tails, then pagination — and returns the * {@link LaidOutPdfDocument} (the PageDoc plus its PDF companion). * * @param body The document body the section model partitions. * @param options The resolved fonts, styles, section model, and PDF-output toggles. * @returns The positioned pages plus the PDF-only {@link PdfLayoutAux} companion. */ export declare function layoutStyledDocument(body: ReadonlyArray, options: StyledRenderOptions): LaidOutPdfDocument; /** * The resolved page box + header/footer offsets, in points. Priority for page * geometry: * 1. explicit value in {@link StyledRenderOptions} (test/library caller override) * 2. value from section properties (`sectPr/pgSz/pgMar` from the docx) * 3. A4 + 1-inch margins fallback */ export interface PageDimensions { readonly pageWidth: number; readonly pageHeight: number; readonly marginLeft: number; readonly marginRight: number; readonly marginTop: number; readonly marginBottom: number; readonly headerOffsetPt: number; readonly footerOffsetPt: number; } /** * Per-section render context: the section's exclusive body `endIndex`, its * resolved {@link PageDimensions} (flattened), the derived content box, the * column geometry, the pre-laid header/footer bands, and the title-page / * even-and-odd header toggles. Built once per section and threaded through both * layout and pagination. */ export interface SectionRenderCtx { readonly endIndex: number; readonly properties: SectionProperties; readonly pageWidth: number; readonly pageHeight: number; readonly marginLeft: number; readonly marginTop: number; readonly marginBottom: number; readonly contentWidth: number; readonly pageContentHeight: number; /** * §17.6.4 multi-column sections: per-column x-offset (from `marginLeft`) and * width. Absent for single-column sections. Body blocks are laid out at the * FIRST column's width (explicit unequal widths degrade to flowing without * re-wrap); headers/footers and footnotes keep the full content width. */ readonly columns?: ReadonlyArray<{ readonly xOffsetPt: number; readonly widthPt: number; }>; readonly headerSet: HeaderFooterSet; readonly footerSet: HeaderFooterSet; readonly titlePg: boolean; readonly evenAndOddHeaders: boolean; /** * §17.6.22 — the section begins on the page already in hand, at the point the * one before it stopped, rather than on a fresh one. */ readonly continuous: boolean; /** * The render options this section's content is laid out with: `options` plus * whatever the section itself decides for its text — today only §17.6.5's * document grid, which is a property of the SECTION and is needed by every * paragraph in it. Absent on a bare context built outside a layout run. */ readonly options?: StyledRenderOptions; } interface HfBandEntry { readonly commands: Array; readonly renderDynamic?: (pageNumber: number, totalPages: number) => Array; /** Laid-out height of the band, so the body can be kept clear of it. */ readonly heightPt?: number; } interface HeaderFooterSet { readonly default: HfBandEntry; readonly first: HfBandEntry; readonly even: HfBandEntry; }