docxodus 12.6.4 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +8 -5
  2. package/dist/canonical.d.ts.map +1 -1
  3. package/dist/canonical.js +7 -3
  4. package/dist/canonical.js.map +1 -1
  5. package/dist/conversion-options.d.ts +16 -0
  6. package/dist/conversion-options.d.ts.map +1 -0
  7. package/dist/conversion-options.js +31 -0
  8. package/dist/conversion-options.js.map +1 -0
  9. package/dist/core.d.ts +17 -9
  10. package/dist/core.d.ts.map +1 -1
  11. package/dist/core.js +23 -24
  12. package/dist/core.js.map +1 -1
  13. package/dist/docxodus.worker.js +2 -1073
  14. package/dist/docxodus.worker.js.map +7 -1
  15. package/dist/editor.bundle.js +35 -14296
  16. package/dist/editor.bundle.js.map +7 -0
  17. package/dist/editor.d.ts +33 -2
  18. package/dist/editor.d.ts.map +1 -1
  19. package/dist/editor.js +6739 -4773
  20. package/dist/editor.js.map +7 -1
  21. package/dist/embed.bundle.js +159 -18703
  22. package/dist/embed.bundle.js.map +7 -0
  23. package/dist/embed.d.ts +1 -1
  24. package/dist/embed.iife.js +37 -18595
  25. package/dist/embed.iife.js.map +7 -0
  26. package/dist/embed.js +1 -1
  27. package/dist/export-assets.json +27 -27
  28. package/dist/export-browser.bundle.js +1388 -477
  29. package/dist/export-browser.d.ts +20 -0
  30. package/dist/export-browser.d.ts.map +1 -1
  31. package/dist/export-browser.js +132 -37
  32. package/dist/export-browser.js.map +1 -1
  33. package/dist/font-runtime.d.ts.map +1 -1
  34. package/dist/font-runtime.js +54 -0
  35. package/dist/font-runtime.js.map +1 -1
  36. package/dist/line-metrics.d.ts +60 -0
  37. package/dist/line-metrics.d.ts.map +1 -0
  38. package/dist/line-metrics.js +343 -0
  39. package/dist/line-metrics.js.map +1 -0
  40. package/dist/pagination.bundle.js +3 -3214
  41. package/dist/pagination.bundle.js.map +7 -0
  42. package/dist/pagination.d.ts +86 -9
  43. package/dist/pagination.d.ts.map +1 -1
  44. package/dist/pagination.js +358 -55
  45. package/dist/pagination.js.map +1 -1
  46. package/dist/react.d.ts.map +1 -1
  47. package/dist/react.js +63 -12
  48. package/dist/react.js.map +1 -1
  49. package/dist/render-report-v2.schema.json +29 -0
  50. package/dist/session.bundle.js +2 -2013
  51. package/dist/session.bundle.js.map +7 -0
  52. package/dist/session.d.ts +14 -5
  53. package/dist/session.d.ts.map +1 -1
  54. package/dist/session.js +62 -29
  55. package/dist/session.js.map +1 -1
  56. package/dist/types.d.ts +137 -21
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/types.js +14 -0
  59. package/dist/types.js.map +1 -1
  60. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  61. package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
  62. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  63. package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
  64. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  65. package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
  66. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  67. package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
  68. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  69. package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
  70. package/dist/wasm/_framework/System.Memory.wasm +0 -0
  71. package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
  72. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  73. package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
  74. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  75. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
  76. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  77. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
  78. package/dist/wasm/_framework/dotnet.boot.js +11 -11
  79. package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
  80. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  81. package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
  82. package/dist/word-text.d.ts +14 -0
  83. package/dist/word-text.d.ts.map +1 -0
  84. package/dist/word-text.js +377 -0
  85. package/dist/word-text.js.map +1 -0
  86. package/dist/worker-proxy.bundle.js +2 -536
  87. package/dist/worker-proxy.bundle.js.map +7 -0
  88. package/dist/worker-proxy.d.ts +14 -1
  89. package/dist/worker-proxy.d.ts.map +1 -1
  90. package/dist/worker-proxy.js +357 -339
  91. package/dist/worker-proxy.js.map +1 -1
  92. package/package.json +13 -12
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pagination.ts", "../src/page-number-format.ts", "../src/line-metrics.ts", "../src/page-geometry.ts"],
4
+ "sourcesContent": ["/**\n * Pagination engine for creating a PDF.js-style paginated view from HTML output.\n *\n * This module provides client-side pagination that measures rendered content\n * and flows it across fixed-size page containers based on document dimensions.\n */\n\nimport { formatPageNumber } from \"./page-number-format.js\";\nimport { alignExactLineBaselines, insetClipBounds, preserveExactLineInk } from \"./line-metrics.js\";\nimport {\n DEFAULT_MARGIN,\n DEFAULT_PAGE_HEIGHT,\n DEFAULT_PAGE_WIDTH,\n parseSectionDimensions,\n ptToPx,\n pxToPt,\n resolvePageBands,\n} from \"./page-geometry.js\";\nimport type { PageBands, PageDimensions } from \"./page-geometry.js\";\n\nexport type { PageBands, PageDimensions } from \"./page-geometry.js\";\n\n/**\n * Whether two sections describe the same physical page box. A continuous section\n * break is only honorable when it does \u2014 a changed page size or margin set forces\n * a real page break, which is Word's behavior too.\n */\nfunction samePageBox(a: PageDimensions, b: PageDimensions): boolean {\n return (\n a.pageWidth === b.pageWidth &&\n a.pageHeight === b.pageHeight &&\n a.marginTop === b.marginTop &&\n a.marginRight === b.marginRight &&\n a.marginBottom === b.marginBottom &&\n a.marginLeft === b.marginLeft\n );\n}\n\n/**\n * Headers and footers for a specific section.\n */\nexport interface SectionHeaderFooter {\n /** Default header (used for odd pages or all pages) */\n headerDefault?: HTMLElement;\n /** First page header */\n headerFirst?: HTMLElement;\n /** Even page header */\n headerEven?: HTMLElement;\n /** Default footer (used for odd pages or all pages) */\n footerDefault?: HTMLElement;\n /** First page footer */\n footerFirst?: HTMLElement;\n /** Even page footer */\n footerEven?: HTMLElement;\n\n // Pre-measured heights (populated during registry parsing for lazy-loading compatibility)\n /** Measured height of default header in points */\n headerDefaultHeight?: number;\n /** Measured height of first page header in points */\n headerFirstHeight?: number;\n /** Measured height of even page header in points */\n headerEvenHeight?: number;\n /** Measured height of default footer in points */\n footerDefaultHeight?: number;\n /** Measured height of first page footer in points */\n footerFirstHeight?: number;\n /** Measured height of even page footer in points */\n footerEvenHeight?: number;\n}\n\n/**\n * Registry of headers and footers by section index.\n */\nexport type HeaderFooterRegistry = Map<number, SectionHeaderFooter>;\n\n/**\n * A measured content block with metadata for pagination decisions.\n */\nexport interface MeasuredBlock {\n /** The DOM element */\n element: HTMLElement;\n /** Section whose body owns this block after a continuous section transition. */\n sectionIndex: number;\n /** Measured height in points (content + padding + border, excluding margins) */\n heightPt: number;\n /** Top margin in points */\n marginTopPt: number;\n /** Bottom margin in points */\n marginBottomPt: number;\n /** Whether to keep this block with the next one */\n keepWithNext: boolean;\n /** Whether to keep all lines of this block together */\n keepLines: boolean;\n /** Whether to force a page break before this block */\n pageBreakBefore: boolean;\n /** Whether this is a page break marker */\n isPageBreak: boolean;\n /** Whether the block is a Word paragraph whose top margin represents paragraph space-before */\n isWordParagraph?: boolean;\n}\n\n/**\n * Information about a rendered page.\n */\nexport interface PageInfo {\n /** 1-based page number */\n pageNumber: number;\n /** Section index this page belongs to */\n sectionIndex: number;\n /** Page dimensions */\n dimensions: PageDimensions;\n /** The page container element */\n element: HTMLElement;\n}\n\n/**\n * Result of pagination operation.\n */\nexport interface PaginationResult {\n /** Total number of pages */\n totalPages: number;\n /** Array of page information */\n pages: PageInfo[];\n /** Present only when the caller supplied an exact layoutToken. */\n pageMap?: PageMap;\n}\n\nexport type PageMapMode = \"paginated\" | \"continuous\";\nexport type PageMapAvailability = \"available\" | \"unavailable\";\nexport type PageMapStory = \"body\" | \"header\" | \"footer\" | \"footnote\" | \"endnote\" | \"comment\";\n\nexport interface PageMapRect {\n /** Page-relative points, independent of viewer zoom/transform. */\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PageMapPage {\n pageNumber: number;\n pageInSection: number;\n width: number;\n height: number;\n sectionIndex?: number;\n pageName: string;\n}\n\nexport interface PageMapFragment {\n fragmentId: string;\n /** Canonical collision-safe `kind:scope:unid`, never the bare editor Unid. */\n anchorId: string;\n fragmentIndex: number;\n pageNumber: number;\n geometry: PageMapRect;\n story: PageMapStory;\n /** Table-cell ownership is orthogonal to story (e.g. a body or footnote table cell). */\n inTableCell: boolean;\n}\n\n/** Versioned portable layout contract consumed by DocxSession and remote agent surfaces. */\nexport interface PageMap {\n schemaVersion: 1;\n mode: PageMapMode;\n availability: PageMapAvailability;\n documentVersion: number;\n rendererFingerprint: string;\n pages: PageMapPage[];\n fragments: PageMapFragment[];\n}\n\n/** Explicit no-pages contract for a continuous viewer. It never estimates page numbers. */\nexport function createUnavailablePageMap(\n documentVersion: number,\n rendererFingerprint: string,\n mode: \"continuous\" = \"continuous\",\n): PageMap {\n if (!rendererFingerprint) throw new Error(\"rendererFingerprint must be non-empty\");\n return {\n schemaVersion: 1,\n mode,\n availability: \"unavailable\",\n documentVersion,\n rendererFingerprint,\n pages: [],\n fragments: [],\n };\n}\n\nexport interface PageCitationNavigation {\n navigated: boolean;\n target?: HTMLElement;\n pageNumber?: number;\n fragmentId?: string;\n unavailableReason?: \"citation_unavailable\" | \"fragment_not_found\";\n}\n\ninterface PageCitationHighlightState {\n target: HTMLElement;\n highlightClass?: string;\n addedClass?: boolean;\n inlineStyles?: Array<{ name: string; value: string; priority: string }>;\n}\n\nconst activePageCitationHighlights = new WeakMap<ParentNode, PageCitationHighlightState>();\n\n/** Remove the citation highlight previously applied within this paginated root. */\nexport function clearPageCitationHighlight(root: ParentNode): void {\n const active = activePageCitationHighlights.get(root);\n if (!active) return;\n if (active.highlightClass && active.addedClass) {\n active.target.classList.remove(active.highlightClass);\n }\n for (const style of active.inlineStyles ?? []) {\n if (style.value) {\n active.target.style.setProperty(style.name, style.value, style.priority);\n } else {\n active.target.style.removeProperty(style.name);\n }\n }\n activePageCitationHighlights.delete(root);\n}\n\n/**\n * Navigate an exact citation over an already-paginated DOM. The page-qualified fragment id is\n * authoritative; page + canonical source identity is a compatibility fallback for older v1 DOMs.\n */\nexport function navigateToPageCitation(\n root: ParentNode,\n citation: {\n availability: PageMapAvailability;\n anchorId: string;\n fragments: Array<{ fragmentId: string; pageNumber: number }>;\n },\n options: {\n highlightClass?: string;\n /** Apply a visible inline highlight when no class is supplied. Default true. */\n highlight?: boolean;\n behavior?: ScrollBehavior;\n block?: ScrollLogicalPosition;\n } = {},\n): PageCitationNavigation {\n clearPageCitationHighlight(root);\n if (citation.availability !== \"available\" || citation.fragments.length === 0) {\n return { navigated: false, unavailableReason: \"citation_unavailable\" };\n }\n\n const byAttribute = (name: string, value: string, within: ParentNode = root): HTMLElement | null => {\n for (const node of Array.from(within.querySelectorAll<HTMLElement>(`[${name}]`))) {\n if (node.getAttribute(name) === value) return node;\n }\n return null;\n };\n\n const fragment = citation.fragments[0];\n let target = byAttribute(\"data-page-fragment-id\", fragment.fragmentId);\n if (!target) {\n const page = byAttribute(\"data-page-number\", String(fragment.pageNumber));\n if (page) target = byAttribute(\"data-source-anchor-id\", citation.anchorId, page) ?? page;\n }\n if (!target) {\n return {\n navigated: false,\n pageNumber: fragment.pageNumber,\n fragmentId: fragment.fragmentId,\n unavailableReason: \"fragment_not_found\",\n };\n }\n\n if (options.highlightClass) {\n const addedClass = !target.classList.contains(options.highlightClass);\n target.classList.add(options.highlightClass);\n activePageCitationHighlights.set(root, {\n target,\n highlightClass: options.highlightClass,\n addedClass,\n });\n } else if (options.highlight !== false) {\n const names = [\"outline\", \"outline-offset\", \"background-color\"];\n const inlineStyles = names.map((name) => ({\n name,\n value: target.style.getPropertyValue(name),\n priority: target.style.getPropertyPriority(name),\n }));\n target.style.setProperty(\"outline\", \"3px solid #f4b400\", \"important\");\n target.style.setProperty(\"outline-offset\", \"2px\", \"important\");\n target.style.setProperty(\"background-color\", \"rgba(255, 235, 59, .18)\", \"important\");\n activePageCitationHighlights.set(root, { target, inlineStyles });\n }\n target.scrollIntoView({\n behavior: options.behavior ?? \"smooth\",\n block: options.block ?? \"center\",\n });\n return {\n navigated: true,\n target,\n pageNumber: fragment.pageNumber,\n fragmentId: fragment.fragmentId,\n };\n}\n\n/**\n * Options for the pagination engine.\n */\nexport interface PaginationOptions {\n /** Scale factor for rendering (1.0 = 100%). Default: 1 */\n scale?: number;\n /** CSS class prefix used in the HTML. Default: \"page-\" */\n cssPrefix?: string;\n /** Whether to show page numbers. Default: true */\n showPageNumbers?: boolean;\n /** Gap between pages in pixels. Default: 20 */\n pageGap?: number;\n /**\n * Whether ordinary paragraphs may be fragmented across page boundaries.\n * Defaults to false for direct PaginationEngine callers; read-only viewer\n * entry points opt in explicitly.\n */\n fragmentParagraphs?: boolean;\n /** Cooperative checkpoint for bounded non-yielding browser layout work. */\n checkCancellation?: () => void;\n /**\n * Skip fragment-identity stamping at the end of paginate(). Callers that mutate the page tree\n * afterwards (running-story placement) and then call normalizePageMapFragmentIdentities() would\n * otherwise pay the whole forced-layout pass twice and discard the first result. Default: false.\n */\n deferFragmentIdentities?: boolean;\n /**\n * Points to withhold from the body height available to specific physical pages (1-based page\n * number \u2192 pt). Only the fitting budget shrinks; the page's rendered bands do not move. The\n * standalone export uses it for its bounded corrective pass: when a finished page still clips\n * its body \u2014 its layout ran a few points past what the placement model predicted \u2014 the export\n * lays the document out again from pristine HTML with that page's budget reduced by the\n * overflow, so the offending block moves or splits onto the next page (issue #848).\n */\n bodyBudgetReductionsPt?: Readonly<Record<number, number>>;\n /**\n * Incremental admission check invoked immediately before each physical page is allocated.\n * Export callers use this to enforce `finalPages` without constructing an over-limit DOM first.\n */\n checkPageCount?: (prospectivePageCount: number) => void;\n /** Exact invalidation tokens used to materialize an authoritative PageMap with the result. */\n layoutToken?: { documentVersion: number; rendererFingerprint: string };\n}\n\n// Default letter size in points (612 x 792 = 8.5\" x 11\")\n// Maximum percentage of content height that footnotes can occupy\n// This allows footnotes to expand upward into body content space when needed\nconst MAX_FOOTNOTE_AREA_RATIO = 0.6; // 60% of content height\n// Hidden measurement and final absolutely-positioned note bands differ by sub-pixel border/margin\n// rounding in Chromium. Reserve a small physical-unit guard so the last baseline stays visible.\nconst FOOTNOTE_MEASUREMENT_GUARD_PT = 2;\n/** Internal provenance retained only while a note element waits between pages. */\nconst FOOTNOTE_SOURCE_POSITION_ATTR = \"data-pagination-footnote-source-position\";\n\n/**\n * Pagination engine that converts HTML with pagination metadata\n * into a paginated view with fixed-size page containers.\n */\n/**\n * Registry of footnotes by ID for per-page distribution.\n */\nexport type FootnoteRegistry = Map<string, HTMLElement>;\n\n/**\n * Tracks footnote content that needs to continue on the next page.\n */\ninterface FootnoteContinuation {\n /** The footnote ID being continued */\n footnoteId: string;\n /** Canonical fn:* definition identity from the registry wrapper. */\n sourceAnchorId?: string;\n /** Remaining paragraphs/elements that didn't fit */\n remainingElements: HTMLElement[];\n}\n\n/**\n * Tracks a partial footnote that was split on a page.\n */\ninterface PartialFootnote {\n /** The footnote ID */\n footnoteId: string;\n /** Elements that fit on this page */\n fittingElements: HTMLElement[];\n}\n\n/** Existing page payload that a newly split note must fit beside. */\ninterface FootnotePackingContext {\n footnoteIds: readonly string[];\n continuation?: FootnoteContinuation | null;\n partialFootnotes?: readonly PartialFootnote[];\n}\n\ntype ParagraphSplitAttempt =\n | { kind: \"split\"; head: HTMLElement; tail: HTMLElement }\n | { kind: \"no-fit\" }\n | { kind: \"indivisible\" };\n\ninterface ParagraphFragmentEndpoint {\n node: Node;\n offset: number;\n /** UTF-16 offset in the paragraph's flattened text. */\n textOffset: number;\n /** Prefer a DOM boundary outside an atomic field over its last text-node boundary. */\n priority: number;\n}\n\n/**\n * A section's `w:pgNumType`, as stamped on its wrapper by the converter. Both fields are optional\n * because both attributes are: an absent `start` means the section continues the previous section's\n * numbering, and an absent `format` means Word's default `1, 2, 3`.\n */\ninterface SectionPageNumbering {\n start?: number;\n format?: string;\n}\n\n/**\n * The top and bottom margins a block occupies in its parent's flow, in CSS px, AFTER CSS margin\n * collapsing through its first and last in-flow children (CSS 2.1 \u00A78.3.1). getBoundingClientRect()\n * excludes such a collapsed-through margin, and getComputedStyle() reports only the element's own:\n * the converter wraps a table in an unstyled `div`, so the table's own top margin collapses\n * through the wrapper and occupies page height that neither of those reads accounted for. The\n * paginator's budget then fell a few pixels to a line short of the laid-out page, and the export\n * refused the clipped page (issue #848).\n *\n * A margin collapses with a parent's while the parent is an ordinary block box (display block or\n * list-item, overflow visible, not floated or absolutely positioned \u2014 anything else establishes\n * its own formatting context) with no border or padding on that side, the child is the parent's\n * first (last) in-flow block-level child, and no line content precedes (follows) it. For the\n * bottom edge the parent must also have an automatic height. Positive margins combine by maximum\n * and negative ones by minimum, as CSS collapses them.\n */\nexport function collapsedBlockMarginsPx(view: Window, element: HTMLElement): { topPx: number; bottomPx: number } {\n return {\n topPx: collapseMarginChain(view, element, \"top\"),\n bottomPx: collapseMarginChain(view, element, \"bottom\"),\n };\n}\n\nfunction collapseMarginChain(view: Window, element: HTMLElement, edge: \"top\" | \"bottom\"): number {\n const margins: number[] = [];\n let current: HTMLElement | null = element;\n while (current) {\n const style = view.getComputedStyle(current);\n margins.push(parseFloat(edge === \"top\" ? style.marginTop : style.marginBottom) || 0);\n current = collapsesThroughTo(view, current, style, edge);\n }\n const positive = Math.max(0, ...margins);\n const negative = Math.min(0, ...margins);\n return positive + negative;\n}\n\n/** The child whose `edge` margin collapses through `parent`'s, or null when none does. */\nfunction collapsesThroughTo(\n view: Window,\n parent: HTMLElement,\n style: CSSStyleDeclaration,\n edge: \"top\" | \"bottom\",\n): HTMLElement | null {\n const ordinaryBlock = (style.display === \"block\" || style.display === \"list-item\")\n && style.overflowX === \"visible\" && style.overflowY === \"visible\"\n && style.float === \"none\" && style.position !== \"absolute\" && style.position !== \"fixed\";\n if (!ordinaryBlock) return null;\n // Column boxes establish a formatting context: child margins stay inside the multicol\n // box and are already included in its measured height (CSS Multi-column Layout \u00A72).\n if (style.columnCount !== \"auto\" || style.columnWidth !== \"auto\") return null;\n const border = parseFloat(edge === \"top\" ? style.borderTopWidth : style.borderBottomWidth) || 0;\n const padding = parseFloat(edge === \"top\" ? style.paddingTop : style.paddingBottom) || 0;\n if (border !== 0 || padding !== 0) return null;\n if (edge === \"bottom\" && parent.style.height !== \"\") return null;\n\n const nodes = Array.from(parent.childNodes);\n if (edge === \"bottom\") nodes.reverse();\n for (const node of nodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n if ((node.textContent ?? \"\").trim() !== \"\") return null; // line content comes first\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\n const child = node as HTMLElement;\n const childStyle = view.getComputedStyle(child);\n if (childStyle.display === \"none\") continue;\n if (childStyle.position === \"absolute\" || childStyle.position === \"fixed\" || childStyle.float !== \"none\") continue;\n const blockLevel = childStyle.display === \"block\" || childStyle.display === \"list-item\"\n || childStyle.display === \"table\" || childStyle.display === \"flow-root\"\n || childStyle.display === \"flex\" || childStyle.display === \"grid\";\n return blockLevel ? child : null; // an inline-level child starts a line box\n }\n return null;\n}\n\n/** A page-space box in client coordinates: left/top inclusive, right/bottom exclusive. */\nexport interface VisibleBox {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\n/**\n * Set by the paginator on a floating drawing it promotes out of its paragraph into the page box;\n * the value is the `data-source-anchor-id` of the element that hosted it. It survives into the\n * standalone export, whose offline re-verification measures through the same function.\n */\nexport const DRAWING_HOST_ATTRIBUTE = \"data-docx-anchor-host\";\n\n/**\n * Set on a source-anchored element a floating drawing was promoted out of, so\n * {@link measureRenderedSource} looks up promoted drawings only for elements that have them.\n */\nexport const HOSTS_PROMOTED_DRAWING_ATTRIBUTE = \"data-docx-hosts-promoted-drawing\";\n\n/** True when `element` shows no text (beyond white space) and no image, SVG or canvas. */\nfunction rendersOnlyItsLine(element: HTMLElement): boolean {\n return (element.textContent ?? '').replace(/[\\s\\u00a0]/g, '') === ''\n && element.querySelector('img, svg, canvas, [data-docx-drawing-anchor]') === null;\n}\n\n/** Self-contained closure for trusted host evaluation in the script-disabled print context. */\nfunction createRenderedSourceMeasurer(\n clipInset: typeof insetClipBounds,\n drawingHostAttribute: string,\n hostsPromotedDrawingAttribute: string,\n) {\n /**\n * Intersect a box with the page and with every ancestor that establishes an overflow clip before\n * the page root. getBoundingClientRect() reports layout outside those clips, which is not rendered\n * and therefore must not satisfy PageMap completeness or inflate portable geometry.\n */\n function visibleWithinPage(\n view: Window,\n element: HTMLElement,\n page: HTMLElement,\n pageRect: DOMRect,\n rect: DOMRect,\n ): VisibleBox {\n let left = Math.max(rect.left, pageRect.left);\n let top = Math.max(rect.top, pageRect.top);\n let right = Math.min(rect.right, pageRect.right);\n let bottom = Math.min(rect.bottom, pageRect.bottom);\n const clips = (value: string) =>\n value === \"hidden\" || value === \"clip\" || value === \"scroll\" || value === \"auto\";\n\n for (let ancestor = element.parentElement;\n ancestor && ancestor !== page;\n ancestor = ancestor.parentElement) {\n const style = view.getComputedStyle(ancestor);\n const clipsX = clips(style.overflowX);\n const clipsY = clips(style.overflowY);\n if (!clipsX && !clipsY && !style.clipPath.startsWith(\"inset(\")) continue;\n const ancestorRect = ancestor.getBoundingClientRect();\n const scale = ancestorRect.width / ancestor.offsetWidth || 1;\n const inset = clipInset(style.clipPath, ancestorRect, scale);\n if (inset) {\n left = Math.max(left, inset.left);\n top = Math.max(top, inset.top);\n right = Math.min(right, inset.right);\n bottom = Math.min(bottom, inset.bottom);\n }\n if (clipsX) {\n left = Math.max(left, ancestorRect.left);\n right = Math.min(right, ancestorRect.right);\n }\n if (clipsY) {\n top = Math.max(top, ancestorRect.top);\n bottom = Math.min(bottom, ancestorRect.bottom);\n }\n }\n return { left, top, right, bottom };\n }\n\n return function measureRenderedSource(\n view: Window,\n element: HTMLElement,\n page: HTMLElement,\n pageRect: DOMRect = page.getBoundingClientRect(),\n ): { rect: DOMRect; visible: VisibleBox } {\n const own = element.getBoundingClientRect();\n const ownResult = () => ({ rect: own, visible: visibleWithinPage(view, element, page, pageRect, own) });\n const ownMeasurable = own.width > 0 && own.height > 0;\n const hostsPromotedDrawing = element.hasAttribute(hostsPromotedDrawingAttribute);\n if (ownMeasurable && !hostsPromotedDrawing) return ownResult();\n\n const hostAnchorId = element.dataset.sourceAnchorId;\n const promoted = hostAnchorId\n ? Array.from(page.querySelectorAll<HTMLElement>(`[${drawingHostAttribute}]`))\n .filter((drawing) => drawing.getAttribute(drawingHostAttribute) === hostAnchorId)\n : [];\n if (ownMeasurable && promoted.length === 0) return ownResult();\n const rendered = [\n ...(ownMeasurable ? [element] : Array.from(element.querySelectorAll<HTMLElement>(\"*\"))),\n ...promoted.flatMap((drawing) => [drawing, ...Array.from(drawing.querySelectorAll<HTMLElement>(\"*\"))]),\n ];\n\n let left = Infinity;\n let top = Infinity;\n let right = -Infinity;\n let bottom = -Infinity;\n for (const child of rendered) {\n const style = view.getComputedStyle(child);\n if (style.display === \"none\" || style.visibility === \"hidden\") continue;\n const childRect = child.getBoundingClientRect();\n if (childRect.width <= 0 || childRect.height <= 0) continue;\n const visible = visibleWithinPage(view, child, page, pageRect, childRect);\n if (visible.right <= visible.left || visible.bottom <= visible.top) continue;\n left = Math.min(left, visible.left);\n top = Math.min(top, visible.top);\n right = Math.max(right, visible.right);\n bottom = Math.max(bottom, visible.bottom);\n }\n if (!(left < right && top < bottom)) return ownResult();\n return { rect: new DOMRect(left, top, right - left, bottom - top), visible: { left, top, right, bottom } };\n };\n}\n\n/**\n * Where a source-anchored element renders on `page`: `rect` is its layout box and `visible` the\n * part of it the page shows. Normally that is the element's own border box. An element the\n * paginator promoted floating drawings out of measures as the union of what it renders \u2014 its own\n * box (or, when that is empty, its descendants) and the drawings promoted out of it (linked by\n * {@link DRAWING_HOST_ATTRIBUTE}) \u2014 each clipped along its OWN ancestor chain, since a promoted\n * drawing sits outside the text column that clips its host (issue #849). Such a paragraph keeps a\n * line of its own when the drawing was its only content (issue #880), and its fragment still\n * encloses the drawing. Returns the element's own degenerate box when none of that renders.\n *\n * Fragment-identity normalization, PageMap materialization and the standalone export's offline\n * re-verification all measure through this one function, so they agree on which elements are\n * measurable and where.\n */\nexport const measureRenderedSource = createRenderedSourceMeasurer(\n insetClipBounds, DRAWING_HOST_ATTRIBUTE, HOSTS_PROMOTED_DRAWING_ATTRIBUTE,\n);\n\n/** @internal Owned runtime code only; never inserts a script or enables document JavaScript. */\nexport function renderedSourceMeasurerExpression(): string {\n return `(${createRenderedSourceMeasurer.toString()})(${insetClipBounds.toString()},\n ${JSON.stringify(DRAWING_HOST_ATTRIBUTE)}, ${JSON.stringify(HOSTS_PROMOTED_DRAWING_ATTRIBUTE)})`;\n}\n\nexport class PaginationEngine {\n private stagingElement: HTMLElement;\n private containerElement: HTMLElement;\n private document: Document;\n private view: Window & typeof globalThis;\n private scale: number;\n private cssPrefix: string;\n private showPageNumbers: boolean;\n private pageGap: number;\n private fragmentParagraphs: boolean;\n private deferFragmentIdentities: boolean;\n private bodyBudgetReductionsPt: Readonly<Record<number, number>>;\n private cancellationCheckpoint?: () => void;\n private pageCountCheckpoint?: (prospectivePageCount: number) => void;\n private createdPageCount = 0;\n private layoutToken?: { documentVersion: number; rendererFingerprint: string };\n private hfRegistry: HeaderFooterRegistry;\n private footnoteRegistry: FootnoteRegistry;\n private footnoteSeparator: HTMLElement | null = null;\n private footnoteContinuationSeparator: HTMLElement | null = null;\n private commentMarginRegistry: Map<string, HTMLElement>;\n private footnoteLayoutLikeWord8 = false;\n private pendingFootnoteContinuation: FootnoteContinuation | null = null;\n /** Per-section `w:pgNumType` (start / format), read off the section wrappers. */\n private pageNumbering: Map<number, SectionPageNumbering> = new Map();\n private lastPages: PageInfo[] = [];\n private expectedPageMapAnchorIds: Set<string> = new Set();\n private state: \"ready\" | \"running\" | \"complete\" | \"failed\" = \"ready\";\n\n /**\n * Creates a new pagination engine.\n *\n * @param staging - The staging element or its ID containing the content to paginate\n * @param container - The container element or its ID where pages will be rendered\n * @param options - Pagination options\n */\n constructor(\n staging: HTMLElement | string,\n container: HTMLElement | string,\n options: PaginationOptions = {}\n ) {\n const ownerDocument =\n typeof staging !== \"string\"\n ? staging.ownerDocument\n : typeof container !== \"string\"\n ? container.ownerDocument\n : globalThis.document;\n this.stagingElement =\n typeof staging === \"string\"\n ? (ownerDocument.getElementById(staging) as HTMLElement)\n : staging;\n this.containerElement =\n typeof container === \"string\"\n ? (ownerDocument.getElementById(container) as HTMLElement)\n : container;\n\n if (!this.stagingElement) {\n throw new Error(\"Staging element not found\");\n }\n if (!this.containerElement) {\n throw new Error(\"Container element not found\");\n }\n if (this.stagingElement.ownerDocument !== this.containerElement.ownerDocument) {\n throw new Error(\"Staging and container elements must belong to the same document\");\n }\n const view = ownerDocument.defaultView;\n if (!view) {\n throw new Error(\"Pagination requires an attached document with a defaultView\");\n }\n this.document = ownerDocument;\n this.view = view as Window & typeof globalThis;\n\n this.scale = options.scale ?? 1;\n this.cssPrefix = options.cssPrefix ?? \"page-\";\n this.showPageNumbers = options.showPageNumbers ?? true;\n this.pageGap = options.pageGap ?? 20;\n this.fragmentParagraphs = options.fragmentParagraphs ?? false;\n this.deferFragmentIdentities = options.deferFragmentIdentities ?? false;\n this.bodyBudgetReductionsPt = options.bodyBudgetReductionsPt ?? {};\n this.cancellationCheckpoint = options.checkCancellation;\n this.pageCountCheckpoint = options.checkPageCount;\n this.layoutToken = options.layoutToken;\n this.hfRegistry = new Map();\n this.footnoteRegistry = new Map();\n this.commentMarginRegistry = new Map();\n }\n\n /**\n * Runs the pagination process.\n *\n * @returns PaginationResult with page information\n */\n paginate(): PaginationResult {\n this.checkpoint();\n if (this.state !== \"ready\") {\n throw new Error(`PaginationEngine is one-shot and is already ${this.state}`);\n }\n if (!this.stagingElement.isConnected || !this.containerElement.isConnected) {\n throw new Error(\"Pagination requires staging and container elements attached to their document\");\n }\n if (this.document.documentElement.getBoundingClientRect().width <= 0) {\n throw new Error(\"Pagination requires a browsing context with non-zero layout\");\n }\n this.state = \"running\";\n\n try {\n const pages: PageInfo[] = [];\n let pageNumber = 1;\n\n // Parse the header/footer registry if present\n this.hfRegistry = this.parseHeaderFooterRegistry();\n\n // Parse the footnote registry if present\n this.footnoteRegistry = this.parseFootnoteRegistry();\n ({\n normal: this.footnoteSeparator,\n continuation: this.footnoteContinuationSeparator,\n } = this.parseFootnoteSeparators());\n\n // Parse the margin-comment registry if present. Its entries are cloned into\n // the side substrate of pages that contain the corresponding range marker.\n this.commentMarginRegistry = this.parseCommentMarginRegistry();\n this.footnoteLayoutLikeWord8 =\n this.stagingElement.dataset.footnoteLayoutLikeWord8 === \"true\";\n\n // Find all section containers\n const sections = this.stagingElement.querySelectorAll<HTMLElement>(\n \"[data-section-index]\"\n );\n\n this.pageNumbering = this.parsePageNumbering(sections);\n\n // If no sections found, treat the entire staging content as one section\n const sectionsToProcess =\n sections.length > 0 ? Array.from(sections) : [this.stagingElement];\n\n // Snapshot the addressable SOURCE inventory before flow moves nodes out of staging. PageMap\n // completeness cannot be inferred from whatever survives into page boxes: that would let a\n // dropped block silently disappear from both the DOM and the supposedly authoritative map.\n // Running-story variants and cited notes are inventoried from their source registries.\n this.expectedPageMapAnchorIds = new Set();\n const referencedFootnoteIds = new Set<string>();\n const referencedCommentIds = new Set<string>();\n for (const section of sectionsToProcess) {\n this.checkpoint();\n this.collectExpectedSourceAnchors(section, this.expectedPageMapAnchorIds, true);\n for (const reference of Array.from(section.querySelectorAll<HTMLElement>(\"[data-footnote-id]\"))) {\n this.checkpoint();\n if (reference.closest(\"#pagination-footnote-registry, #pagination-hf-registry\")) continue;\n const id = reference.dataset.footnoteId;\n if (id) referencedFootnoteIds.add(id);\n }\n for (const reference of Array.from(section.querySelectorAll<HTMLElement>(\"[data-comment-id]\"))) {\n this.checkpoint();\n if (reference.closest(\n \"#pagination-comment-margin-registry, #pagination-footnote-registry, #pagination-hf-registry\",\n )) continue;\n const id = reference.dataset.commentId;\n if (id) referencedCommentIds.add(id);\n }\n }\n for (const id of referencedFootnoteIds) {\n this.checkpoint();\n const source = this.footnoteRegistry.get(id);\n if (!source) continue;\n this.collectExpectedSourceAnchors(source, this.expectedPageMapAnchorIds);\n // Margin comments are presentation stories selected by markers. Markers\n // inside a cited footnote are just as visible/addressable as body markers.\n for (const reference of Array.from(\n source.querySelectorAll<HTMLElement>(\"[data-comment-id]\"),\n )) {\n this.checkpoint();\n const commentId = reference.dataset.commentId;\n if (commentId) referencedCommentIds.add(commentId);\n }\n }\n for (const id of referencedCommentIds) {\n this.checkpoint();\n const source = this.commentMarginRegistry.get(id);\n if (source) this.collectExpectedSourceAnchors(source, this.expectedPageMapAnchorIds);\n }\n\n // Group adjacent sections into page runs. A `w:type=\"continuous\"` section keeps\n // filling the page its predecessor started rather than opening a fresh one, so it\n // joins the previous run \u2014 provided the page box (size and margins) is unchanged,\n // which is also Word's own condition for honoring a continuous break. Pages of a\n // merged run begins under the leading section's running stories. Each measured block retains\n // its own section, though: once a continuous section spills to a later physical page, that\n // page must switch to the new section's headers, footers, and page-numbering state.\n interface PageRun {\n sections: HTMLElement[];\n sectionIndex: number;\n sectionType: string;\n dims: PageDimensions;\n sectionDimensions: Map<number, PageDimensions>;\n }\n const runs: PageRun[] = [];\n for (const section of sectionsToProcess) {\n this.checkpoint();\n const dims = parseSectionDimensions(section);\n const previous = runs[runs.length - 1];\n if (\n previous &&\n section.dataset.sectionType === \"continuous\" &&\n samePageBox(previous.dims, dims)\n ) {\n previous.sections.push(section);\n previous.sectionDimensions.set(\n parseInt(section.dataset.sectionIndex || \"0\", 10),\n dims,\n );\n } else {\n const sectionIndex = parseInt(section.dataset.sectionIndex || \"0\", 10);\n runs.push({\n sections: [section],\n sectionIndex,\n sectionType: section.dataset.sectionType ?? \"nextPage\",\n dims,\n sectionDimensions: new Map([[sectionIndex, dims]]),\n });\n }\n }\n\n for (const run of runs) {\n this.checkpoint();\n // Odd/even section breaks begin the new section on the requested physical side. When the\n // following page has the opposite parity, Word inserts one intentionally blank filler page.\n // It belongs to the preceding section, advances physical/logical numbering, retains that\n // section's paper geometry, and carries no running story.\n const needsParityFiller = pages.length > 0\n && ((run.sectionType === \"oddPage\" && pageNumber % 2 === 0)\n || (run.sectionType === \"evenPage\" && pageNumber % 2 === 1));\n if (needsParityFiller) {\n const precedingPage = pages[pages.length - 1];\n const precedingPageInSection = parseInt(\n precedingPage.element.dataset.pageInSection ?? \"1\",\n 10,\n );\n const precedingDisplayedPageNumber = parseInt(\n precedingPage.element.dataset.displayedPageNumber ?? String(precedingPage.pageNumber),\n 10,\n );\n const filler = this.createPage(\n precedingPage.dimensions,\n pageNumber,\n precedingPage.sectionIndex,\n precedingDisplayedPageNumber + 1,\n [],\n precedingPageInSection + 1,\n [],\n 0,\n null,\n undefined,\n true,\n );\n pages.push(filler);\n pageNumber++;\n }\n // Make staging visible for measurement\n this.stagingElement.style.visibility = \"hidden\";\n this.stagingElement.style.position = \"absolute\";\n this.stagingElement.style.left = \"-9999px\";\n this.stagingElement.style.display = \"block\";\n\n const blocks: MeasuredBlock[] = [];\n for (const section of run.sections) {\n this.checkpoint();\n const sectionIndex = parseInt(section.dataset.sectionIndex || \"0\", 10);\n const sectionDims = run.sectionDimensions.get(sectionIndex) ?? run.dims;\n // Set width for accurate line wrapping\n section.style.width = `${sectionDims.contentWidth}pt`;\n\n const columnCount = parseInt(section.dataset.cols || \"1\", 10);\n if (columnCount > 1) {\n const gap = parseFloat(section.dataset.colGap || \"\");\n blocks.push(...this.buildColumnBlocks(\n section,\n sectionDims,\n sectionIndex,\n columnCount,\n Number.isFinite(gap) ? gap : 36,\n section.dataset.sectionType === \"continuous\"\n && section === sectionsToProcess[sectionsToProcess.length - 1],\n ));\n } else {\n blocks.push(...this.measureBlocks(section, sectionDims, sectionIndex));\n }\n }\n\n // Flow blocks into pages\n const sectionPages = this.flowToPages(\n blocks,\n run.dims,\n pageNumber,\n run.sectionIndex,\n run.sectionDimensions,\n );\n this.checkpoint();\n pages.push(...sectionPages);\n pageNumber += sectionPages.length;\n }\n\n // Hide staging after measurement\n this.stagingElement.style.display = \"none\";\n\n // Every page box exists now, so NUMPAGES has an answer and each PAGE marker knows its page.\n this.substitutePageNumberFields(pages.length);\n\n // Only running-story variants selected by a real page are expected to materialize. Read IDs\n // from registry sources, not presentation clones, so a failed clone remains detectable.\n for (const page of pages) {\n this.checkpoint();\n if (page.element.dataset.sectionFiller === \"true\") continue;\n const pageInSection = parseInt(page.element.dataset.pageInSection || \"1\", 10);\n const displayedPageNumber = parseInt(\n page.element.dataset.displayedPageNumber || String(page.pageNumber),\n 10,\n );\n const header = this.selectHeader(page.sectionIndex, pageInSection, displayedPageNumber);\n const footer = this.selectFooter(page.sectionIndex, pageInSection, displayedPageNumber);\n if (header) this.collectExpectedSourceAnchors(header, this.expectedPageMapAnchorIds);\n if (footer) this.collectExpectedSourceAnchors(footer, this.expectedPageMapAnchorIds);\n }\n\n // Establish one active editor anchor and page-qualify every presentation fragment.\n // Full canonical source identities remain on all clones, including table cells.\n this.qualifyPageFragments(pages);\n this.transferVisibleFragmentTargets();\n if (!this.deferFragmentIdentities) this.normalizeVisiblePageFragments(pages);\n this.lastPages = pages;\n\n const result = {\n totalPages: pages.length,\n pages,\n pageMap: this.layoutToken\n ? this.materializePageMap(\n this.layoutToken.documentVersion,\n this.layoutToken.rendererFingerprint,\n )\n : undefined,\n };\n this.state = \"complete\";\n return result;\n } catch (error) {\n this.state = \"failed\";\n throw error;\n }\n }\n\n private checkpoint(): void {\n this.cancellationCheckpoint?.();\n }\n\n /**\n * Normalize visible fragment identities after a caller applies final standalone styles. This\n * deliberately runs before the stability barrier; materializePageMap is read-only so PageMap\n * measurement cannot mutate a tree after it was declared stable.\n */\n normalizePageMapFragmentIdentities(): void {\n if (this.lastPages.length === 0) {\n throw new Error(\"paginate() must complete before fragment identities can be normalized\");\n }\n this.normalizeVisiblePageFragments(this.lastPages);\n }\n\n /**\n * Materialize the last completed browser layout as portable page-relative point geometry.\n * The caller supplies both invalidation tokens; this engine never guesses a document version\n * or renderer fingerprint.\n */\n materializePageMap(documentVersion: number, rendererFingerprint: string): PageMap {\n if (!Number.isSafeInteger(documentVersion) || documentVersion < 0) {\n throw new Error(\"documentVersion must be a non-negative safe integer\");\n }\n if (!rendererFingerprint) throw new Error(\"rendererFingerprint must be non-empty\");\n if (this.lastPages.length === 0) throw new Error(\"paginate() must complete before materializePageMap()\");\n\n const pages: PageMapPage[] = this.lastPages.map((page) => ({\n pageNumber: page.pageNumber,\n pageInSection: parseInt(page.element.dataset.pageInSection || \"1\", 10),\n width: page.dimensions.pageWidth,\n height: page.dimensions.pageHeight,\n sectionIndex: page.sectionIndex,\n pageName: `docxodus-section-${page.sectionIndex}`,\n }));\n\n const fragments: PageMapFragment[] = [];\n const requiredAnchorIds = new Set(this.expectedPageMapAnchorIds);\n if (requiredAnchorIds.size === 0) {\n throw new Error(\"cannot publish an available PageMap without canonical source inventory\");\n }\n const measuredAnchorIds = new Set<string>();\n const emittedFragmentCounts = new Map<string, number>();\n for (const page of this.lastPages) {\n this.checkpoint();\n const pageRect = page.element.getBoundingClientRect();\n if (pageRect.width <= 0 || pageRect.height <= 0) {\n throw new Error(`page ${page.pageNumber} has no measurable geometry`);\n }\n // Ratio-to-known-page-size removes CSS px, zoom, and transform from the contract.\n const pointPerRenderedX = page.dimensions.pageWidth / pageRect.width;\n const pointPerRenderedY = page.dimensions.pageHeight / pageRect.height;\n const nodes = page.element.querySelectorAll<HTMLElement>(\"[data-source-anchor-id]\");\n for (const element of Array.from(nodes)) {\n this.checkpoint();\n // Preserve the source-side exclusion contract on presentation clones as well. This\n // covers the node itself and any excluded/hidden/aria-hidden ancestor within the page.\n if (this.isDeliberatelyUnrenderedSource(element, page.element)) continue;\n const anchorId = element.dataset.sourceAnchorId;\n if (!anchorId || !element.dataset.pageFragmentId\n || !Number.isInteger(parseInt(element.dataset.fragmentIndex || \"\", 10))) {\n throw new Error(`page ${page.pageNumber} contains an unqualified source anchor`);\n }\n\n const style = this.view.getComputedStyle(element);\n const deliberatelyHidden = style.display === \"none\" || style.visibility === \"hidden\";\n if (deliberatelyHidden) continue;\n const { rect, visible: visibleRect } = measureRenderedSource(this.view, element, page.element, pageRect);\n requiredAnchorIds.add(anchorId);\n const left = visibleRect.left;\n const top = visibleRect.top;\n const right = visibleRect.right;\n const bottom = visibleRect.bottom;\n if (rect.width <= 0 || rect.height <= 0 || right <= left || bottom <= top) {\n // A continued note/story clone can contain children clipped off this page which become\n // measurable on its next clone. Enforce completeness once every page has been inspected.\n continue;\n }\n\n measuredAnchorIds.add(anchorId);\n // Clipped descendants in repeated note/story clones are deliberately omitted from the\n // portable map. Re-number only the visible fragments so the emitted contract remains\n // contiguous even when an earlier DOM clone carried no visible geometry on its page.\n const fragmentIndex = emittedFragmentCounts.get(anchorId) ?? 0;\n emittedFragmentCounts.set(anchorId, fragmentIndex + 1);\n const fragmentId = `p${page.pageNumber}-f${fragmentIndex}-${anchorId}`;\n if (element.dataset.fragmentIndex !== String(fragmentIndex)\n || element.dataset.pageFragmentId !== fragmentId\n || element.dataset.pageNumber !== String(page.pageNumber)) {\n throw new Error(\n `page ${page.pageNumber} fragment identity changed after final-tree normalization`,\n );\n }\n fragments.push({\n fragmentId,\n anchorId,\n fragmentIndex,\n pageNumber: page.pageNumber,\n geometry: {\n x: (left - pageRect.left) * pointPerRenderedX,\n y: (top - pageRect.top) * pointPerRenderedY,\n width: (right - left) * pointPerRenderedX,\n height: (bottom - top) * pointPerRenderedY,\n },\n story: this.storyForCanonicalAnchor(anchorId),\n inTableCell: element.matches(\"td,th\") || element.closest(\"td,th\") !== null,\n });\n }\n }\n\n const missingAnchor = Array.from(requiredAnchorIds).find((id) => !measuredAnchorIds.has(id));\n if (missingAnchor) {\n throw new Error(`source anchor ${missingAnchor} has no measurable fragment in the paginated layout`);\n }\n\n return {\n schemaVersion: 1,\n mode: \"paginated\",\n availability: \"available\",\n documentVersion,\n rendererFingerprint,\n pages,\n fragments,\n };\n }\n\n private normalizeVisiblePageFragments(pages: PageInfo[]): void {\n const emittedFragmentCounts = new Map<string, number>();\n for (const page of pages) {\n this.checkpoint();\n const pageRect = page.element.getBoundingClientRect();\n for (const element of Array.from(\n page.element.querySelectorAll<HTMLElement>(\"[data-source-anchor-id]\"),\n )) {\n this.checkpoint();\n if (this.isDeliberatelyUnrenderedSource(element, page.element)) continue;\n const anchorId = element.dataset.sourceAnchorId;\n if (!anchorId) continue;\n const style = this.view.getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\") continue;\n const { rect, visible } = measureRenderedSource(this.view, element, page.element, pageRect);\n if (rect.width <= 0 || rect.height <= 0\n || visible.right <= visible.left || visible.bottom <= visible.top) continue;\n const fragmentIndex = emittedFragmentCounts.get(anchorId) ?? 0;\n emittedFragmentCounts.set(anchorId, fragmentIndex + 1);\n element.dataset.pageNumber = String(page.pageNumber);\n element.dataset.fragmentIndex = String(fragmentIndex);\n element.dataset.pageFragmentId = `p${page.pageNumber}-f${fragmentIndex}-${anchorId}`;\n }\n }\n }\n\n private storyForCanonicalAnchor(anchorId: string): PageMapStory {\n const first = anchorId.indexOf(\":\");\n const second = first < 0 ? -1 : anchorId.indexOf(\":\", first + 1);\n const scope = first >= 0 && second > first ? anchorId.slice(first + 1, second) : \"body\";\n if (scope.startsWith(\"hdr\")) return \"header\";\n if (scope.startsWith(\"ftr\")) return \"footer\";\n if (scope === \"fn\") return \"footnote\";\n if (scope === \"en\") return \"endnote\";\n if (scope === \"cmt\") return \"comment\";\n return \"body\";\n }\n\n /**\n * Add canonical IDs from an addressable source subtree to the pre-pagination inventory.\n * Registry wrappers are excluded when scanning staging because selectable registry contents are\n * inventoried separately. Producers may explicitly mark content that has no visual substrate\n * with `data-page-map-exclude=\"true\"`; native hidden semantics carry the same signal.\n */\n private collectExpectedSourceAnchors(\n source: HTMLElement,\n destination: Set<string>,\n excludeRegistries = false,\n ): void {\n const candidates: HTMLElement[] = source.matches(\"[data-source-anchor-id]\") ? [source] : [];\n candidates.push(...Array.from(source.querySelectorAll<HTMLElement>(\"[data-source-anchor-id]\")));\n for (const element of candidates) {\n if (excludeRegistries && element.closest(\n \"#pagination-hf-registry, #pagination-footnote-registry, #pagination-comment-margin-registry\",\n )) {\n continue;\n }\n if (this.isZeroHeightExplicitBreakCarrier(element)) {\n // Flow consumes the following break marker and clones this otherwise-empty paragraph.\n // Persist the exclusion so that the clone cannot reintroduce the anchor during PageMap\n // measurement after the marker itself has disappeared.\n element.dataset.pageMapExclude = \"true\";\n }\n if (this.isDeliberatelyUnrenderedSource(element, source)) continue;\n const anchorId = element.dataset.sourceAnchorId;\n if (anchorId) destination.add(anchorId);\n }\n }\n\n private isZeroHeightExplicitBreakCarrier(element: HTMLElement): boolean {\n const next = element.nextElementSibling as HTMLElement | null;\n const bounds = element.getBoundingClientRect();\n return element.hasAttribute(\"data-source-anchor-id\")\n && (element.textContent ?? \"\").replace(/\\u00a0/g, \"\").trim() === \"\"\n // Exclude only the converter's zero-height carrier. A blank paragraph with height from\n // padding, borders, a background, or authored sizing still has a visible page substrate and\n // must remain addressable under the authoritative PageMap contract.\n && bounds.height <= 0.01\n && !element.querySelector(\"[data-source-anchor-id]\")\n && !element.querySelector(\"img,svg,canvas,table,hr,input,textarea,select\")\n && (next?.dataset.pageBreak === \"true\"\n || next?.classList.contains(`${this.cssPrefix}break`) === true);\n }\n\n private isDeliberatelyUnrenderedSource(element: HTMLElement, sourceRoot: HTMLElement): boolean {\n for (let current: HTMLElement | null = element; current; current = current.parentElement) {\n const zeroHeightExplicitBreakCarrier = current === element\n && this.isZeroHeightExplicitBreakCarrier(current);\n if (\n current.dataset.pageMapExclude === \"true\"\n // An explicit page-break marker controls flow but has no painted substrate. The converter\n // intentionally emits it as an empty div, so requiring point geometry for its source\n // identity would make every otherwise-valid document with w:br[type=page] fail closed.\n || current.dataset.pageBreak === \"true\"\n || current.classList.contains(`${this.cssPrefix}break`)\n || zeroHeightExplicitBreakCarrier\n || current.hidden\n || current.getAttribute(\"aria-hidden\") === \"true\"\n || current.style.display === \"none\"\n || current.style.visibility === \"hidden\"\n ) {\n return true;\n }\n if (current === sourceRoot) break;\n }\n return false;\n }\n\n /**\n * Keep exactly one active bare-Unid editor anchor per source block. Presentation clones use\n * canonical source identity plus page/fragment qualification instead.\n */\n private qualifyPageFragments(pages: PageInfo[]): void {\n const fragmentCounts = new Map<string, number>();\n const activeCanonicalIds = new Set<string>();\n const activeBareAnchorIds = new Set<string>();\n\n const makeInactive = (element: HTMLElement): void => {\n element.removeAttribute(\"data-anchor\");\n element.removeAttribute(\"data-committed-text\");\n if (element.hasAttribute(\"contenteditable\")) element.setAttribute(\"contenteditable\", \"false\");\n };\n\n for (const page of pages) {\n const nodes = page.element.querySelectorAll<HTMLElement>(\"[data-source-anchor-id]\");\n for (const element of Array.from(nodes)) {\n const anchorId = element.dataset.sourceAnchorId;\n if (!anchorId) continue;\n const fragmentIndex = fragmentCounts.get(anchorId) ?? 0;\n fragmentCounts.set(anchorId, fragmentIndex + 1);\n element.dataset.pageNumber = String(page.pageNumber);\n element.dataset.fragmentIndex = String(fragmentIndex);\n element.dataset.pageFragmentId = `p${page.pageNumber}-f${fragmentIndex}-${anchorId}`;\n\n const story = this.storyForCanonicalAnchor(anchorId);\n const mayOwnActiveEditorAnchor =\n story === \"body\" || story === \"comment\" || story === \"footnote\" || story === \"endnote\";\n if (element.hasAttribute(\"data-anchor\")) {\n const bareAnchorId = element.dataset.anchor!;\n if (mayOwnActiveEditorAnchor\n && !activeCanonicalIds.has(anchorId)\n && !activeBareAnchorIds.has(bareAnchorId)) {\n activeCanonicalIds.add(anchorId);\n activeBareAnchorIds.add(bareAnchorId);\n } else {\n makeInactive(element);\n }\n }\n }\n }\n\n // Body/comment page nodes are the editable copies. A repeated header/footer registry entry can\n // also render the same source story once per section/variant, so retain at most one active\n // staging node per canonical source and make every presentation duplicate inert.\n const activeStagingCanonicalIds = new Set<string>();\n for (const element of Array.from(\n this.stagingElement.querySelectorAll<HTMLElement>(\"[data-source-anchor-id][data-anchor]\"),\n )) {\n const anchorId = element.dataset.sourceAnchorId;\n if (!anchorId) continue;\n const bareAnchorId = element.dataset.anchor!;\n if (activeCanonicalIds.has(anchorId)\n || activeStagingCanonicalIds.has(anchorId)\n || activeBareAnchorIds.has(bareAnchorId)) {\n makeInactive(element);\n } else {\n activeStagingCanonicalIds.add(anchorId);\n activeBareAnchorIds.add(bareAnchorId);\n }\n }\n }\n\n /**\n * Page flow clones source blocks while the hidden staging tree stays in the document. Any HTML\n * fragment target copied into a visible page would therefore resolve to its earlier hidden\n * source. Transfer target ownership to the page presentation after flow is complete; registry\n * and wrapper IDs that have no visible counterpart remain available to pagination internals.\n */\n private transferVisibleFragmentTargets(): void {\n const visibleIds = new Set(Array.from(\n this.containerElement.querySelectorAll<HTMLElement>(\"[id]\"),\n ).map((element) => element.id).filter(Boolean));\n if (visibleIds.size === 0) return;\n for (const source of Array.from(\n this.stagingElement.querySelectorAll<HTMLElement>(\"[id]\"),\n )) {\n if (visibleIds.has(source.id)) source.removeAttribute(\"id\");\n }\n }\n\n /** Read each section's `w:pgNumType` off its wrapper (see {@link SectionPageNumbering}). */\n private parsePageNumbering(sections: ArrayLike<HTMLElement>): Map<number, SectionPageNumbering> {\n const map = new Map<number, SectionPageNumbering>();\n for (const section of Array.from(sections)) {\n const index = parseInt(section.dataset.sectionIndex || \"0\", 10);\n const rawStart = section.dataset.pageNumStart;\n const start = rawStart === undefined ? undefined : parseInt(rawStart, 10);\n map.set(index, {\n start: start !== undefined && Number.isFinite(start) ? start : undefined,\n format: section.dataset.pageNumFmt,\n });\n }\n return map;\n }\n\n /**\n * Fill in the page-number fields inside every page's cloned header/footer.\n *\n * A header/footer is authored once and cloned onto each page, so a PAGE field's single cached\n * result would otherwise show the same number on every page \u2014 the whole reason the converter\n * marks these. `data-field-format` (the field's own `\\*` switch) wins over the section's format\n * when present, which is exactly how Word resolves the two.\n *\n * Runs after layout because NUMPAGES cannot be known before the last page exists. The\n * substituted text can therefore be marginally wider than the cached result the header was\n * measured with; the header band clips, so the failure mode is a hair of overflow rather than\n * a layout that disagrees with itself.\n *\n * Scoped to the CLONED header/footer regions on purpose. A page-number field in body text is\n * ordinary run content that the editor may make editable, and committing an edited block writes\n * back whatever text the DOM holds \u2014 rewriting it here would mean a body field commits a number\n * the document never contained. Body content is also not cloned, so it does not have the problem\n * this method exists to solve.\n */\n private substitutePageNumberFields(totalPages: number): void {\n const boxes = this.containerElement.querySelectorAll<HTMLElement>(`.${this.cssPrefix}box`);\n for (const box of Array.from(boxes)) {\n const markers = box.querySelectorAll<HTMLElement>(\n `.${this.cssPrefix}header [data-field], .${this.cssPrefix}footer [data-field]`,\n );\n if (markers.length === 0) continue;\n\n const sectionIndex = parseInt(box.dataset.sectionIndex || \"0\", 10);\n const pageNumber = parseInt(box.dataset.pageNumber || \"1\", 10);\n const numbering = this.pageNumbering.get(sectionIndex) ?? {};\n const displayed = parseInt(box.dataset.displayedPageNumber || String(pageNumber), 10);\n\n for (const marker of Array.from(markers)) {\n const kind = marker.dataset.field;\n if (kind !== \"PAGE\" && kind !== \"NUMPAGES\") continue;\n const format = marker.dataset.fieldFormat ?? numbering.format;\n marker.textContent = formatPageNumber(kind === \"PAGE\" ? displayed : totalPages, format);\n }\n }\n }\n\n /**\n * Measures all content blocks in a section.\n */\n private measureBlocks(\n section: HTMLElement,\n dims: PageDimensions,\n sectionIndex: number,\n ): MeasuredBlock[] {\n const blocks: MeasuredBlock[] = [];\n\n // Get direct children (paragraphs, tables, divs, etc.)\n const children = Array.from(section.children) as HTMLElement[];\n\n for (const child of children) {\n this.checkpoint();\n // Skip section dividers that are just wrappers\n if (child.dataset.sectionIndex !== undefined) {\n // Recursively get blocks from nested sections\n const nestedSectionIndex = parseInt(child.dataset.sectionIndex || String(sectionIndex), 10);\n const nestedBlocks = this.measureBlocks(child, dims, nestedSectionIndex);\n blocks.push(...nestedBlocks);\n continue;\n }\n\n // Converter-shaped endnotes are a safe nested block structure whose outer\n // section/list wrappers must not make the complete endnote collection one\n // indivisible page block. Flatten only the exact shape we understand; any\n // richer author HTML retains the conservative whole-block fallback below.\n if (child.matches(\"section.endnotes\")) {\n const endnoteBlocks = this.measureSafeEndnoteBlocks(child, dims, sectionIndex);\n if (endnoteBlocks) {\n blocks.push(...endnoteBlocks);\n continue;\n }\n }\n\n // Measure height and margins separately for proper margin collapsing calculation\n // getBoundingClientRect() returns content+padding+border, not margins\n const rect = child.getBoundingClientRect();\n const { topPx: marginTopPx, bottomPx: marginBottomPx } = collapsedBlockMarginsPx(this.view, child);\n const heightPt = pxToPt(rect.height);\n const marginTopPt = pxToPt(marginTopPx);\n const marginBottomPt = pxToPt(marginBottomPx);\n\n const isPageBreak =\n child.dataset.pageBreak === \"true\" ||\n child.classList.contains(`${this.cssPrefix}break`);\n\n blocks.push({\n element: child,\n sectionIndex,\n heightPt,\n marginTopPt,\n marginBottomPt,\n keepWithNext: child.dataset.keepWithNext === \"true\",\n keepLines: child.dataset.keepLines === \"true\",\n pageBreakBefore: child.dataset.pageBreakBefore === \"true\",\n isPageBreak,\n isWordParagraph: this.isWordParagraphElement(child),\n });\n }\n\n return blocks;\n }\n\n /**\n * Flatten the converter's `section.endnotes > ol > li > p` presentation into\n * ordinary paragraph blocks. This preserves paragraph formatting and canonical\n * p:en/en:en identities while allowing the existing paragraph fragmenter to\n * split a long endnote across page boundaries.\n */\n private measureSafeEndnoteBlocks(\n section: HTMLElement,\n dims: PageDimensions,\n sectionIndex: number,\n ): MeasuredBlock[] | null {\n const sectionChildren = Array.from(section.children) as HTMLElement[];\n const list = sectionChildren.find((child) => child.tagName === \"OL\");\n if (\n !list\n || sectionChildren.some((child) => child.tagName !== \"HR\" && child !== list)\n || Array.from(list.children).some((child) => child.tagName !== \"LI\")\n ) {\n return null;\n }\n\n const items = Array.from(list.children) as HTMLElement[];\n if (items.length === 0 || items.some((item) =>\n item.children.length === 0\n || Array.from(item.children).some((child) => child.tagName !== \"P\")\n )) {\n return null;\n }\n\n const blocks: MeasuredBlock[] = [];\n const sectionStyle = this.view.getComputedStyle(section);\n for (const rule of sectionChildren.filter((child) => child.tagName === \"HR\")) {\n this.checkpoint();\n const clonedRule = rule.cloneNode(true) as HTMLElement;\n // The rule leaves its section when flattened, so preserve the section-scoped\n // print styling before measuring and painting the detached clone.\n const ruleStyle = this.view.getComputedStyle(rule);\n for (const property of [\"height\", \"width\", \"max-width\", \"border\", \"background-color\", \"color\",\n \"margin-top\", \"margin-right\", \"margin-bottom\", \"margin-left\"]) {\n clonedRule.style.setProperty(property, ruleStyle.getPropertyValue(property));\n }\n if (blocks.length === 0) {\n clonedRule.style.marginTop = `calc(${sectionStyle.marginTop} + ${ruleStyle.marginTop})`;\n }\n blocks.push(this.measureElement(clonedRule, dims, sectionIndex));\n }\n\n const listStyle = this.view.getComputedStyle(list);\n for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {\n this.checkpoint();\n const item = items[itemIndex];\n const ownerAnchorId = item.dataset.sourceAnchorId;\n const paragraphs = Array.from(item.children) as HTMLElement[];\n for (let paragraphIndex = 0; paragraphIndex < paragraphs.length; paragraphIndex++) {\n this.checkpoint();\n const paragraph = paragraphs[paragraphIndex];\n // The flattened clone no longer has the section/ol/li ancestors that supplied the\n // source's computed layout. Validate while the real paragraph is still attached; the\n // marker below records that completed check for canFragmentParagraph(), whose detached\n // clone cannot obtain meaningful computed styles. A richer custom endnote falls back to\n // the established indivisible section path instead of being range-split incorrectly.\n if (!this.hasRangeFragmentSafeLayout(paragraph)) {\n return null;\n }\n\n const clone = paragraph.cloneNode(true) as HTMLElement;\n clone.dataset.paginationSafeEndnote = \"true\";\n clone.style.fontSize ||= sectionStyle.fontSize;\n clone.style.lineHeight ||= sectionStyle.lineHeight;\n clone.style.paddingLeft ||= listStyle.paddingLeft;\n\n // Older/custom producers may put the endnote identity only on the li.\n // Mirror it into the visible paragraph without displacing the paragraph's\n // own identity, matching current converter output.\n if (ownerAnchorId && !Array.from(\n clone.querySelectorAll<HTMLElement>(\"[data-source-anchor-id]\"),\n ).some((node) => node.dataset.sourceAnchorId === ownerAnchorId)) {\n const owner = this.document.createElement(\"span\");\n owner.dataset.sourceAnchorId = ownerAnchorId;\n while (clone.firstChild) owner.appendChild(clone.firstChild);\n clone.appendChild(owner);\n }\n\n if (paragraphIndex === 0) {\n // Flattening must preserve both ends of the converter's endnote link and the list's\n // numbering format. The outer id survives only on the leading range fragment; normal\n // continuation cleanup removes it from every later fragment.\n const itemId = item.id;\n if (itemId) {\n if (clone.id && clone.id !== itemId) return null;\n clone.id = itemId;\n }\n if (!clone.querySelector('[data-endnote-number]')) {\n const value = parseInt(item.getAttribute(\"value\") || String(itemIndex + 1), 10);\n const marker = this.formatOrderedListMarker(\n Number.isFinite(value) ? value : itemIndex + 1,\n listStyle.listStyleType,\n );\n clone.insertBefore(this.document.createTextNode(`${marker}. `), clone.firstChild);\n }\n }\n blocks.push(this.measureElement(clone, dims, sectionIndex));\n }\n }\n\n return blocks;\n }\n\n /** Render the CSS ordered-list formats emitted by the converter after an endnote is flattened. */\n private formatOrderedListMarker(value: number, listStyleType: string): string {\n const format = (() => {\n switch (listStyleType) {\n case \"lower-roman\": return \"lowerRoman\";\n case \"upper-roman\": return \"upperRoman\";\n case \"lower-alpha\":\n case \"lower-latin\": return \"lowerLetter\";\n case \"upper-alpha\":\n case \"upper-latin\": return \"upperLetter\";\n default: return \"decimal\";\n }\n })();\n return formatPageNumber(value, format);\n }\n\n /**\n * Flows a multi-column (`w:cols`) section's children into CSS-multicol container\n * blocks. Word lays such a section out as N columns inside the same body extent;\n * a `column-count` container reproduces that geometry, and the paginator\n * then places each container as one ordinary measured block. A container grows\n * greedily until it would exceed the smallest page body available to the section.\n * Final continuous sections fill that height sequentially; other sections retain\n * balancing. A long columned section still splits across pages at block\n * boundaries. Each child lands in exactly one container, so anchors never\n * duplicate. An explicit page break child passes through as its own block, which\n * ends the current container and lets the normal flow logic turn the page.\n */\n private buildColumnBlocks(\n section: HTMLElement,\n dims: PageDimensions,\n sectionIndex: number,\n columnCount: number,\n columnGapPt: number,\n sequential = false,\n ): MeasuredBlock[] {\n const children = Array.from(section.children) as HTMLElement[];\n const blocks: MeasuredBlock[] = [];\n const maxFragmentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);\n\n const isBreak = (child: HTMLElement) =>\n child.dataset.pageBreak === \"true\" ||\n child.classList.contains(`${this.cssPrefix}break`);\n\n const makeContainer = (slice: HTMLElement[]): HTMLElement => {\n const container = this.document.createElement(\"div\");\n container.style.columnCount = String(columnCount);\n container.style.columnGap = `${columnGapPt}pt`;\n if (sequential) {\n container.dataset.paginationSequentialColumns = \"true\";\n container.style.columnFill = \"auto\";\n container.style.maxHeight = `${maxFragmentHeight}pt`;\n }\n for (const child of slice) {\n container.appendChild(child.cloneNode(true));\n }\n if (sequential) this.liftColumnSpaceBefore(container, dims);\n return container;\n };\n\n let start = 0;\n while (start < children.length) {\n this.checkpoint();\n if (isBreak(children[start])) {\n blocks.push(this.measureElement(children[start], dims, sectionIndex));\n start++;\n continue;\n }\n\n // Grow the container one child at a time. Even a single oversized child is\n // emitted alone, preserving the established oversized-block fallback.\n let end = start + 1;\n let container = makeContainer(children.slice(start, end));\n if (sequential && this.columnContainerOverflows(container, dims)) {\n // An indivisible child that exceeds all available columns keeps the established\n // oversized-block path; a constrained box must not hide it in an extra column.\n delete container.dataset.paginationSequentialColumns;\n container.style.columnFill = \"balance\";\n container.style.maxHeight = \"\";\n }\n let measured = this.measureElement(container, dims, sectionIndex);\n while (end < children.length && !isBreak(children[end])) {\n this.checkpoint();\n const candidate = makeContainer(children.slice(start, end + 1));\n const candidateMeasured = this.measureElement(candidate, dims, sectionIndex);\n if (candidateMeasured.heightPt > maxFragmentHeight\n || (sequential && this.columnContainerOverflows(candidate, dims))) break;\n container = candidate;\n measured = candidateMeasured;\n end++;\n }\n\n blocks.push({\n element: container,\n sectionIndex,\n heightPt: measured.heightPt,\n marginTopPt: measured.marginTopPt,\n marginBottomPt: measured.marginBottomPt,\n keepWithNext: false,\n keepLines: false,\n pageBreakBefore: false,\n isPageBreak: false,\n isWordParagraph: false,\n });\n start = end;\n }\n\n return blocks;\n }\n\n /** Keep Word's paragraph space-before in the ordinary inter-block spacing budget. */\n private liftColumnSpaceBefore(container: HTMLElement, dims: PageDimensions): void {\n container.style.marginTop = \"\";\n const first = container.firstElementChild as HTMLElement | null;\n if (!first || !this.isWordParagraphElement(first)) return;\n const host = this.document.createElement(\"div\");\n host.style.width = `${dims.contentWidth}pt`;\n host.appendChild(container);\n this.stagingElement.appendChild(host);\n try {\n const spaceBefore = first.dataset.paginationColumnSpaceBefore\n ?? this.view.getComputedStyle(first).marginTop;\n first.dataset.paginationColumnSpaceBefore = spaceBefore;\n first.style.marginTop = \"0\";\n container.style.marginTop = spaceBefore;\n } finally {\n container.remove();\n host.remove();\n }\n }\n\n /** A height-constrained multicol box overflows horizontally when it needs another column. */\n private columnContainerOverflows(element: HTMLElement, dims: PageDimensions): boolean {\n const host = this.document.createElement(\"div\");\n host.style.width = `${dims.contentWidth}pt`;\n const clone = element.cloneNode(true) as HTMLElement;\n host.appendChild(clone);\n this.stagingElement.appendChild(host);\n try {\n if (clone.scrollWidth > clone.clientWidth + 1) return true;\n // Keep-lines paragraphs must fit as a whole. A constrained column container\n // can report only its first partial box, even when scrollWidth still fits.\n // Move that source paragraph to the next page before print clips its text.\n return Array.from(clone.querySelectorAll<HTMLElement>('[data-keep-lines=\"true\"]'))\n .some(paragraph => paragraph.getClientRects().length > 1);\n } finally {\n host.remove();\n }\n }\n\n /**\n * Reflow a final continuous column section against the actual remaining body band.\n * A preceding title, running story or note reservation can make it shorter than the\n * fresh-page band used while collecting source blocks. Split at source-child boundaries\n * when the narrower band would create an extra horizontal column; the tail is reconsidered\n * normally on the following page. No source child is duplicated or discarded.\n */\n private reflowSequentialColumns(\n block: MeasuredBlock,\n dims: PageDimensions,\n availableHeightPt: number,\n ): MeasuredBlock[] | null {\n if (block.element.dataset.paginationSequentialColumns !== \"true\"\n || availableHeightPt <= 0) return null;\n const maximumHeight = this.smallestEffectiveContentHeight(dims, block.sectionIndex);\n const height = Math.min(availableHeightPt, maximumHeight);\n if (Math.abs(parseFloat(block.element.style.maxHeight) - height) < 0.01) return null;\n const children = Array.from(block.element.children) as HTMLElement[];\n const make = (slice: HTMLElement[], limit: number): HTMLElement => {\n const container = block.element.cloneNode(false) as HTMLElement;\n container.style.maxHeight = `${limit}pt`;\n for (const child of slice) container.appendChild(child.cloneNode(true));\n this.liftColumnSpaceBefore(container, dims);\n return container;\n };\n const whole = make(children, height);\n if (!this.columnContainerOverflows(whole, dims)) {\n return [this.measureElement(whole, dims, block.sectionIndex)];\n }\n let end = 0;\n for (let count = 1; count < children.length; count++) {\n this.checkpoint();\n const candidate = make(children.slice(0, count), height);\n if (this.columnContainerOverflows(candidate, dims)) break;\n if (children[count - 1].dataset.keepWithNext !== \"true\") end = count;\n }\n if (end === 0) return null;\n return [\n this.measureElement(make(children.slice(0, end), height), dims, block.sectionIndex),\n this.measureElement(make(children.slice(end), maximumHeight), dims, block.sectionIndex),\n ];\n }\n\n /**\n * Measures one element in the same hidden staging context used for the source blocks.\n * This is intentionally DOM-based: table row heights cannot be inferred from individual\n * rows because wrapping and collapsed borders change the height of a fragment.\n */\n private measureElement(\n element: HTMLElement,\n dims: PageDimensions,\n sectionIndex: number,\n ): MeasuredBlock {\n const measurementHost = this.document.createElement(\"div\");\n measurementHost.style.position = \"absolute\";\n measurementHost.style.visibility = \"hidden\";\n measurementHost.style.left = \"-9999px\";\n measurementHost.style.width = `${dims.contentWidth}pt`;\n\n const measuredElement = element.cloneNode(true) as HTMLElement;\n measurementHost.appendChild(measuredElement);\n this.stagingElement.appendChild(measurementHost);\n\n const rect = measuredElement.getBoundingClientRect();\n const margins = collapsedBlockMarginsPx(this.view, measuredElement);\n const measured: MeasuredBlock = {\n element,\n sectionIndex,\n heightPt: pxToPt(rect.height),\n marginTopPt: pxToPt(margins.topPx),\n marginBottomPt: pxToPt(margins.bottomPx),\n keepWithNext: element.dataset.keepWithNext === \"true\",\n keepLines: element.dataset.keepLines === \"true\",\n pageBreakBefore: element.dataset.pageBreakBefore === \"true\",\n isPageBreak:\n element.dataset.pageBreak === \"true\" ||\n element.classList.contains(`${this.cssPrefix}break`),\n isWordParagraph: this.isWordParagraphElement(element),\n };\n\n this.stagingElement.removeChild(measurementHost);\n return measured;\n }\n\n /**\n * Returns the contiguous keep-with-next chain beginning at a block.\n *\n * A hard page break or a page-break-before directive is stronger than a\n * keep-with-next directive, so it terminates the chain. The caller only\n * keeps a chain together when the whole chain can fit on a fresh page.\n */\n private getKeepWithNextChain(blocks: MeasuredBlock[], startIndex: number): MeasuredBlock[] {\n const firstBlock = blocks[startIndex];\n if (!firstBlock) return [];\n\n const chain = [firstBlock];\n let lastIndex = startIndex;\n\n while (blocks[lastIndex].keepWithNext) {\n const nextBlock = blocks[lastIndex + 1];\n if (!nextBlock || nextBlock.isPageBreak || nextBlock.pageBreakBefore) {\n break;\n }\n\n chain.push(nextBlock);\n lastIndex++;\n }\n\n return chain;\n }\n\n /**\n * Measures the visible body height of a keep-with-next chain using the same\n * collapsed-margin rules as normal block placement. The trailing margin is\n * intentionally excluded, matching the individual block fit check.\n */\n private measureKeepWithNextChainBodyHeight(\n chain: MeasuredBlock[],\n previousMarginBottomPt: number,\n isFirstOnPage: boolean,\n pageInSection: number\n ): number {\n const firstBlock = chain[0];\n if (!firstBlock) return 0;\n\n const firstMarginTop = this.effectiveBlockMarginTop(\n firstBlock,\n previousMarginBottomPt,\n isFirstOnPage,\n pageInSection\n );\n let bodyHeight = firstMarginTop + firstBlock.heightPt;\n\n for (let index = 1; index < chain.length; index++) {\n const previousBlock = chain[index - 1];\n const block = chain[index];\n bodyHeight += Math.max(previousBlock.marginBottomPt, block.marginTopPt) + block.heightPt;\n }\n\n return bodyHeight;\n }\n\n /** Word paragraphs render as `p`, or as `h1`\u2013`h6` when their style has an outline level. */\n private isWordParagraphElement(element: HTMLElement): boolean {\n return element.tagName === \"P\" || /^H[1-6]$/.test(element.tagName);\n }\n\n private shouldSuppressPageTopSpacing(\n block: MeasuredBlock,\n isFirstOnPage: boolean,\n pageInSection: number\n ): boolean {\n return isFirstOnPage && pageInSection > 1 && block.isWordParagraph === true;\n }\n\n /**\n * Resolves the part of a block's top margin that consumes the current page.\n *\n * In Word's native DOCX layout, paragraph space-before is suppressed when a paragraph is the\n * first body block on a later page of the SAME section. The first page of a document/section is\n * the exception and keeps its spacing. Tables and other block margins are not paragraph spacing,\n * so they continue to use the ordinary CSS collapsing rule.\n */\n private effectiveBlockMarginTop(\n block: MeasuredBlock,\n previousMarginBottomPt: number,\n isFirstOnPage: boolean,\n pageInSection: number\n ): number {\n if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {\n return 0;\n }\n return isFirstOnPage\n ? block.marginTopPt\n : Math.max(block.marginTopPt, previousMarginBottomPt) - previousMarginBottomPt;\n }\n\n /** Clone a source block with the same page-top spacing decision used by the height budget. */\n private cloneBlockForPage(\n block: MeasuredBlock,\n isFirstOnPage: boolean,\n pageInSection: number\n ): HTMLElement {\n const clone = block.element.cloneNode(true) as HTMLElement;\n if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {\n clone.style.setProperty(\"margin-top\", \"0\", \"important\");\n }\n return clone;\n }\n\n /**\n * Finds footnote references introduced by a sequence of blocks, preserving\n * document order and excluding references already assigned to the page.\n */\n private collectNewFootnoteIds(\n blocks: MeasuredBlock[],\n existingFootnoteIds: string[]\n ): string[] {\n const knownIds = new Set(existingFootnoteIds);\n const newIds: string[] = [];\n\n for (const block of blocks) {\n this.checkpoint();\n for (const id of this.extractFootnoteRefs(block.element)) {\n if (!knownIds.has(id)) {\n knownIds.add(id);\n newIds.push(id);\n }\n }\n }\n\n return newIds;\n }\n\n /**\n * The shortest body available to a section's first, default, or even page.\n * A row fragment must fit every variant, otherwise a later header/footer could\n * send it through the oversized-block fallback again.\n */\n private smallestEffectiveContentHeight(dims: PageDimensions, sectionIndex: number): number {\n return Math.min(\n this.getPageBands(dims, sectionIndex, 1, 1).bodyHeight,\n this.getPageBands(dims, sectionIndex, 2, 2).bodyHeight,\n this.getPageBands(dims, sectionIndex, 3, 3).bodyHeight\n );\n }\n\n /**\n * Builds a clone of a simple table wrapper containing a contiguous run of rows.\n * Complex table features are deliberately rejected by the caller: a split across\n * vertically merged cells (rowspan), nested tables, or footnotes cannot be made\n * correct by cloning rows. Horizontal merges (colspan) need no rejection: a colspan\n * is confined to its own row, and rows are only ever cloned whole.\n */\n private createSimpleTableFragment(\n wrapper: HTMLElement,\n table: HTMLTableElement,\n body: HTMLTableSectionElement,\n rows: HTMLTableRowElement[],\n retainAnchor: boolean\n ): HTMLElement {\n const wrapperClone = wrapper.cloneNode(false) as HTMLElement;\n const tableClone = table.cloneNode(false) as HTMLTableElement;\n\n // The eligibility gate permits only colgroups alongside the body. Keep each\n // colgroup so fixed and proportional column widths remain stable per fragment.\n for (const child of Array.from(table.children)) {\n if (child !== body) {\n tableClone.appendChild(child.cloneNode(true));\n }\n }\n\n const bodyClone = body.cloneNode(false) as HTMLTableSectionElement;\n for (const row of rows) {\n bodyClone.appendChild(row.cloneNode(true));\n }\n tableClone.appendChild(bodyClone);\n\n if (!retainAnchor) {\n wrapperClone.removeAttribute(\"data-anchor\");\n tableClone.removeAttribute(\"data-anchor\");\n }\n\n wrapperClone.appendChild(tableClone);\n return wrapperClone;\n }\n\n /**\n * Splits an oversized, ordinary table at row boundaries. This only participates\n * in the existing oversized-block fallback; unsupported tables keep the previous\n * overflow behavior rather than risking broken table semantics.\n */\n private trySplitSimpleOversizedTable(\n block: MeasuredBlock,\n dims: PageDimensions,\n sectionIndex: number\n ): MeasuredBlock[] | null {\n const wrapper = block.element;\n if (\n wrapper.tagName !== \"DIV\" ||\n wrapper.children.length !== 1 ||\n block.keepWithNext ||\n block.keepLines ||\n block.pageBreakBefore ||\n block.isPageBreak\n ) {\n return null;\n }\n\n const tableElement = wrapper.firstElementChild;\n if (!tableElement || tableElement.localName !== \"table\") {\n return null;\n }\n const table = tableElement as HTMLTableElement;\n\n const body = table.tBodies.length === 1 ? table.tBodies[0] : null;\n if (\n !body ||\n table.tHead ||\n table.tFoot ||\n body.rows.length < 2 ||\n Array.from(table.children).some(child => child !== body && child.tagName !== \"COLGROUP\") ||\n table.querySelector(\"table, [rowspan], [data-footnote-id]\") ||\n wrapper.querySelector(\"[data-footnote-id]\")\n ) {\n return null;\n }\n\n const rows = Array.from(body.rows);\n const minimumContentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);\n // Use the source's full vertical margins while forming groups. Continuation\n // fragments later clear their joining margins, so this conservative bound\n // cannot create a fragment that overflows a header/footer variant.\n const maximumFragmentHeight = minimumContentHeight - block.marginTopPt - block.marginBottomPt;\n if (maximumFragmentHeight <= 0) {\n return null;\n }\n\n const groups: HTMLTableRowElement[][] = [];\n let start = 0;\n while (start < rows.length) {\n this.checkpoint();\n let end = start;\n while (end < rows.length) {\n this.checkpoint();\n const candidate = this.createSimpleTableFragment(\n wrapper,\n table,\n body,\n rows.slice(start, end + 1),\n start === 0\n );\n const measured = this.measureElement(candidate, dims, block.sectionIndex);\n if (measured.heightPt > maximumFragmentHeight) {\n break;\n }\n end++;\n }\n\n // Even a one-row fragment cannot fit. Preserve the established overflow\n // fallback rather than looping or clipping a partially split row.\n if (end === start) {\n return null;\n }\n\n groups.push(rows.slice(start, end));\n start = end;\n }\n\n if (groups.length < 2) {\n return null;\n }\n\n const fragments: MeasuredBlock[] = [];\n for (let index = 0; index < groups.length; index++) {\n const isFirst = index === 0;\n const isLast = index === groups.length - 1;\n const fragment = this.createSimpleTableFragment(\n wrapper,\n table,\n body,\n groups[index],\n isFirst\n );\n\n // Keep the source's outer spacing only at the table's real boundaries.\n // Continuation margins would otherwise add blank space at the top/bottom\n // of every paginated fragment.\n if (!isFirst) {\n fragment.style.setProperty(\"margin-top\", \"0\", \"important\");\n }\n if (!isLast) {\n fragment.style.setProperty(\"margin-bottom\", \"0\", \"important\");\n }\n\n const measured = this.measureElement(fragment, dims, block.sectionIndex);\n if (\n measured.heightPt + measured.marginTopPt + measured.marginBottomPt >\n minimumContentHeight\n ) {\n return null;\n }\n\n fragments.push({\n ...measured,\n keepWithNext: false,\n keepLines: false,\n pageBreakBefore: false,\n isPageBreak: false,\n });\n }\n\n return fragments;\n }\n\n /**\n * DOM endpoints that can finish a paragraph fragment. The flattened UTF-16\n * offsets are checked against the browser's Unicode grapheme segmenter, so a\n * formatting-run boundary can never bisect a surrogate pair, combining\n * sequence, or joined emoji. NBSP/word-joiner boundaries remain indivisible.\n * PAGE/NUMPAGES field results are atomic because splitting their marker would\n * make later substitution duplicate or replace only half of the field.\n */\n private paragraphFragmentEndpoints(\n paragraph: HTMLElement,\n emergencyGraphemeBreaks = false,\n ): ParagraphFragmentEndpoint[] {\n const textNodes: Text[] = [];\n const textStarts = new Map<Text, number>();\n const lastTextByField = new Map<HTMLElement, Text>();\n const walker = this.document.createTreeWalker(paragraph, this.view.NodeFilter.SHOW_TEXT);\n const flattenedChunks: string[] = [];\n let flattenedLength = 0;\n let textNode: Text | null;\n while ((textNode = walker.nextNode() as Text | null)) {\n this.checkpoint();\n textStarts.set(textNode, flattenedLength);\n textNodes.push(textNode);\n flattenedChunks.push(textNode.data);\n flattenedLength += textNode.data.length;\n let field = textNode.parentElement?.closest<HTMLElement>(\"[data-field]\") ?? null;\n while (field?.parentElement?.closest<HTMLElement>(\"[data-field]\")) {\n field = field.parentElement.closest<HTMLElement>(\"[data-field]\");\n }\n if (field && paragraph.contains(field)) lastTextByField.set(field, textNode);\n }\n const flattenedText = flattenedChunks.join(\"\");\n if (!this.hasVisibleText(flattenedText)) return [];\n\n const invisible = /[\\s\\u200B-\\u200F\\uFEFF]/;\n let firstVisibleOffset = -1;\n let lastVisibleOffset = -1;\n for (let index = 0; index < flattenedText.length; index++) {\n if (index % 4096 === 0) this.checkpoint();\n if (invisible.test(flattenedText[index])) continue;\n if (firstVisibleOffset < 0) firstVisibleOffset = index;\n lastVisibleOffset = index;\n }\n\n const graphemeBoundaries = this.graphemeBoundaryOffsets(flattenedText);\n const candidates: ParagraphFragmentEndpoint[] = [];\n const addCandidate = (candidate: ParagraphFragmentEndpoint): void => {\n const { textOffset } = candidate;\n if (textOffset <= 0 || textOffset >= flattenedText.length) return;\n if (textOffset <= firstVisibleOffset || textOffset > lastVisibleOffset) return;\n if (this.isNonBreakingTextBoundary(flattenedText, textOffset)) return;\n if (!this.isLegalParagraphLineBoundary(paragraph, flattenedText, textOffset)) return;\n if (graphemeBoundaries) {\n if (!graphemeBoundaries.has(textOffset)) return;\n } else if (!this.isConservativeFallbackTextBoundary(flattenedText, textOffset)) {\n return;\n }\n candidates.push(candidate);\n };\n\n for (const node of textNodes) {\n this.checkpoint();\n const start = textStarts.get(node)!;\n // Field results and converter list markers are semantic atoms. A field\n // receives one boundary outside its wrapper below; a list marker stays\n // with the first real text fragment and is never emitted by itself.\n const atomic = node.parentElement?.closest<HTMLElement>(\n \"[data-field], [data-list-marker], a[data-comment-id]\",\n );\n if (atomic && paragraph.contains(atomic)) continue;\n\n // Only CSS-collapsible ASCII whitespace is a wrapping opportunity. JS's\n // broader `\\s` class includes NBSP and other explicitly non-breaking text.\n const whitespace = /[\\u0009-\\u000D\\u0020]+/g;\n let match: RegExpExecArray | null;\n let matchesSinceCheckpoint = 0;\n while ((match = whitespace.exec(node.data)) !== null) {\n if (++matchesSinceCheckpoint % 256 === 0) this.checkpoint();\n const offset = match.index + match[0].length;\n addCandidate({ node, offset, textOffset: start + offset, priority: 0 });\n }\n addCandidate({\n node,\n offset: node.data.length,\n textOffset: start + node.data.length,\n priority: 0,\n });\n if (emergencyGraphemeBreaks) {\n for (let offset = 1; offset < node.data.length; offset++) {\n if (offset % 256 === 0) this.checkpoint();\n addCandidate({\n node,\n offset,\n textOffset: start + offset,\n priority: -1,\n });\n }\n }\n }\n\n // A field is splittable only immediately after its complete outer wrapper.\n // Starting the tail at the parent's child boundary prevents Range from\n // cloning an empty `[data-field]` shell that substitution could repopulate.\n for (const field of Array.from(paragraph.querySelectorAll<HTMLElement>(\"[data-field]\"))) {\n this.checkpoint();\n if (field.parentElement?.closest(\"[data-field]\")) continue;\n const last = lastTextByField.get(field);\n const parent = field.parentNode;\n if (!last || !parent) continue;\n const childIndex = Array.prototype.indexOf.call(parent.childNodes, field);\n if (childIndex < 0) continue;\n addCandidate({\n node: parent,\n offset: childIndex + 1,\n textOffset: textStarts.get(last)! + last.data.length,\n priority: 1,\n });\n }\n\n // Several DOM boundaries can represent the same flattened offset. Prefer\n // the outer atomic boundary, then keep one stable document-order candidate.\n candidates.sort((left, right) =>\n left.textOffset - right.textOffset || right.priority - left.priority);\n const endpoints: ParagraphFragmentEndpoint[] = [];\n for (const candidate of candidates) {\n if (endpoints[endpoints.length - 1]?.textOffset !== candidate.textOffset) {\n endpoints.push(candidate);\n }\n }\n return endpoints;\n }\n\n /** Browser-native UAX #29 boundaries; null keeps older runtimes conservative. */\n private graphemeBoundaryOffsets(text: string): Set<number> | null {\n type Segment = { segment: string; index: number };\n type Segmenter = new (\n locale?: string,\n options?: { granularity: \"grapheme\" },\n ) => { segment(value: string): Iterable<Segment> };\n const SegmenterConstructor = (this.view.Intl as typeof Intl & {\n Segmenter?: Segmenter;\n }).Segmenter;\n if (!SegmenterConstructor) return null;\n\n const boundaries = new Set<number>([0, text.length]);\n let segmentCount = 0;\n for (const part of new SegmenterConstructor(\"en\", { granularity: \"grapheme\" }).segment(text)) {\n if (++segmentCount % 256 === 0) this.checkpoint();\n boundaries.add(part.index);\n boundaries.add(part.index + part.segment.length);\n }\n return boundaries;\n }\n\n /**\n * Conservative UAX #14/CSS wrapping opportunities used for synthetic page\n * boundaries. In particular, a formatting-run boundary is not itself a word\n * boundary, and Japanese opening/closing punctuation stays with its pair.\n * Arbitrary grapheme breaks are admitted only when the paragraph's CSS asks\n * the browser to wrap anywhere.\n */\n private isLegalParagraphLineBoundary(\n paragraph: HTMLElement,\n text: string,\n offset: number,\n ): boolean {\n const lastCodeUnit = text.charCodeAt(offset - 1);\n const beforeOffset = lastCodeUnit >= 0xDC00 && lastCodeUnit <= 0xDFFF\n ? Math.max(0, offset - 2)\n : offset - 1;\n const beforeCodePoint = text.codePointAt(beforeOffset);\n const afterCodePoint = text.codePointAt(offset);\n const before = beforeCodePoint === undefined ? \"\" : String.fromCodePoint(beforeCodePoint);\n const after = afterCodePoint === undefined ? \"\" : String.fromCodePoint(afterCodePoint);\n if (/^[\\u0009-\\u000D\\u0020]$/.test(before)\n || /^[\\u0009-\\u000D\\u0020]$/.test(after)) return true;\n if (/[-\\u00AD\\u2010]$/u.test(before)) return true;\n\n const style = this.view.getComputedStyle(paragraph);\n if (style.wordBreak === \"break-all\"\n || style.overflowWrap === \"anywhere\"\n || style.overflowWrap === \"break-word\") return true;\n\n const eastAsian = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u;\n if (!eastAsian.test(before) && !eastAsian.test(after)) return false;\n const opening = \"([{<\\u2018\\u201C\\u3008\\u300A\\u300C\\u300E\\u3010\\u3014\\u3016\\u3018\\u301A\\uFF08\\uFF3B\\uFF5B\";\n const closing = \")]}>\\u2019\\u201D\\u3001\\u3002\\u3009\\u300B\\u300D\\u300F\\u3011\\u3015\\u3017\\u3019\\u301B\\uFF01\\uFF05\\uFF09\\uFF0C\\uFF0E\\uFF1A\\uFF1B\\uFF1F\\uFF3D\\uFF5D\";\n return !opening.includes(before) && !closing.includes(after);\n }\n\n /** Never fragment across characters whose line-break meaning is explicitly non-breaking. */\n private isNonBreakingTextBoundary(text: string, offset: number): boolean {\n const codePointBefore = (index: number): number | undefined => {\n if (index <= 0) return undefined;\n const last = text.charCodeAt(index - 1);\n const start = last >= 0xDC00 && last <= 0xDFFF ? index - 2 : index - 1;\n return text.codePointAt(Math.max(0, start));\n };\n const nonBreaking = new Set([\n 0x00A0, 0x200E, 0x200F, 0x2011, 0x202A, 0x202B, 0x202C,\n 0x202D, 0x202E, 0x202F, 0x2060, 0x2066, 0x2067, 0x2068,\n 0x2069, 0xFEFF,\n ]);\n return nonBreaking.has(codePointBefore(offset) ?? -1)\n || nonBreaking.has(text.codePointAt(offset) ?? -1);\n }\n\n /**\n * Without Intl.Segmenter, admit only an all-ASCII boundary. This still\n * fragments ordinary prose while refusing to guess about Unicode clusters.\n */\n private isConservativeFallbackTextBoundary(text: string, offset: number): boolean {\n return text.charCodeAt(offset - 1) <= 0x7F && text.charCodeAt(offset) <= 0x7F;\n }\n\n /**\n * Whether a range contains visible text after ignoring bidi/zero-width marks.\n * Paragraph fragmentation deliberately excludes non-textual descendants, so\n * this is enough to reject empty head or tail fragments.\n */\n private hasVisibleFragmentText(fragment: DocumentFragment): boolean {\n return this.hasVisibleText(fragment.textContent || \"\");\n }\n\n private hasVisibleText(text: string): boolean {\n return text\n .replace(/[\\u200B-\\u200F\\uFEFF]/g, \"\")\n .trim()\n .length > 0;\n }\n\n /**\n * Phase one only handles text-like paragraph descendants. Objects, explicit\n * line breaks, list markers, notes, and out-of-flow/inline-block content all\n * require their own line-layout rules and retain the established whole-block\n * fallback instead of risking broken content or duplicate anchors.\n */\n private canFragmentParagraph(block: MeasuredBlock): boolean {\n if (!this.fragmentParagraphs) {\n return false;\n }\n\n const paragraph = block.element;\n if (\n block.keepWithNext ||\n block.keepLines ||\n block.pageBreakBefore ||\n block.isPageBreak ||\n paragraph.dataset.widowControl === \"true\" ||\n paragraph.hasAttribute(\"contenteditable\")\n ) {\n return false;\n }\n\n return this.canRangeFragmentParagraph(paragraph);\n }\n\n /**\n * Shared structural gate for body and note paragraph fragmentation. Footnote\n * first paragraphs are inline beside their marker, so that one known layout\n * context may opt into an inline root while retaining every descendant and\n * break-safety restriction used for body text.\n */\n private canRangeFragmentParagraph(\n paragraph: HTMLElement,\n allowInlineRoot: boolean = false,\n ): boolean {\n if (\n paragraph.tagName !== \"P\" ||\n paragraph.dataset.keepWithNext === \"true\" ||\n paragraph.dataset.keepLines === \"true\" ||\n paragraph.dataset.pageBreakBefore === \"true\" ||\n paragraph.dataset.widowControl === \"true\" ||\n paragraph.hasAttribute(\"contenteditable\")\n ) {\n return false;\n }\n\n // Non-textual/out-of-flow descendants still need dedicated fragmenters.\n // Inline ids/editor anchors are reconciled after Range cloning, and list\n // markers are kept atomically with the leading text fragment.\n const unsupportedDescendants = [\n \"br\",\n \"img\",\n \"picture\",\n \"svg\",\n \"math\",\n \"canvas\",\n \"video\",\n \"audio\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"input\",\n \"button\",\n \"select\",\n \"textarea\",\n \"table\",\n \"ol\",\n \"ul\",\n \"li\",\n \"dl\",\n \"div\",\n \"p\",\n \"section\",\n \"article\",\n \"aside\",\n \"figure\",\n \"fieldset\",\n \"details\",\n \"[data-footnote-id]\",\n \"[contenteditable]\",\n ].join(\", \");\n if (paragraph.querySelector(unsupportedDescendants)) {\n return false;\n }\n\n const isValidatedEndnote = paragraph.dataset.paginationSafeEndnote === \"true\";\n return isValidatedEndnote || this.hasRangeFragmentSafeLayout(paragraph, allowInlineRoot);\n }\n\n /**\n * A range clone preserves nested inline formatting exactly. Anything that establishes its own\n * box/layout context is deferred until a future fragmenter can model it accurately. Callers\n * must invoke this while the paragraph is attached to the styled document.\n */\n private hasRangeFragmentSafeLayout(\n paragraph: HTMLElement,\n allowInlineRoot: boolean = false,\n ): boolean {\n const paragraphStyle = this.view.getComputedStyle(paragraph);\n if (\n (paragraphStyle.display !== \"block\" &&\n !(allowInlineRoot && paragraphStyle.display === \"inline\")) ||\n paragraphStyle.position !== \"static\" ||\n paragraphStyle.float !== \"none\" ||\n (paragraphStyle.whiteSpace !== \"normal\" && paragraphStyle.whiteSpace !== \"pre-wrap\") ||\n paragraphStyle.breakBefore !== \"auto\" ||\n paragraphStyle.breakAfter !== \"auto\" ||\n paragraphStyle.breakInside === \"avoid\" ||\n paragraphStyle.pageBreakBefore !== \"auto\" ||\n paragraphStyle.pageBreakAfter !== \"auto\" ||\n paragraphStyle.pageBreakInside === \"avoid\"\n ) {\n return false;\n }\n\n for (const descendant of Array.from(paragraph.querySelectorAll<HTMLElement>(\"*\"))) {\n const style = this.view.getComputedStyle(descendant);\n if (\n style.display !== \"inline\" ||\n style.position !== \"static\" ||\n style.float !== \"none\" ||\n (style.whiteSpace !== \"normal\" && style.whiteSpace !== \"pre-wrap\")\n ) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Builds one range-cloned paragraph fragment. Only the leading fragment keeps\n * the source paragraph's addressability; continuations must not duplicate an\n * id/data-anchor in the rendered document.\n */\n private createParagraphFragment(\n paragraph: HTMLElement,\n range: Range,\n retainSourceIdentity: boolean,\n isFinalFragment: boolean\n ): HTMLElement {\n const fragment = paragraph.cloneNode(false) as HTMLElement;\n const contents = range.cloneContents();\n fragment.appendChild(contents);\n\n if (!retainSourceIdentity) {\n fragment.removeAttribute(\"id\");\n fragment.removeAttribute(\"data-anchor\");\n // A continuation starts with a normal line rather than repeating a first-\n // line/hanging indent. Keep the side margin so paragraph alignment remains.\n fragment.style.setProperty(\"margin-top\", \"0\", \"important\");\n fragment.style.setProperty(\"text-indent\", \"0\", \"important\");\n }\n if (!isFinalFragment) {\n // The original bottom spacing belongs after the complete paragraph, not\n // between synthetic page fragments.\n fragment.style.setProperty(\"margin-bottom\", \"0\", \"important\");\n }\n\n return fragment;\n }\n\n /**\n * Range clones repeat an inline ancestor when the split lands inside it. Keep\n * semantic wrappers (links, comments, formatting) on both sides, but retain a\n * duplicated HTML/editor identity only on the leading fragment. Targets that\n * occur wholly after the split are absent from `head` and remain on `tail`.\n */\n private reconcileParagraphFragmentIdentities(\n head: HTMLElement,\n tail: HTMLElement,\n ): void {\n const elements = (root: HTMLElement, selector: string): HTMLElement[] => [\n ...(root.matches(selector) ? [root] : []),\n ...Array.from(root.querySelectorAll<HTMLElement>(selector)),\n ];\n\n const headIds = new Set(elements(head, \"[id]\").map((element) => element.id));\n for (const element of elements(tail, \"[id]\")) {\n if (headIds.has(element.id)) element.removeAttribute(\"id\");\n }\n\n const headAnchors = new Set(elements(head, \"[data-anchor]\")\n .map((element) => element.dataset.anchor)\n .filter((anchor): anchor is string => Boolean(anchor)));\n for (const element of elements(tail, \"[data-anchor]\")) {\n if (!element.dataset.anchor || !headAnchors.has(element.dataset.anchor)) continue;\n element.removeAttribute(\"data-anchor\");\n element.removeAttribute(\"data-committed-text\");\n }\n }\n\n /**\n * Range-clone the largest safe prefix accepted by `fits`. The measurement\n * policy stays with the caller, allowing body blocks and note bands to share\n * one DOM fragmenter while measuring in their respective layout contexts.\n */\n private splitParagraphAtLargestFit(\n paragraph: HTMLElement,\n fits: (head: HTMLElement) => boolean,\n options: {\n emergencyGraphemeBreaks?: boolean;\n forceFirstOnNoFit?: boolean;\n } = {},\n ): ParagraphSplitAttempt {\n const largestFit = (endpoints: ParagraphFragmentEndpoint[]) => {\n // Prefix height is usually monotone, but selector-sensitive inline CSS\n // (`:last-child`, `:only-child`) can make a longer Range clone shorter.\n // Probe the largest legal endpoint first so the common non-monotone case\n // cannot be discarded by the binary search below.\n const lastEndpoint = endpoints[endpoints.length - 1];\n if (lastEndpoint) {\n const lastRange = this.document.createRange();\n lastRange.setStart(paragraph, 0);\n lastRange.setEnd(lastEndpoint.node, lastEndpoint.offset);\n const lastContents = lastRange.cloneContents();\n if (this.hasVisibleFragmentText(lastContents)) {\n const lastHead = this.createParagraphFragment(paragraph, lastRange, true, false);\n if (fits(lastHead)) return { endpoint: lastEndpoint, head: lastHead };\n }\n }\n\n let low = 0;\n let high = endpoints.length - 2;\n let best: { endpoint: ParagraphFragmentEndpoint; head: HTMLElement } | null = null;\n while (low <= high) {\n this.checkpoint();\n const middle = Math.floor((low + high) / 2);\n const endpoint = endpoints[middle];\n const headRange = this.document.createRange();\n headRange.setStart(paragraph, 0);\n headRange.setEnd(endpoint.node, endpoint.offset);\n const headContents = headRange.cloneContents();\n if (!this.hasVisibleFragmentText(headContents)) {\n low = middle + 1;\n continue;\n }\n\n const head = this.createParagraphFragment(paragraph, headRange, true, false);\n if (fits(head)) {\n best = { endpoint, head };\n low = middle + 1;\n } else {\n high = middle - 1;\n }\n }\n return best;\n };\n\n let endpoints = this.paragraphFragmentEndpoints(paragraph);\n let best = largestFit(endpoints);\n if (!best && options.emergencyGraphemeBreaks) {\n endpoints = this.paragraphFragmentEndpoints(paragraph, true);\n best = largestFit(endpoints);\n }\n if (!best && options.forceFirstOnNoFit && endpoints.length > 0) {\n const endpoint = endpoints[0];\n const headRange = this.document.createRange();\n headRange.setStart(paragraph, 0);\n headRange.setEnd(endpoint.node, endpoint.offset);\n best = {\n endpoint,\n head: this.createParagraphFragment(paragraph, headRange, true, false),\n };\n }\n if (!best) {\n return endpoints.length > 0 ? { kind: \"no-fit\" } : { kind: \"indivisible\" };\n }\n\n const tailRange = this.document.createRange();\n tailRange.setStart(best.endpoint.node, best.endpoint.offset);\n tailRange.setEnd(paragraph, paragraph.childNodes.length);\n const tailContents = tailRange.cloneContents();\n if (!this.hasVisibleFragmentText(tailContents)) return { kind: \"indivisible\" };\n\n const tail = this.createParagraphFragment(paragraph, tailRange, false, true);\n this.reconcileParagraphFragmentIdentities(best.head, tail);\n\n return {\n kind: \"split\",\n head: best.head,\n tail,\n };\n }\n\n /**\n * Splits a simple paragraph at the largest DOM Range endpoint that fits the\n * currently available body space. The caller then processes the tail normally,\n * allowing it to fragment again on later pages when necessary.\n */\n private tryFragmentParagraph(\n block: MeasuredBlock,\n dims: PageDimensions,\n availableHeightPt: number,\n effectiveMarginTopPt: number\n ): MeasuredBlock[] | null {\n if (!this.canFragmentParagraph(block) || availableHeightPt <= effectiveMarginTopPt) {\n return null;\n }\n\n const split = this.splitParagraphAtLargestFit(block.element, (head) => {\n const measured = this.measureElement(head, dims, block.sectionIndex);\n return effectiveMarginTopPt + measured.heightPt <= availableHeightPt;\n });\n if (split.kind !== \"split\") return null;\n\n const headMeasured = this.measureElement(split.head, dims, block.sectionIndex);\n const tailMeasured = this.measureElement(split.tail, dims, block.sectionIndex);\n\n return [\n {\n ...headMeasured,\n element: split.head,\n keepWithNext: false,\n keepLines: false,\n pageBreakBefore: false,\n isPageBreak: false,\n },\n {\n ...tailMeasured,\n element: split.tail,\n keepWithNext: false,\n keepLines: false,\n pageBreakBefore: false,\n isPageBreak: false,\n },\n ];\n }\n\n /**\n * Parses the header/footer registry from the staging element.\n * Also measures heights during parsing for lazy-loading compatibility.\n */\n private parseHeaderFooterRegistry(): HeaderFooterRegistry {\n const registry: HeaderFooterRegistry = new Map();\n const registryEl = this.stagingElement.querySelector(\"#pagination-hf-registry\");\n\n if (!registryEl) return registry;\n\n // Build a map of section index -> content width for measurement\n const sectionWidths = new Map<number, number>();\n const sections = Array.from(this.stagingElement.querySelectorAll<HTMLElement>(\"[data-section-index]\"));\n for (const section of sections) {\n const idx = parseInt(section.dataset.sectionIndex || \"0\", 10);\n const contentWidth = parseFloat(section.dataset.contentWidth || \"\") || DEFAULT_PAGE_WIDTH - 2 * DEFAULT_MARGIN;\n sectionWidths.set(idx, contentWidth);\n }\n // Fallback content width if no sections found\n const defaultContentWidth = sectionWidths.get(0) || DEFAULT_PAGE_WIDTH - 2 * DEFAULT_MARGIN;\n\n const entries = Array.from(registryEl.querySelectorAll<HTMLElement>(\"[data-section][data-hf-type]\"));\n\n for (const entry of entries) {\n const sectionIndex = parseInt(entry.dataset.section || \"0\", 10);\n const hfType = entry.dataset.hfType as string;\n\n if (!registry.has(sectionIndex)) {\n registry.set(sectionIndex, {});\n }\n\n const section = registry.get(sectionIndex)!;\n // Clone the first child element (the actual header/footer content)\n const content = entry.cloneNode(true) as HTMLElement;\n\n // Get content width for this section (for accurate measurement)\n const contentWidth = sectionWidths.get(sectionIndex) || defaultContentWidth;\n\n // Measure height during parsing (one-time cost, enables lazy loading)\n const measuredHeight = this.measureHeaderFooterHeight(content, contentWidth);\n\n switch (hfType) {\n case \"header-default\":\n section.headerDefault = content;\n section.headerDefaultHeight = measuredHeight;\n break;\n case \"header-first\":\n section.headerFirst = content;\n section.headerFirstHeight = measuredHeight;\n break;\n case \"header-even\":\n section.headerEven = content;\n section.headerEvenHeight = measuredHeight;\n break;\n case \"footer-default\":\n section.footerDefault = content;\n section.footerDefaultHeight = measuredHeight;\n break;\n case \"footer-first\":\n section.footerFirst = content;\n section.footerFirstHeight = measuredHeight;\n break;\n case \"footer-even\":\n section.footerEven = content;\n section.footerEvenHeight = measuredHeight;\n break;\n }\n }\n\n return registry;\n }\n\n /**\n * Parses the footnote registry from the staging element.\n */\n private parseFootnoteRegistry(): FootnoteRegistry {\n const registry: FootnoteRegistry = new Map();\n const registryEl = this.stagingElement.querySelector(\"#pagination-footnote-registry\");\n\n if (!registryEl) return registry;\n\n // Only direct registry entries are definitions. Custom separator stories may\n // legitimately contain arbitrary converted markup, including stale semantic\n // attributes that must never be mistaken for another note definition.\n const entries = Array.from(registryEl.children)\n .filter((entry): entry is HTMLElement =>\n entry instanceof this.view.HTMLElement && entry.hasAttribute(\"data-footnote-id\"));\n\n for (const entry of entries) {\n const footnoteId = entry.dataset.footnoteId;\n if (footnoteId) {\n // Clone the footnote element for later use\n registry.set(footnoteId, entry.cloneNode(true) as HTMLElement);\n }\n }\n\n return registry;\n }\n\n /** Read Word's optional normal and continuation separator stories. */\n private parseFootnoteSeparators(): {\n normal: HTMLElement | null;\n continuation: HTMLElement | null;\n } {\n const registry = this.stagingElement.querySelector(\"#pagination-footnote-registry\");\n const clone = (kind: \"normal\" | \"continuation\") => {\n const source = Array.from(registry?.children ?? []).find((entry) =>\n entry instanceof this.view.HTMLElement\n && entry.getAttribute(\"data-footnote-separator\") === kind,\n );\n return source?.cloneNode(true) as HTMLElement | undefined;\n };\n return {\n normal: clone(\"normal\") ?? null,\n continuation: clone(\"continuation\") ?? null,\n };\n }\n\n /** Append the exact separator story selected for this initial/continued note band. */\n private appendFootnoteSeparator(container: HTMLElement, continuation: boolean): void {\n const kind = continuation ? \"continuation\" : \"normal\";\n const source = continuation ? this.footnoteContinuationSeparator : this.footnoteSeparator;\n if (!source) {\n const fallback = this.document.createElement(\"hr\");\n fallback.dataset.footnoteSeparator = kind;\n container.appendChild(fallback);\n return;\n }\n\n const clone = source.cloneNode(true) as HTMLElement;\n for (const element of [clone, ...Array.from(clone.querySelectorAll<HTMLElement>(\"*\"))]) {\n element.removeAttribute(\"id\");\n element.removeAttribute(\"data-anchor\");\n element.removeAttribute(\"data-committed-text\");\n element.removeAttribute(\"data-source-anchor-id\");\n element.removeAttribute(\"data-page-fragment-id\");\n element.removeAttribute(\"data-fragment-index\");\n element.removeAttribute(\"data-footnote-id\");\n element.removeAttribute(\"data-comment-id\");\n element.removeAttribute(\"name\");\n if (element instanceof this.view.HTMLAnchorElement\n && element.getAttribute(\"href\")?.startsWith(\"#\")) {\n element.removeAttribute(\"href\");\n }\n element.setAttribute(\"contenteditable\", \"false\");\n }\n container.appendChild(clone);\n }\n\n /** Parses the hidden source notes used to render paginated margin comments. */\n private parseCommentMarginRegistry(): Map<string, HTMLElement> {\n const registry = new Map<string, HTMLElement>();\n const registryEl = this.stagingElement.querySelector(\n \"#pagination-comment-margin-registry\",\n );\n if (!registryEl) return registry;\n\n for (const entry of Array.from(\n registryEl.querySelectorAll<HTMLElement>(\"[data-comment-id]\"),\n )) {\n const commentId = entry.dataset.commentId;\n if (!commentId) continue;\n // A reply's note nests inside its thread root (issue #540), so every id in a thread\n // maps to the root's clone \u2014 a reply marker selects the whole thread, and the\n // placement loop dedupes by the clone's own id so the thread lands once per page.\n let root = entry;\n for (\n let parent = root.parentElement?.closest<HTMLElement>(\"[data-comment-id]\");\n parent && registryEl.contains(parent);\n parent = root.parentElement?.closest<HTMLElement>(\"[data-comment-id]\")\n ) {\n root = parent;\n }\n registry.set(commentId, root.cloneNode(true) as HTMLElement);\n }\n return registry;\n }\n\n /**\n * Extracts footnote reference IDs from an element.\n */\n private extractFootnoteRefs(element: HTMLElement): string[] {\n const refs = element.querySelectorAll<HTMLElement>(\"[data-footnote-id]\");\n const ids: string[] = [];\n for (const ref of Array.from(refs)) {\n const id = ref.dataset.footnoteId;\n if (id && !ids.includes(id)) {\n ids.push(id);\n }\n }\n return ids;\n }\n\n /** Clone a note child for the continuation queue with its original selector position. */\n private cloneFootnoteElementForContinuation(\n element: HTMLElement,\n sourceIndex: number,\n ): HTMLElement {\n const clone = element.cloneNode(true) as HTMLElement;\n clone.setAttribute(\n FOOTNOTE_SOURCE_POSITION_ATTR,\n sourceIndex === 0 ? \"first\" : \"later\",\n );\n return clone;\n }\n\n /** Remove identities that belong only to the initial registry presentation shell. */\n private makeContinuationShellInert(element: HTMLElement): void {\n element.removeAttribute(\"id\");\n element.removeAttribute(\"data-anchor\");\n element.removeAttribute(\"data-committed-text\");\n }\n\n /**\n * Build the exact continuation shape shared by measurement and paint.\n *\n * Keep the source `.footnote-item > .footnote-content` ancestry: document\n * CSS, inherited direction/language, and custom classes frequently target\n * those shells. Only the number and initial HTML/editor identities are\n * omitted. A hidden sentinel preserves `p:not(:first-of-type)` for a page\n * whose first carried element was a later source paragraph.\n */\n private createFootnoteContinuationWrapper(\n continuation: FootnoteContinuation,\n elements: readonly HTMLElement[] = continuation.remainingElements,\n ): HTMLElement {\n const source = this.footnoteRegistry.get(continuation.footnoteId);\n const wrapper = source\n ? source.cloneNode(false) as HTMLElement\n : this.document.createElement(\"div\");\n this.makeContinuationShellInert(wrapper);\n wrapper.classList.add(\"footnote-continuation\");\n wrapper.dataset.footnoteId = continuation.footnoteId;\n if (continuation.sourceAnchorId) {\n wrapper.dataset.sourceAnchorId = continuation.sourceAnchorId;\n }\n\n const sourceContent = source?.querySelector<HTMLElement>(\".footnote-content\");\n const content = sourceContent\n ? sourceContent.cloneNode(false) as HTMLElement\n : this.document.createElement(\"span\");\n this.makeContinuationShellInert(content);\n if (!sourceContent) content.className = \"footnote-content\";\n\n const first = elements[0];\n if (\n first?.tagName === \"P\"\n && first.getAttribute(FOOTNOTE_SOURCE_POSITION_ATTR) === \"later\"\n ) {\n const sentinel = this.document.createElement(\"p\");\n sentinel.className = \"footnote-continuation-position-sentinel\";\n sentinel.setAttribute(\"aria-hidden\", \"true\");\n sentinel.style.setProperty(\"display\", \"none\", \"important\");\n content.appendChild(sentinel);\n }\n for (let index = 0; index < elements.length; index++) {\n this.checkpoint();\n const element = elements[index];\n const clone = element.cloneNode(true) as HTMLElement;\n clone.removeAttribute(FOOTNOTE_SOURCE_POSITION_ATTR);\n if (index === 0 && clone.tagName === \"P\") {\n // Without the initial number, a carried paragraph always starts a new\n // line even when source CSS made the note's first paragraph inline.\n clone.style.setProperty(\"display\", \"block\", \"important\");\n }\n content.appendChild(clone);\n }\n wrapper.appendChild(content);\n return wrapper;\n }\n\n /** Build the exact partial-note item shape shared by measurement and paint. */\n private createPartialFootnoteItem(\n footnote: HTMLElement,\n fittingElements: readonly HTMLElement[],\n ): HTMLElement {\n const item = footnote.cloneNode(false) as HTMLElement;\n const number = footnote.querySelector(\".footnote-number\");\n if (number) item.appendChild(number.cloneNode(true));\n\n const sourceContent = footnote.querySelector<HTMLElement>(\".footnote-content\");\n const content = sourceContent\n ? sourceContent.cloneNode(false) as HTMLElement\n : this.document.createElement(\"span\");\n if (!sourceContent) content.className = \"footnote-content\";\n for (const element of fittingElements) {\n this.checkpoint();\n content.appendChild(element.cloneNode(true));\n }\n item.appendChild(content);\n return item;\n }\n\n /**\n * Where a note measurement tree is attached.\n *\n * A note band is reserved from a measurement and then painted; if the two\n * happen under different inherited typography the painted notes overflow the\n * reserve, which is the invisible clipping this engine exists to avoid. The\n * registry lives in the staging tree but a band paints inside the output\n * container, so measure there. The host is never a page box, so a page's\n * `zoom` cannot scale a reserve that is accounted for in unscaled points.\n */\n private noteMeasurementHost(): HTMLElement {\n return this.containerElement.isConnected\n ? this.containerElement\n : this.stagingElement;\n }\n\n /**\n * Measures the height of footnotes for given IDs (in points).\n * Creates a temporary container to measure the footnotes.\n * @param footnoteIds - IDs of footnotes to measure\n * @param contentWidth - Width for measurement\n * @param continuation - Optional continuation content to include first\n */\n private measureFootnotesHeight(\n footnoteIds: string[],\n contentWidth: number,\n continuation?: FootnoteContinuation | null,\n partialFootnotes?: readonly PartialFootnote[],\n ): number {\n const hasContinuation = continuation && continuation.remainingElements.length > 0;\n if (footnoteIds.length === 0 && !hasContinuation) {\n return 0;\n }\n // Mirror addPageFootnotes: with no registry and nothing carried, no band is\n // painted, so reserving the separator's height would shrink every page's\n // body for a document whose note references resolve to nothing.\n if (this.footnoteRegistry.size === 0 && !hasContinuation) {\n return 0;\n }\n\n // Measure in the SAME styling context the notes render in: `.page-footnotes` carries\n // its own font-size and line-height, so measuring without the class sizes the note\n // block against body type and the reserve can never match what is drawn.\n // Create a temporary measurement container\n const measureContainer = this.document.createElement(\"div\");\n measureContainer.style.position = \"absolute\";\n measureContainer.style.visibility = \"hidden\";\n measureContainer.style.width = `${contentWidth}pt`;\n measureContainer.style.left = \"-9999px\";\n measureContainer.className = this.cssPrefix + \"footnotes\";\n\n // Add the same normal/continuation separator story that will be painted.\n this.appendFootnoteSeparator(measureContainer, Boolean(hasContinuation));\n\n // Add continuation content first (if any)\n if (hasContinuation) {\n measureContainer.appendChild(this.createFootnoteContinuationWrapper(continuation!));\n }\n\n // Add footnotes\n const partialById = new Map(\n partialFootnotes?.map((partial) => [partial.footnoteId, partial] as const) ?? [],\n );\n for (const id of footnoteIds) {\n this.checkpoint();\n const footnote = this.footnoteRegistry.get(id);\n if (footnote) {\n const partial = partialById.get(id);\n measureContainer.appendChild(partial\n ? this.createPartialFootnoteItem(footnote, partial.fittingElements)\n : footnote.cloneNode(true));\n }\n }\n\n // Append to staging for measurement\n this.noteMeasurementHost().appendChild(measureContainer);\n try {\n return pxToPt(measureContainer.getBoundingClientRect().height);\n } finally {\n measureContainer.remove();\n }\n }\n\n /**\n * Partition a continuation for one page's note band, preferring complete children\n * and range-fragmenting an eligible paragraph when necessary. Always advances by\n * at least one element so an indivisible oversized paragraph follows the established\n * clipped fallback without trapping pagination in a loop.\n */\n private splitContinuationForPage(\n continuation: FootnoteContinuation,\n availableHeightPt: number,\n contentWidth: number,\n ): { current: FootnoteContinuation; overflow: FootnoteContinuation | null } {\n const fitting: HTMLElement[] = [];\n let remaining: HTMLElement[] = [];\n\n // Keep one live measurement tree and append each candidate exactly once.\n // Rebuilding `[...fitting, candidate]` for every child cloned 1+2+...+N\n // descendants before a page/resource cap could run, which is quadratic for\n // producer-authored notes containing thousands of tiny paragraphs/runs.\n const measureContainer = this.document.createElement(\"div\");\n measureContainer.style.position = \"absolute\";\n measureContainer.style.visibility = \"hidden\";\n measureContainer.style.width = `${contentWidth}pt`;\n measureContainer.style.left = \"-9999px\";\n measureContainer.className = this.cssPrefix + \"footnotes\";\n this.appendFootnoteSeparator(measureContainer, true);\n const wrapper = this.createFootnoteContinuationWrapper(continuation, []);\n const content = wrapper.querySelector<HTMLElement>(\":scope > .footnote-content\");\n if (!content) {\n throw new Error(\"Footnote continuation is missing its content shell\");\n }\n measureContainer.appendChild(wrapper);\n this.noteMeasurementHost().appendChild(measureContainer);\n\n try {\n for (let index = 0; index < continuation.remainingElements.length; index++) {\n this.checkpoint();\n const element = continuation.remainingElements[index];\n const sourcePosition = element.getAttribute(FOOTNOTE_SOURCE_POSITION_ATTR);\n if (\n fitting.length === 0\n && element.tagName === \"P\"\n && element.getAttribute(FOOTNOTE_SOURCE_POSITION_ATTR) === \"later\"\n ) {\n const sentinel = this.document.createElement(\"p\");\n sentinel.className = \"footnote-continuation-position-sentinel\";\n sentinel.setAttribute(\"aria-hidden\", \"true\");\n sentinel.style.setProperty(\"display\", \"none\", \"important\");\n content.appendChild(sentinel);\n }\n\n const candidate = element.cloneNode(true) as HTMLElement;\n candidate.removeAttribute(FOOTNOTE_SOURCE_POSITION_ATTR);\n if (fitting.length === 0 && candidate.tagName === \"P\") {\n candidate.style.setProperty(\"display\", \"block\", \"important\");\n }\n content.appendChild(candidate);\n const candidateHeight = pxToPt(measureContainer.getBoundingClientRect().height);\n if (candidateHeight <= availableHeightPt) {\n fitting.push(element);\n continue;\n }\n\n const canSplitCandidate = candidate.tagName === \"P\"\n && this.canRangeFragmentParagraph(candidate);\n candidate.remove();\n let split: ParagraphSplitAttempt | null = null;\n if (canSplitCandidate) {\n split = this.splitParagraphAtLargestFit(candidate, (head) => {\n content.appendChild(head);\n try {\n return pxToPt(measureContainer.getBoundingClientRect().height) <= availableHeightPt;\n } finally {\n head.remove();\n }\n }, {\n // Both fallbacks below cut where ordinary line breaking would not,\n // so they are only ever right for an element that owns the whole\n // band: if this page already carries earlier content, a paragraph\n // that will not start here belongs intact in the next note band.\n emergencyGraphemeBreaks: fitting.length === 0,\n // A continuation page owns the full note band. If even one legal\n // grapheme is taller than it, clip only that unit and keep draining.\n forceFirstOnNoFit: fitting.length === 0,\n });\n }\n if (split?.kind === \"split\") {\n if (sourcePosition) {\n split.head.setAttribute(FOOTNOTE_SOURCE_POSITION_ATTR, sourcePosition);\n split.tail.setAttribute(FOOTNOTE_SOURCE_POSITION_ATTR, sourcePosition);\n }\n fitting.push(split.head);\n remaining = [split.tail, ...continuation.remainingElements.slice(index + 1)];\n } else if (fitting.length === 0) {\n // A genuinely indivisible first element retains the established visible\n // clipped fallback, but only for itself. Its siblings continue later.\n fitting.push(element);\n remaining = continuation.remainingElements.slice(index + 1);\n } else {\n remaining = continuation.remainingElements.slice(index);\n }\n break;\n }\n } finally {\n measureContainer.remove();\n }\n\n return {\n current: {\n footnoteId: continuation.footnoteId,\n sourceAnchorId: continuation.sourceAnchorId,\n remainingElements: fitting,\n },\n overflow: remaining.length > 0 ? {\n footnoteId: continuation.footnoteId,\n sourceAnchorId: continuation.sourceAnchorId,\n remainingElements: remaining,\n } : null,\n };\n }\n\n /**\n * Splits a footnote element into parts that fit within the available height.\n * Returns the elements that fit and the elements that need to continue.\n */\n private splitFootnoteToFit(\n footnoteElement: HTMLElement,\n availableHeightPt: number,\n contentWidth: number,\n forceProgress = false,\n existingPayload?: FootnotePackingContext,\n ): { fits: HTMLElement[]; overflow: HTMLElement[] } {\n // Registry entries are detached clones, so getComputedStyle() cannot validate\n // their real paragraph layout. Attach an exact clone in the rendered note\n // context for the duration of the conservative range-fragmentation check.\n const layoutContext = this.document.createElement(\"div\");\n layoutContext.style.position = \"absolute\";\n layoutContext.style.visibility = \"hidden\";\n layoutContext.style.width = `${contentWidth}pt`;\n layoutContext.style.left = \"-9999px\";\n layoutContext.className = this.cssPrefix + \"footnotes\";\n this.appendFootnoteSeparator(layoutContext, false);\n const attachedFootnote = footnoteElement.cloneNode(true) as HTMLElement;\n layoutContext.appendChild(attachedFootnote);\n this.noteMeasurementHost().appendChild(layoutContext);\n\n // A second live tree represents the exact already-packed page payload plus\n // an initially empty partial item. Candidates are appended once and layout\n // is read in place, avoiding cumulative prefix re-cloning.\n const measureContainer = this.document.createElement(\"div\");\n measureContainer.style.position = \"absolute\";\n measureContainer.style.visibility = \"hidden\";\n measureContainer.style.width = `${contentWidth}pt`;\n measureContainer.style.left = \"-9999px\";\n measureContainer.className = this.cssPrefix + \"footnotes\";\n const hasContinuation = Boolean(existingPayload?.continuation?.remainingElements.length);\n this.appendFootnoteSeparator(measureContainer, hasContinuation);\n if (hasContinuation) {\n measureContainer.appendChild(this.createFootnoteContinuationWrapper(\n existingPayload!.continuation!,\n ));\n }\n const partialById = new Map(\n existingPayload?.partialFootnotes?.map((partial) =>\n [partial.footnoteId, partial] as const) ?? [],\n );\n for (const id of existingPayload?.footnoteIds ?? []) {\n this.checkpoint();\n const source = this.footnoteRegistry.get(id);\n if (!source) continue;\n const partial = partialById.get(id);\n measureContainer.appendChild(partial\n ? this.createPartialFootnoteItem(source, partial.fittingElements)\n : source.cloneNode(true));\n }\n const measuredPartial = this.createPartialFootnoteItem(attachedFootnote, []);\n const measuredContent = measuredPartial.querySelector<HTMLElement>(\":scope > .footnote-content\");\n if (!measuredContent) {\n layoutContext.remove();\n throw new Error(\"Footnote is missing its content shell\");\n }\n measureContainer.appendChild(measuredPartial);\n this.noteMeasurementHost().appendChild(measureContainer);\n\n // Get child elements (paragraphs) of the footnote content.\n //\n // `fits` is spliced into a freshly built `.footnote-item` > `.footnote-content` wrapper by\n // addPageFootnotes, so it must contain the note's CONTENT, never the note element itself.\n // Returning the whole `.footnote-item` here nested a complete item (number span and all)\n // inside another item's content span; the inner block-level div then broke the line, so the\n // note's number rendered alone above its text \u2014 the same visible symptom as the escaped-CSS\n // bug, from an unrelated cause, on the notes that happened to take a can't-split path.\n try {\n const footnoteContent = attachedFootnote.querySelector(\".footnote-content\");\n if (!footnoteContent) {\n // No content structure to split \u2014 hand back the element's own children.\n return {\n fits: Array.from(attachedFootnote.children).map((el) =>\n el.cloneNode(true) as HTMLElement),\n overflow: [],\n };\n }\n\n const children = Array.from(footnoteContent.children) as HTMLElement[];\n const fits: HTMLElement[] = [];\n let overflow: HTMLElement[] = [];\n\n for (let i = 0; i < children.length; i++) {\n this.checkpoint();\n const child = children[i];\n const measuredCandidate = child.cloneNode(true) as HTMLElement;\n measuredContent.appendChild(measuredCandidate);\n const candidateFits = pxToPt(measureContainer.getBoundingClientRect().height)\n <= availableHeightPt;\n if (candidateFits) {\n fits.push(child.cloneNode(true) as HTMLElement);\n continue;\n }\n\n measuredCandidate.remove();\n\n const split = this.canRangeFragmentParagraph(child, true)\n ? this.splitParagraphAtLargestFit(child, (head) => {\n measuredContent.appendChild(head);\n try {\n return pxToPt(measureContainer.getBoundingClientRect().height)\n <= availableHeightPt;\n } finally {\n head.remove();\n }\n }, {\n // Both fallbacks cut where ordinary line breaking would not, so they\n // are only ever right for an element that owns the whole band: with\n // earlier siblings already packed here, a paragraph that will not\n // start belongs intact in the next note band.\n emergencyGraphemeBreaks: fits.length === 0 && forceProgress,\n forceFirstOnNoFit: fits.length === 0 && forceProgress,\n })\n : null;\n if (split?.kind === \"split\") {\n fits.push(split.head);\n split.tail.setAttribute(\n FOOTNOTE_SOURCE_POSITION_ATTR,\n i === 0 ? \"first\" : \"later\",\n );\n overflow = [split.tail, ...children.slice(i + 1).map((element, offset) =>\n this.cloneFootnoteElementForContinuation(element, i + 1 + offset))];\n } else if (split?.kind === \"no-fit\") {\n // The paragraph is splittable, but this citation page has less than\n // one line left. Defer it whole so a fresh note band can try again.\n overflow = children.slice(i).map((element, offset) =>\n this.cloneFootnoteElementForContinuation(element, i + offset));\n } else if (fits.length === 0 && forceProgress) {\n // Preserve the one-element clipping fallback for content that cannot be\n // range-fragmented only on a dedicated full note band, while allowing\n // every later sibling to continue. A residual citation-page band must\n // defer the whole element: it may fit untouched on the next page.\n fits.push(child.cloneNode(true) as HTMLElement);\n overflow = children.slice(i + 1).map((element, offset) =>\n this.cloneFootnoteElementForContinuation(element, i + 1 + offset));\n } else {\n overflow = children.slice(i).map((element, offset) =>\n this.cloneFootnoteElementForContinuation(element, i + offset));\n }\n break;\n }\n\n return { fits, overflow };\n } finally {\n measureContainer.remove();\n layoutContext.remove();\n }\n }\n\n /**\n * Adds footnotes to a page container, including continuation content.\n */\n private addPageFootnotes(\n pageBox: HTMLElement,\n footnoteIds: string[],\n dims: PageDimensions,\n bands: PageBands,\n footnoteHeight: number,\n continuation?: FootnoteContinuation | null,\n partialFootnotes?: PartialFootnote[]\n ): void {\n const hasContinuation = continuation && continuation.remainingElements.length > 0;\n if (footnoteIds.length === 0 && !hasContinuation) {\n return;\n }\n if (this.footnoteRegistry.size === 0 && !hasContinuation) {\n return;\n }\n\n // Calculate max height for footnotes area (content height minus margin for body content)\n const maxFootnoteHeight = Math.min(\n footnoteHeight,\n bands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO\n );\n\n const footnotesDiv = this.document.createElement(\"div\");\n footnotesDiv.className = `${this.cssPrefix}footnotes`;\n footnotesDiv.style.position = \"absolute\";\n // Notes sit at the FOOT OF THE BODY BAND, not at the bottom margin: a footer taller than\n // its margin raises that edge, and anchoring to the raw margin would draw notes over it.\n footnotesDiv.style.bottom = `${dims.pageHeight - (bands.bodyTop + bands.bodyHeight)}pt`;\n footnotesDiv.style.left = `${dims.marginLeft}pt`;\n footnotesDiv.style.width = `${dims.contentWidth}pt`;\n footnotesDiv.style.boxSizing = \"border-box\";\n // Constrain height and clip overflow to prevent footnotes covering body content\n footnotesDiv.style.maxHeight = `${maxFootnoteHeight}pt`;\n footnotesDiv.style.overflow = \"hidden\";\n\n this.appendFootnoteSeparator(footnotesDiv, Boolean(hasContinuation));\n\n // Add continuation content first (if any)\n if (hasContinuation) {\n footnotesDiv.appendChild(this.createFootnoteContinuationWrapper(continuation!));\n }\n\n // Clone footnotes in order of appearance\n for (const id of footnoteIds) {\n // Check if this is a partial footnote\n const partial = partialFootnotes?.find(p => p.footnoteId === id);\n if (partial) {\n // Render partial footnote (only the fitting elements)\n const footnote = this.footnoteRegistry.get(id);\n if (footnote) {\n footnotesDiv.appendChild(\n this.createPartialFootnoteItem(footnote, partial.fittingElements),\n );\n }\n } else {\n // Render full footnote from registry\n const footnote = this.footnoteRegistry.get(id);\n if (footnote) {\n footnotesDiv.appendChild(footnote.cloneNode(true));\n }\n }\n }\n\n pageBox.appendChild(footnotesDiv);\n }\n\n /** Select the section's first/odd/even header from its one-based page position. */\n private selectHeader(\n sectionIndex: number,\n pageInSection: number,\n displayedPageNumber: number,\n ): HTMLElement | undefined {\n const sectionHf = this.hfRegistry.get(sectionIndex);\n if (!sectionHf) return undefined;\n\n // First page of section uses first header if available\n if (pageInSection === 1 && sectionHf.headerFirst) {\n return sectionHf.headerFirst;\n }\n\n // ECMA-376 \u00A717.10.5 hangs the even story on \"even numbered pages\": the PAGE NUMBER,\n // which keeps counting across a section boundary unless w:pgNumType restarts it (and a\n // restart moves the parity with it). Word and LibreOffice both render DB001-Sections\n // this way; selecting by position-in-section flips every story of a section that\n // begins on an even page (issue #536).\n if (displayedPageNumber % 2 === 0 && sectionHf.headerEven) {\n return sectionHf.headerEven;\n }\n\n // Default (odd) pages\n return sectionHf.headerDefault;\n }\n\n /**\n * The story KIND (`\"first\"` / `\"even\"` / `\"default\"`) a page position selects \u2014 the same\n * decision {@link selectHeader} / {@link selectFooter} make, named rather than resolved to\n * an element, so a page can advertise which OOXML story it is showing.\n */\n private storyKindFor(\n which: \"header\" | \"footer\",\n sectionIndex: number,\n pageInSection: number,\n displayedPageNumber: number,\n ): \"first\" | \"even\" | \"default\" {\n const sectionHf = this.hfRegistry.get(sectionIndex);\n const first = which === \"header\" ? sectionHf?.headerFirst : sectionHf?.footerFirst;\n const even = which === \"header\" ? sectionHf?.headerEven : sectionHf?.footerEven;\n if (pageInSection === 1 && first) return \"first\";\n if (displayedPageNumber % 2 === 0 && even) return \"even\";\n return \"default\";\n }\n\n /** Select the section's first/odd/even footer from its one-based page position. */\n private selectFooter(\n sectionIndex: number,\n pageInSection: number,\n displayedPageNumber: number,\n ): HTMLElement | undefined {\n const sectionHf = this.hfRegistry.get(sectionIndex);\n if (!sectionHf) return undefined;\n\n // First page of section uses first footer if available\n if (pageInSection === 1 && sectionHf.footerFirst) {\n return sectionHf.footerFirst;\n }\n\n if (displayedPageNumber % 2 === 0 && sectionHf.footerEven) {\n return sectionHf.footerEven;\n }\n\n // Default (odd) pages\n return sectionHf.footerDefault;\n }\n\n /**\n * The header, body, and footer bands for one page position.\n *\n * The single owner of \"where does anything sit vertically on this page\" \u2014 placement in\n * {@link createPage}, the body budget the flow loop spends, and the note area's anchor all\n * read it, so those three cannot disagree about where the body ends.\n *\n * Deterministic: it depends only on the section's page setup and the registry's pre-measured\n * story heights, never on the page's content, which is what keeps it lazy-loading compatible.\n */\n /** The body height a physical page may fill: its body band less any caller-requested reduction. */\n private bodyBudgetPt(bodyHeightPt: number, physicalPageNumber: number): number {\n return Math.max(0, bodyHeightPt - (this.bodyBudgetReductionsPt[physicalPageNumber] ?? 0));\n }\n\n private getPageBands(\n dims: PageDimensions,\n sectionIndex: number,\n pageInSection: number,\n displayedPageNumber: number,\n ): PageBands {\n const sectionHf = this.hfRegistry.get(sectionIndex);\n return resolvePageBands(\n dims,\n this.selectStoryHeight(\n sectionHf?.headerFirstHeight,\n sectionHf?.headerEvenHeight,\n sectionHf?.headerDefaultHeight,\n pageInSection,\n displayedPageNumber\n ),\n this.selectStoryHeight(\n sectionHf?.footerFirstHeight,\n sectionHf?.footerEvenHeight,\n sectionHf?.footerDefaultHeight,\n pageInSection,\n displayedPageNumber\n )\n );\n }\n\n /**\n * The measured height of the running story this page position selects, mirroring\n * {@link selectHeader}/{@link selectFooter}. Zero when the page has no such story.\n */\n private selectStoryHeight(\n first: number | undefined,\n even: number | undefined,\n fallback: number | undefined,\n pageInSection: number,\n displayedPageNumber: number,\n ): number {\n if (pageInSection === 1 && first != null) return first;\n if (displayedPageNumber % 2 === 0 && even != null) return even;\n return fallback ?? 0;\n }\n\n /**\n * Measures the content height of a header or footer element.\n *\n * This is what tells {@link resolvePageBands} whether the story stays inside its margin or\n * pushes the body, so it must measure the story ALONE \u2014 any padding added here would have to\n * be added to the rendered band too, and the two drifting apart is exactly how a header\n * silently starts overlapping body text.\n */\n private measureHeaderFooterHeight(\n source: HTMLElement,\n contentWidth: number\n ): number {\n // Create a temporary measurement container\n const measureContainer = this.document.createElement(\"div\");\n measureContainer.style.position = \"absolute\";\n measureContainer.style.visibility = \"hidden\";\n measureContainer.style.width = `${contentWidth}pt`;\n measureContainer.style.left = \"-9999px\";\n\n // Clone and add the header/footer content\n for (const child of Array.from(source.childNodes)) {\n measureContainer.appendChild(child.cloneNode(true));\n }\n\n // Append to staging for measurement\n this.stagingElement.appendChild(measureContainer);\n\n // Measure\n const rect = measureContainer.getBoundingClientRect();\n const heightPt = pxToPt(rect.height);\n\n // Clean up\n this.stagingElement.removeChild(measureContainer);\n\n return heightPt;\n }\n\n /**\n * Flows measured blocks into page containers.\n * Implements a single-pass, forward-only algorithm that is compatible with future lazy loading.\n * Supports footnote continuation - long footnotes can split across pages.\n */\n private flowToPages(\n blocks: MeasuredBlock[],\n dims: PageDimensions,\n startPageNumber: number,\n sectionIndex: number,\n sectionDimensions: ReadonlyMap<number, PageDimensions>,\n ): PageInfo[] {\n const pages: PageInfo[] = [];\n let currentContent: HTMLElement[] = [];\n let pageNumber = startPageNumber;\n let pageSectionIndex = sectionIndex;\n // A continuous section can begin on a page owned by its predecessor. That shared physical\n // page is still page 1 of the later section, so its first independently owned page is page 2.\n const sectionStartPages = new Map<number, number>([[sectionIndex, startPageNumber]]);\n const pageInSection = (owner = pageSectionIndex, physicalPage = pageNumber) =>\n physicalPage - (sectionStartPages.get(owner) ?? physicalPage) + 1;\n const dimensionsFor = (owner = pageSectionIndex) =>\n sectionDimensions.get(owner) ?? dims;\n const markSectionPlaced = (owner: number) => {\n if (!sectionStartPages.has(owner)) sectionStartPages.set(owner, pageNumber);\n };\n const previousBox = this.containerElement.querySelector<HTMLElement>(\n `.${this.cssPrefix}box:last-of-type`,\n );\n let precedingDisplayedPageNumber = parseInt(\n previousBox?.dataset.displayedPageNumber ?? \"0\",\n 10,\n );\n const displayedPageNumber = (\n owner = pageSectionIndex,\n ownerPageInSection = pageInSection(owner),\n physicalPage = pageNumber,\n ) => {\n const numbering = this.pageNumbering.get(owner) ?? {};\n if (numbering.start !== undefined) {\n return numbering.start + ownerPageInSection - 1;\n }\n const ownerNumbering = this.pageNumbering.get(pageSectionIndex) ?? {};\n const currentPhysicalNumber = ownerNumbering.start !== undefined\n ? ownerNumbering.start + pageInSection(pageSectionIndex) - 1\n : precedingDisplayedPageNumber + 1;\n return currentPhysicalNumber + physicalPage - pageNumber;\n };\n\n // Get effective content height for first page (accounts for header/footer sizes)\n let effectiveContentHeight = this.bodyBudgetPt(this.getPageBands(\n dimensionsFor(), pageSectionIndex, pageInSection(), displayedPageNumber()\n ).bodyHeight, pageNumber);\n let remainingHeight = effectiveContentHeight;\n\n // Track the previous block's bottom margin for margin collapsing\n let prevMarginBottomPt = 0;\n // Track footnote IDs for the current page\n let currentFootnoteIds: string[] = [];\n let currentPageHasFootnoteReference = false;\n // Track height consumed by footnotes on current page\n let currentFootnoteHeight = 0;\n // Track footnote continuation for current page (from previous page)\n let currentContinuation: FootnoteContinuation | null = this.pendingFootnoteContinuation;\n // Track any new continuation that will carry to next page\n let nextPageContinuation: FootnoteContinuation | null = null;\n let currentContinuationPartitioned = false;\n let currentPageAdmitted = false;\n /**\n * Whole notes that could not be started on this page and must render on the next one.\n *\n * `nextPageContinuation` is a single slot, but the code that fills it runs once per note in a\n * page's citation list \u2014 so when two notes on the same page both failed to fit, the second\n * overwrote the first and that note was never rendered anywhere. On a 94-footnote document\n * four notes disappeared from the output entirely. A deferral queue keeps the single-slot\n * continuation for its real meaning (the tail of a note that was SPLIT) and carries\n * never-started notes forward as ordinary footnote ids, which the next page already knows how\n * to lay out.\n */\n let deferredFootnoteIds: string[] = [];\n // Track partial footnotes for current page (footnotes that were split)\n let currentPartialFootnotes: PartialFootnote[] = [];\n\n const adoptEmptyPageOwner = (owner: number) => {\n pageSectionIndex = owner;\n const bands = this.getPageBands(\n dimensionsFor(owner),\n owner,\n pageInSection(owner),\n displayedPageNumber(owner),\n );\n effectiveContentHeight = this.bodyBudgetPt(bands.bodyHeight, pageNumber);\n remainingHeight = effectiveContentHeight;\n };\n\n const admitCurrentPage = () => {\n if (currentPageAdmitted) return;\n this.admitPageAllocation();\n currentPageAdmitted = true;\n };\n\n const prepareCurrentContinuation = () => {\n if (currentContinuationPartitioned\n || (currentContinuation?.remainingElements.length ?? 0) === 0) return;\n // A continuation guarantees that this physical page will exist. Admit it\n // before touching the carried DOM, then partition once and retain that\n // exact head while body placement is decided.\n admitCurrentPage();\n const ownedDimensions = dimensionsFor();\n const pageBands = this.getPageBands(\n ownedDimensions,\n pageSectionIndex,\n pageInSection(),\n displayedPageNumber(),\n );\n const partition = this.splitContinuationForPage(\n currentContinuation!,\n pageBands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO,\n ownedDimensions.contentWidth,\n );\n currentContinuation = partition.current;\n if (partition.overflow) nextPageContinuation = partition.overflow;\n currentContinuationPartitioned = true;\n currentFootnoteHeight = this.measureFootnotesHeight(\n currentFootnoteIds,\n ownedDimensions.contentWidth,\n currentContinuation,\n currentPartialFootnotes,\n );\n };\n\n const finishPage = (\n nextPageSectionIndex = pageSectionIndex,\n forceEmptyPage = false,\n ) => {\n const hasCurrentContinuation =\n (currentContinuation?.remainingElements.length ?? 0) > 0;\n if (!forceEmptyPage && currentContent.length === 0 && currentFootnoteIds.length === 0\n && !hasCurrentContinuation) {\n adoptEmptyPageOwner(nextPageSectionIndex);\n return;\n }\n\n prepareCurrentContinuation();\n // Pages without a carried continuation are charged here. Continuation\n // pages were charged before their page-owned partition above.\n admitCurrentPage();\n\n let pageContinuation = currentContinuation;\n const ownedPageInSection = pageInSection();\n const ownedDimensions = dimensionsFor();\n const ownedDisplayedPageNumber = displayedPageNumber(\n pageSectionIndex,\n ownedPageInSection,\n );\n const pageBands = this.getPageBands(\n ownedDimensions,\n pageSectionIndex,\n ownedPageInSection,\n ownedDisplayedPageNumber,\n );\n const maxFootnoteHeight = pageBands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO;\n // `prepareCurrentContinuation` has already packed the exact head for this\n // page. Keeping that partition stable lets body fit decisions share the\n // remaining band without rescanning the entire tail.\n pageContinuation = currentContinuation;\n\n // Recompute the entire painted payload after continuation partitioning.\n // Measuring only the carried head here discarded the reserve for a newer\n // whole/partial note and let the clipped note band silently consume it.\n currentFootnoteHeight = this.measureFootnotesHeight(\n currentFootnoteIds,\n ownedDimensions.contentWidth,\n pageContinuation,\n currentPartialFootnotes,\n );\n\n // A final body page can seed the next page with several whole notes. Partition that queue\n // before materializing a note-only page: addPageFootnotes deliberately clips its band, so\n // placing the entire queue in one page would keep the DOM nodes while making later notes\n // invisible (and therefore absent from a geometry-clipped PageMap).\n if (currentContent.length === 0 && currentFootnoteIds.length > 0\n && currentPartialFootnotes.length === 0) {\n const fittingIds: string[] = [];\n for (let index = 0; index < currentFootnoteIds.length; index++) {\n const footnoteId = currentFootnoteIds[index];\n const candidateIds = [...fittingIds, footnoteId];\n const candidateHeight = this.measureFootnotesHeight(\n candidateIds, ownedDimensions.contentWidth, pageContinuation);\n const guardedCandidateHeight = candidateHeight + FOOTNOTE_MEASUREMENT_GUARD_PT;\n if (guardedCandidateHeight <= maxFootnoteHeight) {\n fittingIds.push(footnoteId);\n currentFootnoteHeight = guardedCandidateHeight;\n continue;\n }\n\n const hasPageContinuation =\n (pageContinuation?.remainingElements.length ?? 0) > 0;\n if (fittingIds.length === 0 && !hasPageContinuation) {\n // One note alone is taller than the note band. Split at the same safe paragraph\n // boundaries used during body flow; if it is indivisible, preserve the established\n // visible clipped fallback while still advancing the queue.\n const source = this.footnoteRegistry.get(footnoteId);\n const split = source\n ? this.splitFootnoteToFit(\n source,\n maxFootnoteHeight - FOOTNOTE_MEASUREMENT_GUARD_PT,\n ownedDimensions.contentWidth,\n true,\n )\n : null;\n fittingIds.push(footnoteId);\n if (source && split && split.fits.length > 0 && split.overflow.length > 0) {\n currentPartialFootnotes.push({ footnoteId, fittingElements: split.fits });\n nextPageContinuation = {\n footnoteId,\n sourceAnchorId: source.dataset.sourceAnchorId,\n remainingElements: split.overflow,\n };\n currentFootnoteHeight = maxFootnoteHeight;\n } else {\n currentFootnoteHeight = guardedCandidateHeight;\n }\n deferredFootnoteIds.push(...currentFootnoteIds.slice(index + 1));\n } else {\n deferredFootnoteIds.push(...currentFootnoteIds.slice(index));\n }\n break;\n }\n currentFootnoteIds = fittingIds;\n }\n\n const page = this.createPage(\n ownedDimensions,\n pageNumber,\n pageSectionIndex,\n ownedDisplayedPageNumber,\n currentContent,\n ownedPageInSection,\n currentFootnoteIds,\n currentFootnoteHeight,\n pageContinuation,\n currentPartialFootnotes.length > 0 ? currentPartialFootnotes : undefined,\n false,\n currentPageAdmitted,\n );\n pages.push(page);\n precedingDisplayedPageNumber = ownedDisplayedPageNumber;\n\n pageNumber++;\n currentContent = [];\n\n pageSectionIndex = nextPageSectionIndex;\n if (!sectionStartPages.has(pageSectionIndex)) {\n // A carried note can own pages before the first body block of a new\n // section lands. Count those physical pages in that section so first /\n // odd / even stories and restarted displayed numbering advance once.\n sectionStartPages.set(pageSectionIndex, pageNumber);\n }\n\n // Get effective content height for new page position\n const newBands = this.getPageBands(\n dimensionsFor(),\n pageSectionIndex,\n pageInSection(),\n displayedPageNumber(),\n );\n effectiveContentHeight = this.bodyBudgetPt(newBands.bodyHeight, pageNumber);\n remainingHeight = effectiveContentHeight;\n\n prevMarginBottomPt = 0; // Reset margin tracking for new page\n currentFootnoteIds = []; // Reset footnotes for new page\n currentPageHasFootnoteReference = false;\n currentPartialFootnotes = []; // Reset partial footnotes for new page\n\n // Carry over continuation to next page\n currentContinuation = nextPageContinuation;\n nextPageContinuation = null;\n currentContinuationPartitioned = false;\n currentPageAdmitted = false;\n\n // Notes that never got started land at the top of the new page's note area. They are\n // ordinary footnotes from here on, so the normal fitting path handles them \u2014 and because\n // this page is fresh, the space they were denied now exists.\n if (deferredFootnoteIds.length > 0) {\n currentFootnoteIds = [...deferredFootnoteIds];\n deferredFootnoteIds = [];\n }\n\n // Whole deferred notes have no carried DOM and can be measured directly.\n // A continuation is partitioned lazily after the prospective page has\n // passed its resource admission check, so never clone its entire tail here.\n const nextDimensions = dimensionsFor();\n currentFootnoteHeight = currentContinuation\n ? 0\n : this.measureFootnotesHeight(currentFootnoteIds, nextDimensions.contentWidth);\n };\n\n const pageHasPayload = () => currentContent.length > 0\n || currentFootnoteIds.length > 0\n || (currentContinuation?.remainingElements.length ?? 0) > 0;\n\n for (let i = 0; i < blocks.length; i++) {\n this.checkpoint();\n const block = blocks[i];\n const allBlockFootnoteIds = this.extractFootnoteRefs(block.element);\n\n // Word normally lets a same-box continuous section begin on its predecessor's page. By\n // default, a footnote reference before that boundary promotes it to a page break. The\n // footnoteLayoutLikeWW8 compatibility switch permits post-break paragraphs without their own\n // references to remain on the shared page; keep checking until the page turns so a later\n // referenced paragraph still begins on a fresh page.\n if (block.sectionIndex !== pageSectionIndex && currentPageHasFootnoteReference\n && (!this.footnoteLayoutLikeWord8 || allBlockFootnoteIds.length > 0)) {\n finishPage(block.sectionIndex);\n }\n\n const pageIsEmpty = currentContent.length === 0\n && currentFootnoteIds.length === 0\n && (currentContinuation?.remainingElements.length ?? 0) === 0;\n if (pageIsEmpty && block.sectionIndex !== pageSectionIndex) {\n adoptEmptyPageOwner(block.sectionIndex);\n }\n\n prepareCurrentContinuation();\n\n if (currentFootnoteHeight > 0) {\n const ownerDimensions = dimensionsFor();\n const ownerBands = this.getPageBands(\n ownerDimensions,\n pageSectionIndex,\n pageInSection(),\n displayedPageNumber(),\n );\n const ownerMaxFootnoteArea = ownerBands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO;\n if (currentFootnoteHeight > ownerMaxFootnoteArea) {\n // A queue of whole notes larger than the band does not entitle it to\n // the whole page: the band is capped and the rest of the page still\n // belongs to body text. Keep the notes that fit here, defer the rest,\n // and only fall back to a note-owned page when not even one note fits\n // \u2014 that case needs the splitting finishPage performs.\n const fittingIds: string[] = [];\n let fittingHeight = 0;\n if ((currentContinuation?.remainingElements.length ?? 0) === 0\n && currentPartialFootnotes.length === 0) {\n for (const footnoteId of currentFootnoteIds) {\n this.checkpoint();\n const candidateHeight = this.measureFootnotesHeight(\n [...fittingIds, footnoteId],\n ownerDimensions.contentWidth,\n );\n if (candidateHeight + FOOTNOTE_MEASUREMENT_GUARD_PT > ownerMaxFootnoteArea) break;\n fittingIds.push(footnoteId);\n fittingHeight = candidateHeight;\n }\n }\n if (fittingIds.length > 0) {\n deferredFootnoteIds = [\n ...currentFootnoteIds.slice(fittingIds.length),\n ...deferredFootnoteIds,\n ];\n currentFootnoteIds = fittingIds;\n currentFootnoteHeight = fittingHeight;\n } else {\n finishPage(block.sectionIndex);\n i--;\n continue;\n }\n }\n }\n const blockDimensions = dimensionsFor(block.sectionIndex);\n const nextBlockPageInSection = pageInSection(block.sectionIndex, pageNumber + 1);\n const freshBlockPageBodyHeight = this.getPageBands(\n blockDimensions,\n block.sectionIndex,\n nextBlockPageInSection,\n displayedPageNumber(block.sectionIndex, nextBlockPageInSection, pageNumber + 1),\n ).bodyHeight;\n\n // Handle explicit page breaks\n if (block.isPageBreak) {\n finishPage(block.sectionIndex);\n continue;\n }\n\n // Handle page break before\n if (block.pageBreakBefore && currentContent.length > 0) {\n finishPage(block.sectionIndex);\n }\n\n // A series of keep-with-next blocks is one indivisible placement unit\n // when it can fit a new page. The former one-block lookahead was never\n // applied to the placement decision, and it could not preserve a chain of\n // headings/paragraphs. Do not force an oversized chain to a fresh page:\n // its members retain the established greedy/overflow behavior instead.\n const previousBlock = blocks[i - 1];\n const startsKeepChain =\n !previousBlock ||\n !previousBlock.keepWithNext ||\n previousBlock.isPageBreak ||\n block.pageBreakBefore;\n // A footnote continuation also occupies this page even when its body has\n // no blocks yet. In that case a feasible chain may need to move past the\n // continuation as a unit.\n const pageHasOccupiedSpace = currentContent.length > 0 || currentFootnoteHeight > 0;\n if (pageHasOccupiedSpace && block.keepWithNext && startsKeepChain) {\n const keepChain = this.getKeepWithNextChain(blocks, i);\n if (keepChain.length > 1) {\n const newChainFootnoteIds = this.collectNewFootnoteIds(\n keepChain,\n currentFootnoteIds\n );\n let additionalChainFootnoteHeight = 0;\n if (newChainFootnoteIds.length > 0 && this.footnoteRegistry.size > 0) {\n const totalChainFootnoteHeight = this.measureFootnotesHeight(\n [...currentFootnoteIds, ...newChainFootnoteIds],\n blockDimensions.contentWidth,\n currentContinuation\n );\n additionalChainFootnoteHeight = Math.max(\n 0,\n totalChainFootnoteHeight - currentFootnoteHeight\n );\n }\n\n const currentChainHeight =\n this.measureKeepWithNextChainBodyHeight(\n keepChain,\n prevMarginBottomPt,\n currentContent.length === 0,\n pageInSection(block.sectionIndex)\n ) +\n additionalChainFootnoteHeight;\n const currentAvailableHeight = remainingHeight - currentFootnoteHeight;\n\n if (currentChainHeight > currentAvailableHeight) {\n const nextPageBands = this.getPageBands(\n dimensionsFor(block.sectionIndex),\n block.sectionIndex,\n pageInSection(block.sectionIndex, pageNumber + 1),\n displayedPageNumber(\n block.sectionIndex,\n pageInSection(block.sectionIndex, pageNumber + 1),\n pageNumber + 1,\n ),\n );\n const freshChainBodyHeight = this.measureKeepWithNextChainBodyHeight(\n keepChain,\n 0,\n true,\n pageInSection(block.sectionIndex, pageNumber + 1)\n );\n // finishPage transfers this continuation to the new page's\n // currentContinuation state, so include it in the destination\n // page's footnote reservation before deciding to move the chain.\n const freshChainFootnoteHeight = this.measureFootnotesHeight(\n newChainFootnoteIds,\n blockDimensions.contentWidth,\n nextPageContinuation\n );\n\n if (\n freshChainBodyHeight + freshChainFootnoteHeight <=\n nextPageBands.bodyHeight\n ) {\n finishPage(block.sectionIndex);\n }\n }\n }\n }\n\n // Extract footnote references from this block\n // Only count new footnotes (not already on this page)\n const newFootnoteIds = this.collectNewFootnoteIds([block], currentFootnoteIds);\n\n // Calculate additional footnote height if this block is added\n let combinedFootnoteHeight = currentFootnoteHeight;\n let additionalFootnoteHeight = 0;\n if (newFootnoteIds.length > 0 && this.footnoteRegistry.size > 0) {\n // Measure the combined height of all footnotes that would be on this page\n // (including any continuation)\n const combinedFootnoteIds = [...currentFootnoteIds, ...newFootnoteIds];\n combinedFootnoteHeight = this.measureFootnotesHeight(\n combinedFootnoteIds,\n blockDimensions.contentWidth,\n currentContinuation,\n currentPartialFootnotes,\n );\n additionalFootnoteHeight = Math.max(\n 0,\n combinedFootnoteHeight - currentFootnoteHeight,\n );\n }\n\n // Calculate the effective height this block will consume\n // Account for margin collapsing: the gap between blocks is max(prevBottom, currTop), not sum\n const isFirstOnPage = currentContent.length === 0;\n const effectiveMarginTop = this.effectiveBlockMarginTop(\n block,\n prevMarginBottomPt,\n isFirstOnPage,\n pageInSection(block.sectionIndex)\n );\n const columnFragments = this.reflowSequentialColumns(\n block,\n blockDimensions,\n remainingHeight - currentFootnoteHeight - additionalFootnoteHeight - effectiveMarginTop,\n );\n if (columnFragments) {\n blocks.splice(i, 1, ...columnFragments);\n i--;\n continue;\n }\n // Visible height = top margin gap + content + footnote space\n // Note: bottom margin is NOT included in the fit check because the last block's\n // bottom margin extends beyond the content area and is clipped by overflow:hidden.\n // It is still tracked in remainingHeight for correct margin collapsing with the next block.\n const blockSpace = effectiveMarginTop + block.heightPt + additionalFootnoteHeight;\n\n // Effective remaining height (content area minus footnotes already on page)\n const effectiveRemainingHeight = remainingHeight - currentFootnoteHeight;\n\n // Calculate maximum footnote area for this page (can expand into body content space)\n const bodyContentUsed = effectiveContentHeight - remainingHeight;\n const maxFootnoteArea = effectiveContentHeight * MAX_FOOTNOTE_AREA_RATIO;\n\n // A paragraph that cannot fit as a whole may still have a simple text-only\n // prefix that fits this page. Fragment before the ordinary next-page or\n // oversized fallback so the cloned head participates in the same margin\n // and footnote accounting as every other block.\n if (blockSpace > effectiveRemainingHeight) {\n const paragraphFragments = this.tryFragmentParagraph(\n block,\n blockDimensions,\n effectiveRemainingHeight,\n effectiveMarginTop\n );\n if (paragraphFragments) {\n blocks.splice(i, 1, ...paragraphFragments);\n i--;\n continue;\n }\n }\n\n // Check if block fits on current page (including its footnotes)\n if (\n blockSpace <= effectiveRemainingHeight\n && (combinedFootnoteHeight === 0\n || combinedFootnoteHeight + FOOTNOTE_MEASUREMENT_GUARD_PT <= maxFootnoteArea)\n ) {\n // Block fits with current footnote allocation\n markSectionPlaced(block.sectionIndex);\n currentContent.push(this.cloneBlockForPage(\n block,\n isFirstOnPage,\n pageInSection(block.sectionIndex),\n ));\n remainingHeight -= (effectiveMarginTop + block.heightPt + block.marginBottomPt);\n prevMarginBottomPt = block.marginBottomPt;\n // Add new footnotes to current page\n if (newFootnoteIds.length > 0) {\n currentFootnoteIds.push(...newFootnoteIds);\n currentFootnoteHeight = combinedFootnoteHeight;\n }\n currentPageHasFootnoteReference ||= allBlockFootnoteIds.length > 0;\n } else if (\n block.heightPt + this.effectiveBlockMarginTop(\n block,\n 0,\n true,\n pageInSection(block.sectionIndex, pageNumber + 1),\n ) <= freshBlockPageBodyHeight\n ) {\n // Block doesn't fit with current allocation - try expanding footnote area\n const blockSpaceWithoutFootnotes = effectiveMarginTop + block.heightPt;\n\n if (newFootnoteIds.length > 0 && blockSpaceWithoutFootnotes <= effectiveRemainingHeight) {\n // Block itself fits, but footnotes don't - expand footnote area\n markSectionPlaced(block.sectionIndex);\n currentContent.push(this.cloneBlockForPage(\n block,\n isFirstOnPage,\n pageInSection(block.sectionIndex),\n ));\n remainingHeight -= (effectiveMarginTop + block.heightPt + block.marginBottomPt);\n prevMarginBottomPt = block.marginBottomPt;\n\n // Calculate EXPANDED space available for footnotes\n // Footnotes can take up to maxFootnoteArea or all remaining space, whichever is less\n const availableForFootnotes = Math.min(\n maxFootnoteArea,\n effectiveContentHeight - bodyContentUsed - blockSpaceWithoutFootnotes\n );\n // Deferring a note costs a page turn, and it only buys anything if the\n // next page's band is larger than what this page can already offer.\n // When this page already offers the maximum band, a note that cannot\n // start here cannot start there either: keep the established clipped\n // fallback on the citing page rather than evacuating it.\n const nextPageMaxFootnoteArea =\n freshBlockPageBodyHeight * MAX_FOOTNOTE_AREA_RATIO;\n const deferralCannotHelp = availableForFootnotes >= nextPageMaxFootnoteArea;\n\n // Try to fit as much of each new footnote as possible in expanded area\n for (let noteIndex = 0; noteIndex < newFootnoteIds.length; noteIndex++) {\n this.checkpoint();\n const footnoteId = newFootnoteIds[noteIndex];\n const footnote = this.footnoteRegistry.get(footnoteId);\n if (!footnote) continue;\n\n // One page can carry only one split tail. Once that slot is used,\n // keep every later note in source order on the deferral queue.\n if (nextPageContinuation) {\n deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex));\n break;\n }\n\n const candidateIds = [...currentFootnoteIds, footnoteId];\n const candidateHeight = this.measureFootnotesHeight(\n candidateIds,\n blockDimensions.contentWidth,\n currentContinuation,\n currentPartialFootnotes,\n );\n const spaceLeftForFootnotes = availableForFootnotes - currentFootnoteHeight;\n\n if (candidateHeight + FOOTNOTE_MEASUREMENT_GUARD_PT <= availableForFootnotes) {\n // Whole footnote fits in expanded area\n currentFootnoteIds.push(footnoteId);\n currentFootnoteHeight = candidateHeight;\n } else {\n // Footnote needs to be split - use all available expanded space\n if (spaceLeftForFootnotes > 20) { // Minimum space to start a footnote\n const { fits, overflow } = this.splitFootnoteToFit(\n footnote,\n availableForFootnotes,\n blockDimensions.contentWidth,\n deferralCannotHelp,\n {\n footnoteIds: currentFootnoteIds,\n continuation: currentContinuation,\n partialFootnotes: currentPartialFootnotes,\n },\n );\n\n if (fits.length > 0) {\n // Add partial footnote to current page\n currentFootnoteIds.push(footnoteId);\n currentPartialFootnotes.push({\n footnoteId,\n fittingElements: fits\n });\n if (overflow.length > 0) {\n nextPageContinuation = {\n footnoteId,\n sourceAnchorId: footnote.dataset.sourceAnchorId,\n remainingElements: overflow\n };\n }\n currentFootnoteHeight = this.measureFootnotesHeight(\n currentFootnoteIds,\n blockDimensions.contentWidth,\n currentContinuation,\n currentPartialFootnotes,\n );\n if (overflow.length > 0) {\n deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex + 1));\n break;\n }\n } else {\n // Nothing of this note fits: defer the WHOLE note rather than assigning the\n // single continuation slot, which a later note on this page would overwrite.\n deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex));\n break;\n }\n } else {\n // Not enough space to even start the note \u2014 same deferral.\n deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex));\n break;\n }\n }\n }\n currentPageHasFootnoteReference ||= allBlockFootnoteIds.length > 0;\n } else {\n // Block itself doesn't fit. Retry after the page turn so carried\n // continuations and deferred notes participate in the normal fit and\n // split decisions instead of being overwritten by forced placement.\n finishPage(block.sectionIndex, !pageHasPayload());\n i--;\n continue;\n }\n } else {\n // Block is taller than a page. Ordinary tables can be split at complete\n // row boundaries; every other block retains the established overflow path.\n const tableFragments = this.trySplitSimpleOversizedTable(\n block,\n dimensionsFor(block.sectionIndex),\n block.sectionIndex,\n );\n if (tableFragments) {\n if (currentContent.length > 0 || currentContinuation) {\n finishPage(block.sectionIndex);\n }\n blocks.splice(i, 1, ...tableFragments);\n i--;\n continue;\n }\n\n // Unsupported oversized blocks are intentionally left intact. Splitting\n // arbitrary HTML, merged tables, or footnote-bearing tables would be less\n // correct than the prior clipped fallback.\n if (pageHasPayload()) {\n finishPage(block.sectionIndex);\n i--;\n continue;\n }\n markSectionPlaced(block.sectionIndex);\n currentContent.push(this.cloneBlockForPage(\n block,\n true,\n pageInSection(block.sectionIndex),\n ));\n // An unsupported body block already occupies/clips its whole body band.\n // Preserve its notes losslessly on following note-only pages rather\n // than drawing them underneath that clipped fallback.\n deferredFootnoteIds.push(...newFootnoteIds);\n currentPageHasFootnoteReference ||= allBlockFootnoteIds.length > 0;\n finishPage(block.sectionIndex);\n }\n }\n\n // Finish last page\n finishPage();\n\n // A split created while finishing the final body page still needs a page substrate.\n // Drain all remaining note paragraphs into footnote-only continuation pages.\n while (\n currentFootnoteIds.length > 0\n || deferredFootnoteIds.length > 0\n || (currentContinuation?.remainingElements.length ?? 0) > 0\n ) {\n finishPage();\n }\n\n // Store any remaining continuation for next section\n this.pendingFootnoteContinuation = nextPageContinuation;\n\n return pages;\n }\n\n\n /**\n * Strip block addressing from a header/footer node cloned into a page box.\n *\n * A running story is authored ONCE and cloned onto every page, so the clones all carry the same\n * `data-anchor` \u2014 on this document, 42 page boxes claiming one footer paragraph. Left editable,\n * committing any one of them writes back through that single shared anchor, and the per-page\n * page-number substitution makes it worse: each clone shows a DIFFERENT number, so a commit\n * writes that page's number into the story as literal text and destroys the PAGE field.\n *\n * Page-box header/footer content is presentation. The docked editing bands\n * (`editor-headerfooter.ts`) are the addressable affordance, and they exist precisely because a\n * cloned node cannot be uniquely addressed.\n */\n private makeClonedStoryInert(root: HTMLElement): void {\n const nodes = [root, ...Array.from(root.querySelectorAll<HTMLElement>(\"*\"))];\n for (const el of nodes) {\n el.removeAttribute(\"data-anchor\");\n el.removeAttribute(\"data-committed-text\");\n if (el.getAttribute(\"contenteditable\") !== null) el.setAttribute(\"contenteditable\", \"false\");\n }\n }\n\n /** A repeated margin note is presentation, not a second bookmark/link target. */\n private makeClonedMarginCommentInert(root: HTMLElement): void {\n this.makeClonedStoryInert(root);\n const nodes = [root, ...Array.from(root.querySelectorAll<HTMLElement>(\"*\"))];\n for (const element of nodes) {\n element.removeAttribute(\"id\");\n if (element.localName === \"a\" && element.getAttribute(\"href\")?.startsWith(\"#\")) {\n element.removeAttribute(\"href\");\n element.setAttribute(\"aria-disabled\", \"true\");\n element.tabIndex = -1;\n }\n }\n }\n\n /**\n * Resolves floating DrawingML objects after their anchor paragraphs have landed on a page.\n *\n * The converter deliberately carries the OOXML bases and offsets as data instead of flattening\n * them into CSS: page/margin/column bases belong to the page, while paragraph/line/character\n * bases belong to the laid-out anchor. Once both coordinate systems exist, promote the object\n * from the clipped text column into the page box and position it in one shared point space.\n */\n private positionDrawingAnchors(\n pageBox: HTMLElement,\n contentArea: HTMLElement,\n dims: PageDimensions,\n pageNumber: number,\n ): void {\n const anchors = Array.from(\n contentArea.querySelectorAll<HTMLElement>('[data-docx-drawing-anchor=\"true\"]'),\n );\n if (anchors.length === 0) return;\n\n const pageRect = pageBox.getBoundingClientRect();\n const pixelsPerPoint = pageRect.width / dims.pageWidth;\n if (!(pixelsPerPoint > 0)) return;\n\n for (const anchor of anchors) {\n const staticRect = anchor.getBoundingClientRect();\n const paragraph = anchor.closest<HTMLElement>('p, h1, h2, h3, h4, h5, h6');\n const paragraphRect = paragraph?.getBoundingClientRect() ?? staticRect;\n const toPageX = (x: number) => (x - pageRect.left) / pixelsPerPoint;\n const toPageY = (y: number) => (y - pageRect.top) / pixelsPerPoint;\n\n const context = {\n staticLeft: toPageX(staticRect.left),\n staticTop: toPageY(staticRect.top),\n lineHeight: paragraph\n ? (parseFloat(this.view.getComputedStyle(paragraph).lineHeight) || staticRect.height) / pixelsPerPoint\n : staticRect.height / pixelsPerPoint,\n paragraphLeft: toPageX(paragraphRect.left),\n paragraphTop: toPageY(paragraphRect.top),\n paragraphWidth: paragraphRect.width / pixelsPerPoint,\n paragraphHeight: paragraphRect.height / pixelsPerPoint,\n };\n\n const widthReference = this.horizontalAnchorReference(\n anchor.getAttribute('data-docx-anchor-width-relative') ?? 'margin',\n dims,\n pageNumber,\n context,\n );\n const widthPercent = this.anchorNumber(anchor, 'width-percent');\n // Word supports strictly positive relative dimensions (MS-ODRAWXML 2.18.3.1/2).\n // Zero is schema-valid but must not collapse a visible object with a stored extent.\n if (widthPercent !== undefined && widthPercent > 0) {\n anchor.style.width = `${widthReference.size * widthPercent / 100}pt`;\n }\n\n const heightReference = this.verticalAnchorReference(\n anchor.getAttribute('data-docx-anchor-height-relative') ?? 'margin',\n dims,\n pageNumber,\n context,\n );\n const heightPercent = this.anchorNumber(anchor, 'height-percent');\n if (heightPercent !== undefined && heightPercent > 0 && anchor.dataset.docxAnchorAutofit !== 'true') {\n anchor.style.height = `${heightReference.size * heightPercent / 100}pt`;\n }\n\n // Read the final border-box size after relative sizing and before reparenting. The stored\n // wp:extent remains the fallback; wps:bodyPr insets stay inside it through border-box sizing.\n const sizedRect = anchor.getBoundingClientRect();\n const width = sizedRect.width / pixelsPerPoint;\n const height = sizedRect.height / pixelsPerPoint;\n const horizontal = this.horizontalAnchorReference(\n anchor.getAttribute('data-docx-anchor-h-relative') ?? 'column',\n dims,\n pageNumber,\n context,\n );\n const vertical = this.verticalAnchorReference(\n anchor.getAttribute('data-docx-anchor-v-relative') ?? 'paragraph',\n dims,\n pageNumber,\n context,\n );\n const left = this.resolveAnchorAxis(\n horizontal,\n width,\n anchor.getAttribute('data-docx-anchor-h-align'),\n this.anchorNumber(anchor, 'h-offset'),\n pageNumber,\n );\n const top = this.resolveAnchorAxis(\n vertical,\n height,\n anchor.getAttribute('data-docx-anchor-v-align'),\n this.anchorNumber(anchor, 'v-offset'),\n pageNumber,\n );\n\n // Leaving the paragraph must not orphan it: a paragraph whose only content was this drawing\n // renders as the drawing, and measureRenderedSource finds it through this link (issue #849).\n const hostElement = anchor.parentElement?.closest<HTMLElement>('[data-source-anchor-id]');\n const hostAnchorId = hostElement?.dataset.sourceAnchorId;\n if (hostAnchorId) anchor.setAttribute(DRAWING_HOST_ATTRIBUTE, hostAnchorId);\n pageBox.appendChild(anchor);\n // A host left with nothing but its paragraph-mark line (issue #880) renders as that line plus\n // the drawing; one with text of its own keeps measuring as its own box.\n if (hostElement && rendersOnlyItsLine(hostElement))\n hostElement.setAttribute(HOSTS_PROMOTED_DRAWING_ATTRIBUTE, 'true');\n anchor.style.position = 'absolute';\n anchor.style.left = `${left}pt`;\n anchor.style.top = `${top}pt`;\n anchor.style.margin = '0';\n }\n }\n\n private anchorNumber(anchor: HTMLElement, suffix: string): number | undefined {\n const raw = anchor.getAttribute(`data-docx-anchor-${suffix}`);\n if (raw === null) return undefined;\n const value = Number(raw);\n return Number.isFinite(value) ? value : undefined;\n }\n\n private horizontalAnchorReference(\n relativeFrom: string,\n dims: PageDimensions,\n pageNumber: number,\n context: {\n staticLeft: number;\n paragraphLeft: number;\n paragraphWidth: number;\n },\n ): { start: number; size: number } {\n const leftMargin = { start: 0, size: dims.marginLeft };\n const rightMargin = {\n start: dims.marginLeft + dims.contentWidth,\n size: dims.marginRight,\n };\n switch (relativeFrom) {\n case 'page':\n return { start: 0, size: dims.pageWidth };\n case 'character':\n return { start: context.staticLeft, size: 0 };\n case 'leftMargin':\n return leftMargin;\n case 'rightMargin':\n return rightMargin;\n case 'insideMargin':\n return pageNumber % 2 === 1 ? leftMargin : rightMargin;\n case 'outsideMargin':\n return pageNumber % 2 === 1 ? rightMargin : leftMargin;\n case 'paragraph':\n // Not part of ST_RelFromH, but accepting it gives malformed/legacy producers the\n // intuitive base without conflating it with the whole text column.\n return { start: context.paragraphLeft, size: context.paragraphWidth };\n case 'column':\n case 'margin':\n default:\n // The paginator currently lays out one text column per section, so the column and page\n // margin boxes coincide. Keeping the names distinct here leaves multi-column geometry\n // localized to this resolver when that layout support is added.\n return { start: dims.marginLeft, size: dims.contentWidth };\n }\n }\n\n private verticalAnchorReference(\n relativeFrom: string,\n dims: PageDimensions,\n pageNumber: number,\n context: {\n staticTop: number;\n lineHeight: number;\n paragraphTop: number;\n paragraphHeight: number;\n },\n ): { start: number; size: number } {\n const topMargin = { start: 0, size: dims.marginTop };\n const bottomMargin = {\n start: dims.marginTop + dims.contentHeight,\n size: dims.marginBottom,\n };\n switch (relativeFrom) {\n case 'page':\n return { start: 0, size: dims.pageHeight };\n case 'paragraph':\n return { start: context.paragraphTop, size: context.paragraphHeight };\n case 'line':\n return { start: context.staticTop, size: context.lineHeight };\n case 'topMargin':\n return topMargin;\n case 'bottomMargin':\n return bottomMargin;\n case 'insideMargin':\n return pageNumber % 2 === 1 ? topMargin : bottomMargin;\n case 'outsideMargin':\n return pageNumber % 2 === 1 ? bottomMargin : topMargin;\n case 'margin':\n default:\n return { start: dims.marginTop, size: dims.contentHeight };\n }\n }\n\n private resolveAnchorAxis(\n reference: { start: number; size: number },\n objectSize: number,\n alignment: string | null,\n offset: number | undefined,\n pageNumber: number,\n ): number {\n if (offset !== undefined) return reference.start + offset;\n\n let fraction = 0;\n if (alignment === 'center') fraction = 0.5;\n else if (alignment === 'right' || alignment === 'bottom') fraction = 1;\n else if (alignment === 'inside') fraction = pageNumber % 2 === 1 ? 0 : 1;\n else if (alignment === 'outside') fraction = pageNumber % 2 === 1 ? 1 : 0;\n return reference.start + (reference.size - objectSize) * fraction;\n }\n\n /** Charge a physical page before any page-owned partitioning or DOM allocation. */\n private admitPageAllocation(): void {\n const prospectivePageCount = this.createdPageCount + 1;\n this.pageCountCheckpoint?.(prospectivePageCount);\n this.createdPageCount = prospectivePageCount;\n }\n\n /**\n * Creates a page container element.\n */\n private createPage(\n dims: PageDimensions,\n pageNumber: number,\n sectionIndex: number,\n displayedPageNumber: number,\n content: HTMLElement[],\n pageInSection: number,\n footnoteIds: string[] = [],\n footnoteHeight: number = 0,\n continuation?: FootnoteContinuation | null,\n partialFootnotes?: PartialFootnote[],\n isSectionFiller = false,\n pageAlreadyAdmitted = false,\n ): PageInfo {\n if (!pageAlreadyAdmitted) this.admitPageAllocation();\n // Create page box at full size, then scale the entire box\n // This ensures proper clipping and consistent scaling of all elements\n const pageBox = this.document.createElement(\"div\");\n pageBox.className = `${this.cssPrefix}box`;\n pageBox.style.width = `${dims.pageWidth}pt`;\n pageBox.style.height = `${dims.pageHeight}pt`;\n pageBox.style.overflow = \"hidden\";\n pageBox.style.position = \"relative\";\n // Use CSS zoom for better text rendering when supported, fall back to transform\n // Zoom affects layout (no negative margin hack needed) and renders text more crisply\n // Note: zoom is non-standard but supported in all major browsers\n if (this.scale !== 1) {\n if (this.view.CSS?.supports(\"zoom\", \"1\")) {\n pageBox.style.zoom = String(this.scale);\n } else {\n pageBox.style.transform = `scale(${this.scale})`;\n pageBox.style.transformOrigin = \"top left\";\n // Transform does not affect layout, so compensate for the natural box dimensions.\n const heightReductionPt = dims.pageHeight * (1 - this.scale);\n const widthReductionPt = dims.pageWidth * (1 - this.scale);\n const heightReductionPx = ptToPx(heightReductionPt);\n const widthReductionPx = ptToPx(widthReductionPt);\n pageBox.style.marginRight = `-${widthReductionPx}px`;\n pageBox.style.marginBottom = `${this.pageGap - heightReductionPx}px`;\n }\n }\n // Hint browser for GPU compositing and layout isolation\n pageBox.style.willChange = \"transform\";\n pageBox.style.contain = \"layout paint\";\n pageBox.dataset.pageNumber = String(pageNumber);\n pageBox.dataset.sectionIndex = String(sectionIndex);\n pageBox.dataset.displayedPageNumber = String(displayedPageNumber);\n if (isSectionFiller) pageBox.dataset.sectionFiller = \"true\";\n // Needed by substitutePageNumberFields: a section that restarts numbering counts from its own\n // first page, not from the document's.\n pageBox.dataset.pageInSection = String(pageInSection);\n\n // Where the three bands sit on this page (no re-measurement needed)\n const bands = this.getPageBands(dims, sectionIndex, pageInSection, displayedPageNumber);\n\n // Add header if available for this section/page\n const headerSource = isSectionFiller\n ? undefined\n : this.selectHeader(sectionIndex, pageInSection, displayedPageNumber);\n\n if (headerSource) {\n const headerDiv = this.document.createElement(\"div\");\n headerDiv.className = `${this.cssPrefix}header`;\n // Which story this page shows, so an editor can map the page back to the OOXML part it\n // edits in place (a first-page header is a different part from the default one).\n headerDiv.dataset.hfType = this.storyKindFor(\n \"header\", sectionIndex, pageInSection, displayedPageNumber,\n );\n headerDiv.style.position = \"absolute\";\n // `w:header` is the distance to the TOP of the story, and the story grows downward from\n // there; `flex-start` therefore pins the edge the OOXML actually declares, and content\n // taller than the measurement clips at the bottom rather than sliding up the page.\n headerDiv.style.top = `${bands.headerTop}pt`;\n headerDiv.style.bottom = \"auto\";\n headerDiv.style.left = `${dims.marginLeft}pt`;\n headerDiv.style.width = `${dims.contentWidth}pt`;\n headerDiv.style.height = `${bands.headerHeight}pt`;\n headerDiv.style.overflow = \"hidden\";\n headerDiv.style.boxSizing = \"border-box\";\n headerDiv.style.display = \"flex\";\n headerDiv.style.flexDirection = \"column\";\n headerDiv.style.justifyContent = \"flex-start\";\n // Clone the header content (skip the wrapper div's data attributes)\n for (const child of Array.from(headerSource.childNodes)) {\n const clonedheaderDiv = child.cloneNode(true) as HTMLElement;\n if (clonedheaderDiv.nodeType === 1) this.makeClonedStoryInert(clonedheaderDiv);\n headerDiv.appendChild(clonedheaderDiv);\n }\n pageBox.appendChild(headerDiv);\n }\n\n // Create content area using pre-computed effective heights\n const contentAreaTop = bands.bodyTop;\n const contentAreaHeight = bands.bodyHeight;\n\n const contentArea = this.document.createElement(\"div\");\n contentArea.className = `${this.cssPrefix}content`;\n contentArea.style.position = \"absolute\";\n contentArea.style.top = `${contentAreaTop}pt`;\n contentArea.style.left = `${dims.marginLeft}pt`;\n contentArea.style.width = `${dims.contentWidth}pt`;\n contentArea.style.height = `${contentAreaHeight}pt`;\n // Clip only vertically: Word lets over-wide content (e.g. a table pulled left by a negative\n // w:tblInd, issue #827) run into the side margins, clipping it only at the paper edge.\n // `clip` rather than `hidden`, because `hidden` would force the x axis to `auto`.\n contentArea.style.overflowX = \"visible\";\n contentArea.style.overflowY = \"clip\";\n\n // Add content\n for (const el of content) {\n contentArea.appendChild(el);\n }\n\n pageBox.appendChild(contentArea);\n\n // Add footnotes if any references appear on this page (or continuation from previous)\n const hasContinuation = continuation && continuation.remainingElements.length > 0;\n if (!isSectionFiller && (footnoteIds.length > 0 || hasContinuation)) {\n this.addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes);\n\n }\n\n // Materialize margin comments after footnotes exist so markers inside a\n // note participate in the same page-owned comment story as body markers.\n const pageCommentIds: string[] = [];\n for (const marker of Array.from(\n pageBox.querySelectorAll<HTMLElement>(\"[data-comment-id]\"),\n )) {\n if (marker.closest(`.${this.cssPrefix}comment-margin`)) continue;\n const id = marker.dataset.commentId;\n if (id && this.commentMarginRegistry.has(id) && !pageCommentIds.includes(id)) {\n pageCommentIds.push(id);\n }\n }\n if (pageCommentIds.length > 0) {\n const marginColumn = this.document.createElement(\"aside\");\n marginColumn.className = `${this.cssPrefix}comment-margin`;\n marginColumn.style.position = \"absolute\";\n marginColumn.style.top = `${contentAreaTop}pt`;\n marginColumn.style.left = `${dims.marginLeft + dims.contentWidth + 3}pt`;\n marginColumn.style.width = `${Math.max(12, dims.marginRight - 6)}pt`;\n marginColumn.style.maxHeight = `${contentAreaHeight}pt`;\n marginColumn.style.overflow = \"hidden\";\n marginColumn.style.boxSizing = \"border-box\";\n const placedThreadRoots = new Set<string>();\n for (const id of pageCommentIds) {\n const source = this.commentMarginRegistry.get(id);\n if (source) {\n // Every id in a nested thread maps to the same root note; place it once even\n // when the root's and a reply's markers share the page.\n const rootId = source.dataset.commentId ?? id;\n if (placedThreadRoots.has(rootId)) continue;\n placedThreadRoots.add(rootId);\n const clone = source.cloneNode(true) as HTMLElement;\n this.makeClonedMarginCommentInert(clone);\n marginColumn.appendChild(clone);\n }\n }\n pageBox.appendChild(marginColumn);\n }\n\n // Add footer if available for this section/page\n const footerSource = isSectionFiller\n ? undefined\n : this.selectFooter(sectionIndex, pageInSection, displayedPageNumber);\n if (footerSource) {\n const footerDiv = this.document.createElement(\"div\");\n footerDiv.className = `${this.cssPrefix}footer`;\n footerDiv.dataset.hfType = this.storyKindFor(\n \"footer\", sectionIndex, pageInSection, displayedPageNumber,\n );\n footerDiv.style.position = \"absolute\";\n // `w:footer` is the distance to the BOTTOM of the story, and the story grows upward from\n // there \u2014 the mirror of the header, so `flex-end` and a bottom anchor.\n footerDiv.style.top = \"auto\";\n footerDiv.style.bottom = `${dims.footerDistance}pt`;\n footerDiv.style.left = `${dims.marginLeft}pt`;\n footerDiv.style.width = `${dims.contentWidth}pt`;\n footerDiv.style.height = `${bands.footerHeight}pt`;\n footerDiv.style.overflow = \"hidden\";\n footerDiv.style.boxSizing = \"border-box\";\n footerDiv.style.display = \"flex\";\n footerDiv.style.flexDirection = \"column\";\n footerDiv.style.justifyContent = \"flex-end\";\n // Clone the footer content (skip the wrapper div's data attributes)\n for (const child of Array.from(footerSource.childNodes)) {\n const clonedfooterDiv = child.cloneNode(true) as HTMLElement;\n if (clonedfooterDiv.nodeType === 1) this.makeClonedStoryInert(clonedfooterDiv);\n footerDiv.appendChild(clonedfooterDiv);\n }\n pageBox.appendChild(footerDiv);\n }\n\n // Add page number (will be hidden by CSS if document has footer)\n if (this.showPageNumbers && !isSectionFiller) {\n const pageNum = this.document.createElement(\"div\");\n pageNum.className = `${this.cssPrefix}number`;\n pageNum.textContent = String(pageNumber);\n pageBox.appendChild(pageNum);\n }\n\n // Add to container\n this.containerElement.appendChild(pageBox);\n\n // Floating objects need the final page and anchor-paragraph boxes. Resolve them only after\n // append, when getBoundingClientRect() is meaningful, and lift them out of content clipping.\n this.positionDrawingAnchors(pageBox, contentArea, dims, pageNumber);\n\n // Body and notes must occupy DISJOINT bands. The note block is absolutely positioned against\n // the page bottom and grows upward, while the content area spans the whole text height \u2014 so\n // nothing but the layout loop's arithmetic keeps them apart, and any disagreement between the\n // height the loop reserved and the height the notes actually render at draws body text and note\n // text on top of each other (observed on a 94-footnote document: ~134pt of superimposed,\n // illegible glyphs). Shrinking the content area to the space the notes actually left removes\n // the failure mode by construction \u2014 the worst case becomes a clean clip by the content area's\n // vertical overflow clip, which is obvious and recoverable rather than silent corruption.\n //\n // This runs AFTER the append on purpose: `getBoundingClientRect()` on a detached node is all\n // zeroes, so measuring while building the page silently did nothing.\n const notesEl = pageBox.querySelector<HTMLElement>(`.${this.cssPrefix}footnotes`);\n if (notesEl) {\n const notesHeightPt = pxToPt(notesEl.getBoundingClientRect().height);\n if (notesHeightPt > 0) {\n contentArea.style.height = `${Math.max(0, contentAreaHeight - notesHeightPt)}pt`;\n }\n }\n\n // Paint offsets must follow range fragmentation: positioned runs are deliberately excluded\n // from that structural algorithm, even though these offsets leave every line box unchanged.\n alignExactLineBaselines(contentArea);\n const paper = pageBox.getBoundingClientRect();\n const header = pageBox.querySelector<HTMLElement>(`.${this.cssPrefix}header`);\n const footer = pageBox.querySelector<HTMLElement>(`.${this.cssPrefix}footer`);\n preserveExactLineInk(contentArea,\n Math.max(paper.top, header?.getBoundingClientRect().bottom ?? paper.top),\n Math.min(paper.bottom, footer?.getBoundingClientRect().top ?? paper.bottom,\n notesEl?.getBoundingClientRect().top ?? paper.bottom), paper);\n\n return {\n pageNumber,\n sectionIndex,\n dimensions: dims,\n element: pageBox,\n };\n }\n}\n\n/**\n * Convenience function to paginate HTML content.\n *\n * @param html - HTML string with pagination metadata\n * @param container - Container element or ID where pages will be rendered\n * @param options - Pagination options\n * @returns PaginationResult\n *\n * @example\n * ```typescript\n * const html = await convertDocxToHtml(docx, { paginationMode: PaginationMode.Paginated });\n *\n * // Create a container for the paginated view\n * const container = document.getElementById('viewer');\n *\n * // Parse and paginate\n * container.innerHTML = html;\n * const staging = document.getElementById('pagination-staging');\n * const pageContainer = document.getElementById('pagination-container');\n *\n * const engine = new PaginationEngine(staging, pageContainer, { scale: 0.8 });\n * const result = engine.paginate();\n *\n * console.log(`Document has ${result.totalPages} pages`);\n * ```\n */\nexport function paginateHtml(\n html: string,\n container: HTMLElement | string,\n options: PaginationOptions = {}\n): PaginationResult {\n const ownerDocument =\n typeof container === \"string\" ? globalThis.document : container.ownerDocument;\n const containerEl =\n typeof container === \"string\"\n ? (ownerDocument.getElementById(container) as HTMLElement)\n : container;\n\n if (!containerEl) {\n throw new Error(\"Container element not found\");\n }\n\n // Insert HTML into container\n containerEl.innerHTML = html;\n\n // Find staging and page container\n const cssPrefix = options.cssPrefix ?? \"page-\";\n const staging = containerEl.querySelector<HTMLElement>(\"#pagination-staging\") ||\n containerEl.querySelector<HTMLElement>(`.${cssPrefix}staging`);\n const pageContainer = containerEl.querySelector<HTMLElement>(\"#pagination-container\") ||\n containerEl.querySelector<HTMLElement>(`.${cssPrefix}container`);\n\n if (!staging) {\n throw new Error(\n \"Pagination staging element not found. Make sure the HTML was generated with PaginationMode.Paginated\"\n );\n }\n\n if (!pageContainer) {\n throw new Error(\"Pagination container element not found\");\n }\n\n // This convenience function is the read-only HTML viewer path. Keep the\n // lower-level engine conservative by default, while enabling paragraph\n // fragmentation here unless a caller deliberately opts out.\n const engine = new PaginationEngine(staging, pageContainer, {\n ...options,\n fragmentParagraphs: options.fragmentParagraphs ?? true,\n });\n return engine.paginate();\n}\n", "/**\n * Rendering a page number in an OOXML number format \u2014 the browser-side counterpart of the .NET\n * `NumberFormats.Render`, used by the paginator to fill in `[data-field]` page-number markers.\n *\n * Two vocabularies reach this module and both are handled here so callers never have to know which\n * one they hold:\n *\n * - `ST_NumberFormat` tokens (`\"lowerRoman\"`, \u2026), from a section's `w:pgNumType/@w:fmt` \u2014\n * stamped on the section wrapper as `data-page-num-fmt`.\n * - Field `\\*` general-formatting switch arguments (`\"roman\"`, `\"ROMAN\"`, `\"alphabetic\"`,\n * `\"ALPHABETIC\"`, `\"Arabic\"`), from a field's own instruction \u2014 stamped as `data-field-format`.\n * Case is load-bearing: `roman` is `i, ii, iii` and `ROMAN` is `I, II, III`.\n *\n * An unrecognized token renders as decimal, matching how Word treats a format it does not\n * implement \u2014 a page number that reads \"12\" is wrong in style; one that reads \"\" is a hole.\n */\n\n/** Formats this module renders. Anything else falls back to decimal. */\ntype Renderer = (value: number) => string;\n\nconst ROMAN_ONES = [\"\", \"i\", \"ii\", \"iii\", \"iv\", \"v\", \"vi\", \"vii\", \"viii\", \"ix\"];\nconst ROMAN_TENS = [\"\", \"x\", \"xx\", \"xxx\", \"xl\", \"l\", \"lx\", \"lxx\", \"lxxx\", \"xc\"];\nconst ROMAN_HUNDREDS = [\"\", \"c\", \"cc\", \"ccc\", \"cd\", \"d\", \"dc\", \"dcc\", \"dccc\", \"cm\"];\nconst ROMAN_THOUSANDS = [\"\", \"m\", \"mm\", \"mmm\"];\n\nfunction toRoman(value: number): string {\n // Word renders anything outside the classical range as a plain number rather than nothing.\n if (value <= 0 || value >= 4000) return String(value);\n return (\n ROMAN_THOUSANDS[Math.floor(value / 1000)] +\n ROMAN_HUNDREDS[Math.floor((value % 1000) / 100)] +\n ROMAN_TENS[Math.floor((value % 100) / 10)] +\n ROMAN_ONES[value % 10]\n );\n}\n\n/** `1 \u2192 a`, `26 \u2192 z`, `27 \u2192 aa` \u2014 Word repeats the letter rather than carrying like a base-26 number. */\nfunction toLetter(value: number): string {\n if (value <= 0) return String(value);\n const wrapped = value % 780 === 0 ? 780 : value % 780;\n const repeats = Math.floor((wrapped - 1) / 26) + 1;\n return \"abcdefghijklmnopqrstuvwxyz\".charAt((wrapped - 1) % 26).repeat(repeats);\n}\n\nconst RENDERERS: Record<string, Renderer> = {\n // ST_NumberFormat tokens (w:pgNumType/@w:fmt, w:numFmt).\n decimal: (v) => String(v),\n lowerRoman: toRoman,\n upperRoman: (v) => toRoman(v).toUpperCase(),\n lowerLetter: toLetter,\n upperLetter: (v) => toLetter(v).toUpperCase(),\n // Field `\\*` general-formatting switch arguments.\n Arabic: (v) => String(v),\n roman: toRoman,\n ROMAN: (v) => toRoman(v).toUpperCase(),\n alphabetic: toLetter,\n ALPHABETIC: (v) => toLetter(v).toUpperCase(),\n};\n\n/**\n * Render `value` in `format`. `format` may be an `ST_NumberFormat` token or a `\\*` switch argument;\n * an absent or unrecognized one renders as decimal.\n */\nexport function formatPageNumber(value: number, format?: string | null): string {\n const renderer = format ? RENDERERS[format] : undefined;\n return (renderer ?? RENDERERS.decimal)(value);\n}\n", "/**\n * Unrounded natural line heights for the paginated export (issue #850).\n *\n * Chromium lays a `line-height: normal` line out at the font's natural line height \u2014 ascent +\n * descent + line gap from its metrics \u2014 but first rounds that to a whole CSS pixel (0.75 pt). The\n * converter relies on `normal` for single spacing and on `calc(1lh * m)` for Word's `auto`\n * multiples, so every line inherits the rounding: 11 pt Carlito advances 18 px instead of\n * 17.90 px, and the error accumulates down the page and moves page breaks. An explicit length keeps\n * Chromium's 1/64-pixel layout precision, so this pass replaces each `normal` with the same font's\n * natural height measured where rounding is negligible.\n */\n\n/** Font size the natural line height is probed at: rounding to a pixel there is < 0.0005 em. */\nconst PROBE_FONT_SIZE_PX = 1000;\n\n/** Probe text for an element whose own text the primary font covers (Latin, through U+024F). */\nconst LATIN_PROBE = \"Hg\";\n\n/** At most this many distinct characters of an element's own text go into its probe. */\nconst MAX_PROBE_CHARACTERS = 256;\n\n/**\n * What to measure an element's natural line height with. Under `normal`, Chromium also grows a line\n * for the fallback fonts its text needs (a CJK run in a Calibri paragraph lays out taller than Latin\n * text), so an element whose own text goes beyond Latin is probed with those characters themselves.\n */\nfunction probeText(element: Element): string {\n const characters = new Set<string>();\n for (const node of Array.from(element.childNodes)) {\n if (node.nodeType !== 3) continue;\n for (const character of node.textContent ?? \"\") {\n if (/\\s/.test(character)) continue;\n characters.add(character);\n if (characters.size >= MAX_PROBE_CHARACTERS) break;\n }\n }\n const beyondLatin = Array.from(characters).filter((character) => character.codePointAt(0)! > 0x024f);\n return beyondLatin.length === 0 ? LATIN_PROBE : LATIN_PROBE + beyondLatin.sort().join(\"\");\n}\n\n/**\n * A cached measure of a font's natural line height as a fraction of its size, probed at\n * {@link PROBE_FONT_SIZE_PX} with the given text.\n */\nfunction naturalLineHeightRatios(document: Document): (style: CSSStyleDeclaration, text: string) => number {\n const ratios = new Map<string, number>();\n return (style, text) => {\n const key = `${style.fontStyle}|${style.fontWeight}|${style.fontStretch}|${style.fontFamily}|${text}`;\n let ratio = ratios.get(key);\n if (ratio === undefined) {\n const probe = document.createElement(\"span\");\n probe.setAttribute(\"aria-hidden\", \"true\");\n probe.style.cssText = \"position:absolute;left:-100000px;top:0;visibility:hidden;\"\n + \"display:inline-block;white-space:nowrap;line-height:normal;margin:0;padding:0;border:0;\";\n probe.style.fontFamily = style.fontFamily;\n probe.style.fontStyle = style.fontStyle;\n probe.style.fontWeight = style.fontWeight;\n probe.style.fontStretch = style.fontStretch;\n probe.style.fontSize = `${PROBE_FONT_SIZE_PX}px`;\n probe.textContent = text;\n (document.body ?? document.documentElement).appendChild(probe);\n ratio = probe.getBoundingClientRect().height / PROBE_FONT_SIZE_PX;\n probe.remove();\n ratios.set(key, ratio);\n }\n return ratio;\n };\n}\n\n/**\n * Give every element under `root` whose computed `line-height` is `normal` an explicit line height\n * equal to its own font's unrounded natural line height (including any fallback font its own text\n * needs). Elements are collected before any is\n * changed, because an explicit value on a parent would otherwise be inherited by children that\n * should keep sizing their lines from their own font. Returns how many elements changed.\n */\nexport function applyUnroundedNormalLineHeights(root: Element): number {\n const document = root.ownerDocument;\n const view = document.defaultView;\n if (!view) return 0;\n const ratioFor = naturalLineHeightRatios(document);\n\n const targets: Array<{ element: HTMLElement; lineHeight: number }> = [];\n for (const element of [root, ...Array.from(root.querySelectorAll(\"*\"))]) {\n if (!(element instanceof view.HTMLElement)) continue;\n const style = view.getComputedStyle(element);\n if (style.lineHeight !== \"normal\") continue;\n const fontSize = Number.parseFloat(style.fontSize);\n const ratio = ratioFor(style, probeText(element));\n if (!(fontSize > 0) || !(ratio > 0)) continue;\n targets.push({ element, lineHeight: ratio * fontSize });\n }\n for (const { element, lineHeight } of targets) {\n element.style.setProperty(\"line-height\", `${lineHeight.toFixed(4)}px`);\n }\n return targets.length;\n}\n\n/** The elements the converter writes a Word paragraph as. */\nconst PARAGRAPH_SELECTOR = \"p, h1, h2, h3, h4, h5, h6\";\n\n/** How far Chromium puts the first baseline below the top of a paragraph shaped like this one. */\nfunction baselineOffsets(document: Document): (paragraph: CSSStyleDeclaration, child: CSSStyleDeclaration) => number {\n const offsets = new Map<string, number>();\n const host = document.body ?? document.documentElement;\n return (paragraph, child) => {\n const key = [paragraph.fontStyle, paragraph.fontWeight, paragraph.fontStretch, paragraph.fontFamily,\n paragraph.fontSize, paragraph.lineHeight, child.fontStyle, child.fontWeight, child.fontStretch,\n child.fontFamily, child.fontSize, child.lineHeight, child.verticalAlign, child.top].join(\"|\");\n let offset = offsets.get(key);\n if (offset === undefined) {\n const probe = document.createElement(\"div\");\n probe.setAttribute(\"aria-hidden\", \"true\");\n probe.style.cssText = \"position:absolute;left:-100000px;top:0;visibility:hidden;width:max-content;\"\n + \"margin:0;padding:0;border:0;white-space:nowrap;\";\n for (const property of [\"font-family\", \"font-style\", \"font-weight\", \"font-stretch\", \"font-size\", \"line-height\"])\n probe.style.setProperty(property, paragraph.getPropertyValue(property));\n const span = document.createElement(\"span\");\n for (const property of [\"font-family\", \"font-style\", \"font-weight\", \"font-stretch\", \"font-size\", \"line-height\",\n \"vertical-align\", \"position\", \"top\"])\n span.style.setProperty(property, child.getPropertyValue(property));\n const marker = document.createElement(\"span\");\n marker.style.cssText = \"display:inline-block;width:0;height:0;vertical-align:baseline;margin:0;padding:0;border:0;\";\n span.append(marker, LATIN_PROBE);\n probe.append(span);\n host.appendChild(probe);\n offset = marker.getBoundingClientRect().bottom - probe.getBoundingClientRect().top;\n probe.remove();\n offsets.set(key, offset);\n }\n return offset;\n };\n}\n\n/**\n * Word places an exact-spaced line's baseline at 80% of its height, independent of font size\n * (issue #882; fixtures/exact-line-spacing/word.json). Move each run by its own font's offset,\n * retaining the converter's top alignment so mixed font sizes cannot enlarge the line boxes.\n */\nexport function alignExactLineBaselines(root: Element): void {\n const view = root.ownerDocument.defaultView;\n if (!view) return;\n const offsetFor = baselineOffsets(root.ownerDocument);\n for (const run of Array.from(root.querySelectorAll<HTMLElement>(\"[data-docx-exact-run]\"))) {\n const paragraph = run.closest(PARAGRAPH_SELECTOR);\n if (!paragraph || !movable(run, view)) continue;\n const style = view.getComputedStyle(paragraph);\n if (!style.getPropertyValue(\"--docx-exact-line-height\")) continue;\n const height = Number.parseFloat(style.lineHeight);\n const runStyle = view.getComputedStyle(run);\n if (!(height > 0) || runStyle.display !== \"inline\" || runStyle.verticalAlign !== \"top\") continue;\n const top = Number.parseFloat(runStyle.top) || 0;\n const offset = offsetFor(style, runStyle) - top;\n const shift = height * 0.8 - offset;\n if (!Number.isFinite(shift)) continue;\n run.style.position = \"relative\";\n run.style.top = `${(top + shift).toFixed(4)}px`;\n }\n}\n\nconst EXACT_INK_ATTRIBUTE = \"data-docx-exact-ink\";\n\n/**\n * Exact line boxes still occupy their declared height when their glyphs extend outside them.\n * Word lets that ink enter the margins. Expand only the paint clip, after placement, for plain\n * inline text whose paragraph fits its band and whose ink cannot enter a running story or leave\n * the paper. Block geometry and pagination budgets remain unchanged.\n */\nexport function preserveExactLineInk(content: HTMLElement, topLimit: number, bottomLimit: number, paper: DOMRect): void {\n const view = content.ownerDocument.defaultView;\n if (!view) return;\n const band = content.getBoundingClientRect();\n const scale = band.width / content.offsetWidth || 1;\n let above = 0;\n let below = 0;\n for (const paragraph of Array.from(content.querySelectorAll<HTMLElement>(PARAGRAPH_SELECTOR))) {\n if (!view.getComputedStyle(paragraph).getPropertyValue(\"--docx-exact-line-height\")) continue;\n const box = paragraph.getBoundingClientRect();\n if (box.top < band.top - 0.5 || box.bottom > band.bottom + 0.5) continue;\n if (!paragraph.querySelector(\"[data-docx-exact-run]\") || paragraph.querySelector(\"img, svg, canvas, video, object, iframe\"))\n continue;\n if (Array.from(paragraph.querySelectorAll<HTMLElement>(\"*\")).some(element => {\n const style = view.getComputedStyle(element);\n return style.display !== \"inline\" || ![\"static\", \"relative\"].includes(style.position);\n })) continue;\n const range = content.ownerDocument.createRange();\n range.selectNodeContents(paragraph);\n const rects = Array.from(range.getClientRects()).filter(rect => rect.width > 0 && rect.height > 0);\n if (rects.length === 0) continue;\n const top = Math.min(...rects.map(rect => rect.top));\n const bottom = Math.max(...rects.map(rect => rect.bottom));\n if (top < topLimit || bottom > bottomLimit) continue;\n const extra = Math.max(0, band.top - top, bottom - band.bottom);\n if (extra === 0) continue;\n paragraph.setAttribute(EXACT_INK_ATTRIBUTE, \"true\");\n above = Math.max(above, band.top - top);\n below = Math.max(below, bottom - band.bottom);\n }\n if (above > 0 || below > 0) {\n const up = Math.ceil(above / scale * 64) / 64;\n const down = Math.ceil(below / scale * 64) / 64;\n // overflow-clip-margin is ignored by Chromium when the other axis stays visible. A pixel\n // inset clips each vertical edge independently and keeps horizontal clipping at the paper.\n content.style.overflowY = \"visible\";\n content.style.clipPath = `inset(${-up}px ${(band.right - paper.right) / scale}px ${-down}px ${(paper.left - band.left) / scale}px)`;\n }\n}\n\n/** Read the pixel inset used for the exact-text paint clip, including CSS's 1-4 value shorthand. */\nexport function insetClipBounds(value: string, box: DOMRect, scale: number):\n { top: number; right: number; bottom: number; left: number } | null {\n const match = /^inset\\(((?:-?\\d+(?:\\.\\d+)?px\\s*){1,4})\\)$/.exec(value);\n if (!match) return null;\n const parts = match[1].trim().split(/\\s+/).map(Number.parseFloat);\n const [top, right = top, bottom = top, left = right] = parts;\n return { top: box.top + top * scale, right: box.right - right * scale,\n bottom: box.bottom - bottom * scale, left: box.left + left * scale };\n}\n\n/** Only inline ink in a fitting exact-spaced paragraph may use the expanded paint clip. */\nexport function isPreservedExactLineInk(element: HTMLElement, content: HTMLElement): boolean {\n const paragraph = element.closest<HTMLElement>(`[${EXACT_INK_ATTRIBUTE}]`);\n const view = content.ownerDocument.defaultView;\n if (!paragraph || !view || element === paragraph || !content.contains(paragraph)) return false;\n if (view.getComputedStyle(element).display !== \"inline\") return false;\n const band = content.getBoundingClientRect();\n const box = paragraph.getBoundingClientRect();\n const clip = insetClipBounds(view.getComputedStyle(content).clipPath, band, band.width / content.offsetWidth || 1);\n const ink = element.getBoundingClientRect();\n return clip !== null && box.top >= band.top - 0.5 && box.bottom <= band.bottom + 0.5\n && ink.top >= clip.top - 0.02 && ink.bottom <= clip.bottom + 0.02;\n}\n\n/**\n * Put every exported baseline where Word puts it, to a fraction of a pixel (issue #942).\n *\n * Word sets a line's baseline its natural line height less the font's descent below the line's top: the\n * font's ascent, with any line gap above it. Chromium instead rounds the ascent and descent to whole pixels\n * and floors half the remaining leading, so the baseline lands up to a pixel off; for 11 pt Calibri and 12 pt\n * Arial it is one pixel high. Word also puts a font's line gap above the text where CSS splits it, which moves\n * Arial's baseline a little further. That offset is the same on every line of a paragraph, whatever its position, so\n * it is measured once per paragraph shape in a probe and taken back with relative positioning, which moves the\n * glyphs without changing any line box: pagination and line pitch are untouched.\n *\n * Each direct inline child of a paragraph is moved, composing with a `top` it already has. A child positioned\n * some other way, or holding an image, an SVG or an absolutely positioned element (for which a relative child\n * would become the containing block), is left alone. Run once the page tree is final: the moved boxes count\n * toward a page band's overflow, which the export's clipping check must not see.\n * Returns how many children moved.\n */\nexport function alignBaselinesToWord(root: Element): number {\n const document = root.ownerDocument;\n const view = document.defaultView;\n if (!view) return 0;\n const descents = new Map<string, number>();\n const ratioFor = naturalLineHeightRatios(document);\n const chromiumOffset = baselineOffsets(document);\n\n /** The font's descent as a fraction of its size, read where rounding is negligible. */\n const descentRatio = (style: CSSStyleDeclaration): number => {\n // The canvas font shorthand takes no percentage stretch (computed styles give one), and ignores a value it\n // cannot parse, so the stretch is left out and an assignment that did not take counts as unmeasurable.\n const font = `${style.fontStyle} ${style.fontWeight} ${PROBE_FONT_SIZE_PX}px ${style.fontFamily}`;\n let ratio = descents.get(font);\n if (ratio === undefined) {\n const context = document.createElement(\"canvas\").getContext(\"2d\");\n ratio = Number.NaN;\n if (context) {\n context.font = font;\n if (context.font.includes(`${PROBE_FONT_SIZE_PX}px`))\n ratio = context.measureText(LATIN_PROBE).fontBoundingBoxDescent / PROBE_FONT_SIZE_PX;\n }\n descents.set(font, ratio);\n }\n return ratio;\n };\n\n const moves: Array<{ element: HTMLElement; top: number }> = [];\n for (const paragraph of Array.from(root.querySelectorAll(PARAGRAPH_SELECTOR))) {\n if (!(paragraph instanceof view.HTMLElement)) continue;\n const style = view.getComputedStyle(paragraph);\n // Probe with a child that sits on the baseline (not a raised or lowered run), preferably in the\n // paragraph's own font rather than, say, a list marker's.\n const candidates = Array.from(paragraph.children).filter((child): child is HTMLElement =>\n child instanceof view.HTMLElement && (child.textContent ?? \"\").trim() !== \"\" && movable(child, view) &&\n view.getComputedStyle(child).display === \"inline\" && view.getComputedStyle(child).verticalAlign === \"baseline\");\n const first = candidates.find((child) => view.getComputedStyle(child).fontFamily === style.fontFamily) ??\n candidates[0];\n if (!first) continue;\n const lineHeight = Number.parseFloat(style.lineHeight);\n const fontSize = Number.parseFloat(style.fontSize);\n // Word's placement is recorded for natural line heights (single spacing and auto multiples, which the\n // converter builds on the paragraph's natural height), not for exact or at-least ones.\n if (!(lineHeight > 0) || !(fontSize > 0) ||\n Math.abs(lineHeight - ratioFor(style, LATIN_PROBE) * fontSize) > 0.01) continue;\n const descent = descentRatio(style);\n if (!(descent > 0)) continue;\n const childStyle = view.getComputedStyle(first);\n // Word: the extra height of a multiple goes below the text (issue #908), so the baseline is one natural\n // line height less the descent below the line's top, however tall the line is.\n const shift = (lineHeight - descent * fontSize) - chromiumOffset(style, childStyle);\n // The correction is Chromium's rounding plus the half of any line gap it puts below the text, which Word\n // puts above: a fraction of the line. Half a line or more means the font does not follow this model.\n if (!Number.isFinite(shift) || Math.abs(shift) < 1 / 64 || Math.abs(shift) >= lineHeight / 2) continue;\n for (const child of Array.from(paragraph.children))\n if (child instanceof view.HTMLElement && movable(child, view)) {\n // Compose with the offset the child already has (a run's w:position, or the lift of an auto multiple),\n // which may come from a stylesheet rule rather than its own style attribute.\n const top = Number.parseFloat(view.getComputedStyle(child).top);\n moves.push({ element: child, top: (Number.isFinite(top) ? top : 0) + shift });\n }\n }\n for (const { element, top } of moves) {\n element.style.setProperty(\"position\", \"relative\");\n element.style.setProperty(\"top\", `${top.toFixed(4)}px`);\n }\n return moves.length;\n}\n\n/** Whether relative positioning can move a paragraph child without disturbing anything laid out from it. */\nfunction movable(element: HTMLElement, view: Window & typeof globalThis): boolean {\n const position = view.getComputedStyle(element).position;\n if (position !== \"static\" && position !== \"relative\") return false;\n // Inline boxes share the line's baseline; tab stops and leaders are inline-blocks on the same line.\n if (![\"inline\", \"inline-block\"].includes(view.getComputedStyle(element).display)) return false;\n if (element.querySelector(\"img, svg\")) return false;\n for (const descendant of Array.from(element.querySelectorAll(\"*\")))\n if ([\"absolute\", \"fixed\"].includes(view.getComputedStyle(descendant).position)) return false;\n return true;\n}\n", "/**\n * Page geometry \u2014 the document's own page setup, and the scaling a view applies to show it.\n *\n * A Word document's line breaking is authored against ONE width: the text column its\n * `w:sectPr` defines (page size minus margins). That width is a property of the document,\n * not of the device showing it, which is why the converter emits it on every section wrapper\n * (`data-page-width`, `data-content-width`, the four margins, \u2026) in every render mode.\n *\n * A view that instead sizes the column from the viewport gets a different line-breaking\n * result on every screen, and \u2014 once a run is enlarged past what the narrowed column can\n * hold \u2014 content that no longer fits the page at all. Word and LibreOffice never do that:\n * they keep the column fixed and ZOOM the page to fit the window. This module is the shared\n * owner of both halves \u2014 reading the geometry, and computing/applying that zoom \u2014 so the\n * paginated view (`pagination.ts`) and the continuous view (`editor.ts` via `viewport.ts`)\n * agree on what a page is instead of each inventing its own.\n */\n\n/** Page dimensions extracted from a section wrapper's data attributes (in points). */\nexport interface PageDimensions {\n /** Page width in points */\n pageWidth: number;\n /** Page height in points */\n pageHeight: number;\n /** Content area width (page minus margins) in points */\n contentWidth: number;\n /** Content area height (page minus margins) in points */\n contentHeight: number;\n /** Top margin in points */\n marginTop: number;\n /** Right margin in points */\n marginRight: number;\n /** Bottom margin in points */\n marginBottom: number;\n /** Left margin in points */\n marginLeft: number;\n /** `w:pgMar/@w:header` \u2014 page top edge to the TOP of the header story, in points. */\n headerDistance: number;\n /** `w:pgMar/@w:footer` \u2014 page bottom edge to the BOTTOM of the footer story, in points. */\n footerDistance: number;\n /** @deprecated Misnamed alias of {@link headerDistance} \u2014 it never held a height. */\n headerHeight: number;\n /** @deprecated Misnamed alias of {@link footerDistance} \u2014 it never held a height. */\n footerHeight: number;\n}\n\n/** US Letter portrait \u2014 Word's default, and this module's fallback for an unstamped section. */\nexport const DEFAULT_PAGE_WIDTH = 612;\nexport const DEFAULT_PAGE_HEIGHT = 792;\nexport const DEFAULT_MARGIN = 72; // 1 inch\n/** Default header/footer distance from the paper edge (0.5 inch). */\nexport const DEFAULT_HEADER_FOOTER_DISTANCE = 36;\n/** @deprecated Misnamed alias of {@link DEFAULT_HEADER_FOOTER_DISTANCE}. */\nexport const DEFAULT_HEADER_FOOTER_HEIGHT = DEFAULT_HEADER_FOOTER_DISTANCE;\n\n/** Converts pixels to points (assuming 96 DPI screen). */\nexport function pxToPt(px: number): number {\n return px * 0.75; // 72 points / 96 pixels\n}\n\n/** Converts points to pixels (assuming 96 DPI screen). */\nexport function ptToPx(pt: number): number {\n return pt / 0.75;\n}\n\nfunction finiteDataNumber(value: string | undefined, fallback: number): number {\n if (value === undefined || value.trim() === \"\") return fallback;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : fallback;\n}\n\nfunction positiveDataNumber(value: string | undefined, fallback: number): number {\n const parsed = finiteDataNumber(value, fallback);\n return parsed > 0 ? parsed : fallback;\n}\n\nfunction nonNegativeDataNumber(value: string | undefined, fallback: number): number {\n const parsed = finiteDataNumber(value, fallback);\n return parsed >= 0 ? parsed : fallback;\n}\n\nfunction positiveFinite(value: number, fallback: number): number {\n return Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\n/**\n * Parses page dimensions from a section wrapper's data attributes.\n *\n * Zero is meaningful for margins and header/footer distances, so truthiness-based parsing would\n * silently replace a valid edge-to-edge section with one-inch defaults. Number() also rejects\n * partial numeric strings (for example `612evil`) instead of letting malformed staging markup\n * leak non-deterministic CSS geometry into pagination.\n */\nexport function parseSectionDimensions(section: HTMLElement): PageDimensions {\n const pageWidth = positiveDataNumber(section.dataset.pageWidth, DEFAULT_PAGE_WIDTH);\n const pageHeight = positiveDataNumber(section.dataset.pageHeight, DEFAULT_PAGE_HEIGHT);\n const marginTop = finiteDataNumber(section.dataset.marginTop, DEFAULT_MARGIN);\n const marginRight = finiteDataNumber(section.dataset.marginRight, DEFAULT_MARGIN);\n const marginBottom = finiteDataNumber(section.dataset.marginBottom, DEFAULT_MARGIN);\n const marginLeft = finiteDataNumber(section.dataset.marginLeft, DEFAULT_MARGIN);\n const derivedContentWidth = positiveFinite(pageWidth - marginLeft - marginRight, pageWidth);\n const derivedContentHeight = positiveFinite(pageHeight - marginTop - marginBottom, pageHeight);\n const contentWidth = positiveDataNumber(section.dataset.contentWidth, derivedContentWidth);\n const contentHeight = positiveDataNumber(section.dataset.contentHeight, derivedContentHeight);\n // `data-header-height`/`data-footer-height` carry `w:pgMar/@w:header` and `@w:footer`, which\n // are distances from the paper edge \u2014 the attribute names predate that being understood.\n const headerDistance = nonNegativeDataNumber(\n section.dataset.headerHeight,\n DEFAULT_HEADER_FOOTER_DISTANCE,\n );\n const footerDistance = nonNegativeDataNumber(\n section.dataset.footerHeight,\n DEFAULT_HEADER_FOOTER_DISTANCE,\n );\n\n return {\n pageWidth,\n pageHeight,\n contentWidth,\n contentHeight,\n marginTop,\n marginRight,\n marginBottom,\n marginLeft,\n headerDistance,\n footerDistance,\n headerHeight: headerDistance,\n footerHeight: footerDistance,\n };\n}\n\n/**\n * Where a page's three bands sit, in points measured from the page's TOP edge.\n *\n * Resolved by {@link resolvePageBands} from the section's page setup plus the measured\n * height of the running content actually selected for that page.\n */\nexport interface PageBands {\n /** Top of the header band; it grows downward from here. */\n headerTop: number;\n /** Height of the header band (the measured header story, clipped to the body's top). */\n headerHeight: number;\n /** Top of the body text band. */\n bodyTop: number;\n /** Height of the body text band. */\n bodyHeight: number;\n /** Top of the footer band; it grows upward, so its BOTTOM is the fixed edge. */\n footerTop: number;\n /** Height of the footer band (the measured footer story, clipped to the body's bottom). */\n footerHeight: number;\n}\n\n/**\n * A body band this short cannot hold a single line, and a page that can hold nothing sends\n * every block down the oversized-block path. Running content may push the body \u2014 Word does\n * that \u2014 but it may not delete it, so a pathological header/footer gives this much back.\n */\nexport const MIN_BODY_BAND_PT = 12;\n\n/**\n * Word's page band model.\n *\n * `w:header`/`w:footer` are DISTANCES from the paper edge to the running content, and\n * `w:top`/`w:bottom` are distances from the paper edge to the body text \u2014 four independent\n * numbers, not two nested boxes. The header is laid out from `headerDistance` downward and\n * the footer from `footerDistance` upward; the body then starts at the top margin unless the\n * header has already run past it, and ends at the bottom margin unless the footer has\n * already climbed above it.\n *\n * Anchoring the bands to the margins instead \u2014 header bottom-aligned to `marginTop`, footer\n * top-aligned to `pageHeight - marginBottom` \u2014 collapses both toward the body by exactly\n * `margin - distance`, which is what this function replaced.\n */\nexport function resolvePageBands(\n dims: PageDimensions,\n headerContentHeight: number,\n footerContentHeight: number,\n): PageBands {\n const headerTop = dims.headerDistance;\n const footerBottom = dims.pageHeight - dims.footerDistance;\n\n // A page with NO running story has nothing below the paper edge to push the body: the\n // distance alone reserves no space. (Word's own empty header is still a story \u2014 one empty\n // paragraph with a real line height \u2014 so zero here means absent, not blank.)\n const headerReach = headerContentHeight > 0 ? headerTop + headerContentHeight : 0;\n const footerReach = footerContentHeight > 0 ? footerBottom - footerContentHeight : dims.pageHeight;\n\n let bodyTop = Math.max(dims.marginTop, headerReach);\n let bodyBottom = Math.min(dims.pageHeight - dims.marginBottom, footerReach);\n\n // Hand back whatever the bands over-claimed, in proportion to how far each grew past its\n // own margin, so a tall header does not silently consume the whole page.\n const shortfall = MIN_BODY_BAND_PT - (bodyBottom - bodyTop);\n if (shortfall > 0) {\n const headerGrowth = bodyTop - dims.marginTop;\n const footerGrowth = dims.pageHeight - dims.marginBottom - bodyBottom;\n const growth = headerGrowth + footerGrowth;\n if (growth > 0) {\n bodyTop -= (shortfall * headerGrowth) / growth;\n bodyBottom += (shortfall * footerGrowth) / growth;\n }\n }\n\n return {\n headerTop,\n headerHeight: Math.max(0, Math.min(headerContentHeight, bodyTop - headerTop)),\n bodyTop,\n bodyHeight: Math.max(0, bodyBottom - bodyTop),\n footerTop: footerBottom - Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom)),\n footerHeight: Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom)),\n };\n}\n\n/** Every section wrapper under `root`, or `root` itself when the render stamped none. */\nexport function sectionWrappers(root: HTMLElement): HTMLElement[] {\n const sections = Array.from(root.querySelectorAll<HTMLElement>(\"[data-section-index]\"));\n return sections.length > 0 ? sections : [root];\n}\n\n/**\n * The zoom that fits `naturalPt` of document into `availablePx` of window, never magnifying\n * past `max`. Below `MIN_FIT_SCALE` the text would be unreadable, so the view stops shrinking\n * and lets the surface scroll horizontally instead \u2014 the same trade a word processor makes.\n */\nexport const MIN_FIT_SCALE = 0.25;\n\nexport function fitScale(availablePx: number, naturalPt: number, max = 1): number {\n if (!(availablePx > 0) || !(naturalPt > 0)) return max;\n const naturalPx = ptToPx(naturalPt);\n if (naturalPx <= availablePx) return max;\n return Math.max(MIN_FIT_SCALE, Math.min(max, availablePx / naturalPx));\n}\n\n/**\n * Applies a zoom factor to `el`.\n *\n * Prefers CSS `zoom`, which participates in layout \u2014 so the element's own box shrinks, the\n * page keeps its scrollbars honest, and caret/hit-testing geometry stays correct inside a\n * `contenteditable`. `transform: scale()` is the fallback for engines without `zoom`; it does\n * not affect layout, so the caller must compensate for the space the unscaled box still\n * reserves, which is what `naturalPt` is for.\n */\nexport function applyZoom(\n el: HTMLElement,\n scale: number,\n naturalPt?: { width: number; height: number },\n): void {\n if (scale === 1) {\n el.style.removeProperty(\"zoom\");\n el.style.removeProperty(\"transform\");\n el.style.removeProperty(\"transform-origin\");\n el.style.removeProperty(\"margin-right\");\n el.style.removeProperty(\"margin-bottom\");\n return;\n }\n const zoomSupported =\n typeof CSS !== \"undefined\" && typeof CSS.supports === \"function\" && CSS.supports(\"zoom\", \"0.5\");\n if (zoomSupported) {\n el.style.zoom = String(scale);\n return;\n }\n el.style.transform = `scale(${scale})`;\n el.style.transformOrigin = \"top left\";\n if (naturalPt) {\n el.style.marginRight = `-${ptToPx(naturalPt.width * (1 - scale))}px`;\n el.style.marginBottom = `-${ptToPx(naturalPt.height * (1 - scale))}px`;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["pagination_exports", "__export", "DRAWING_HOST_ATTRIBUTE", "HOSTS_PROMOTED_DRAWING_ATTRIBUTE", "PaginationEngine", "clearPageCitationHighlight", "collapsedBlockMarginsPx", "createUnavailablePageMap", "measureRenderedSource", "navigateToPageCitation", "paginateHtml", "renderedSourceMeasurerExpression", "ROMAN_ONES", "ROMAN_TENS", "ROMAN_HUNDREDS", "ROMAN_THOUSANDS", "toRoman", "value", "toLetter", "wrapped", "repeats", "RENDERERS", "v", "formatPageNumber", "format", "PARAGRAPH_SELECTOR", "baselineOffsets", "document", "offsets", "host", "paragraph", "child", "key", "offset", "probe", "property", "span", "marker", "alignExactLineBaselines", "root", "view", "offsetFor", "run", "movable", "style", "height", "runStyle", "top", "shift", "EXACT_INK_ATTRIBUTE", "preserveExactLineInk", "content", "topLimit", "bottomLimit", "paper", "band", "scale", "above", "below", "box", "element", "range", "rects", "rect", "bottom", "up", "down", "insetClipBounds", "value", "match", "parts", "right", "left", "movable", "element", "view", "position", "descendant", "pxToPt", "px", "ptToPx", "pt", "finiteDataNumber", "value", "fallback", "parsed", "positiveDataNumber", "nonNegativeDataNumber", "positiveFinite", "parseSectionDimensions", "section", "pageWidth", "pageHeight", "marginTop", "marginRight", "marginBottom", "marginLeft", "derivedContentWidth", "derivedContentHeight", "contentWidth", "contentHeight", "headerDistance", "footerDistance", "MIN_BODY_BAND_PT", "resolvePageBands", "dims", "headerContentHeight", "footerContentHeight", "headerTop", "footerBottom", "headerReach", "footerReach", "bodyTop", "bodyBottom", "shortfall", "headerGrowth", "footerGrowth", "growth", "samePageBox", "a", "b", "createUnavailablePageMap", "documentVersion", "rendererFingerprint", "mode", "activePageCitationHighlights", "clearPageCitationHighlight", "root", "active", "style", "navigateToPageCitation", "citation", "options", "byAttribute", "name", "value", "within", "node", "fragment", "target", "page", "addedClass", "inlineStyles", "MAX_FOOTNOTE_AREA_RATIO", "FOOTNOTE_MEASUREMENT_GUARD_PT", "FOOTNOTE_SOURCE_POSITION_ATTR", "collapsedBlockMarginsPx", "view", "element", "collapseMarginChain", "edge", "margins", "current", "collapsesThroughTo", "positive", "negative", "parent", "border", "padding", "nodes", "child", "childStyle", "DRAWING_HOST_ATTRIBUTE", "HOSTS_PROMOTED_DRAWING_ATTRIBUTE", "rendersOnlyItsLine", "createRenderedSourceMeasurer", "clipInset", "drawingHostAttribute", "hostsPromotedDrawingAttribute", "visibleWithinPage", "pageRect", "rect", "left", "top", "right", "bottom", "clips", "ancestor", "clipsX", "clipsY", "ancestorRect", "scale", "inset", "own", "ownResult", "ownMeasurable", "hostsPromotedDrawing", "hostAnchorId", "promoted", "drawing", "rendered", "childRect", "visible", "measureRenderedSource", "insetClipBounds", "renderedSourceMeasurerExpression", "PaginationEngine", "staging", "container", "ownerDocument", "pages", "pageNumber", "sections", "sectionsToProcess", "referencedFootnoteIds", "referencedCommentIds", "section", "reference", "id", "source", "commentId", "runs", "dims", "parseSectionDimensions", "previous", "sectionIndex", "run", "precedingPage", "precedingPageInSection", "precedingDisplayedPageNumber", "filler", "blocks", "sectionDims", "columnCount", "gap", "sectionPages", "pageInSection", "displayedPageNumber", "header", "footer", "result", "error", "fragments", "requiredAnchorIds", "measuredAnchorIds", "emittedFragmentCounts", "pointPerRenderedX", "pointPerRenderedY", "anchorId", "visibleRect", "fragmentIndex", "fragmentId", "missingAnchor", "first", "second", "scope", "destination", "excludeRegistries", "candidates", "next", "bounds", "sourceRoot", "zeroHeightExplicitBreakCarrier", "fragmentCounts", "activeCanonicalIds", "activeBareAnchorIds", "makeInactive", "story", "mayOwnActiveEditorAnchor", "bareAnchorId", "activeStagingCanonicalIds", "visibleIds", "map", "index", "rawStart", "start", "totalPages", "boxes", "box", "markers", "numbering", "displayed", "marker", "kind", "format", "formatPageNumber", "children", "nestedSectionIndex", "nestedBlocks", "endnoteBlocks", "marginTopPx", "marginBottomPx", "heightPt", "pxToPt", "marginTopPt", "marginBottomPt", "isPageBreak", "sectionChildren", "list", "items", "item", "sectionStyle", "rule", "clonedRule", "ruleStyle", "property", "listStyle", "itemIndex", "ownerAnchorId", "paragraphs", "paragraphIndex", "paragraph", "clone", "owner", "itemId", "listStyleType", "columnGapPt", "sequential", "maxFragmentHeight", "isBreak", "makeContainer", "slice", "end", "measured", "candidate", "candidateMeasured", "host", "spaceBefore", "block", "availableHeightPt", "maximumHeight", "height", "make", "limit", "whole", "count", "measurementHost", "measuredElement", "startIndex", "firstBlock", "chain", "lastIndex", "nextBlock", "previousMarginBottomPt", "isFirstOnPage", "bodyHeight", "previousBlock", "existingFootnoteIds", "knownIds", "newIds", "wrapper", "table", "body", "rows", "retainAnchor", "wrapperClone", "tableClone", "bodyClone", "row", "tableElement", "minimumContentHeight", "maximumFragmentHeight", "groups", "isFirst", "isLast", "emergencyGraphemeBreaks", "textNodes", "textStarts", "lastTextByField", "walker", "flattenedChunks", "flattenedLength", "textNode", "field", "flattenedText", "invisible", "firstVisibleOffset", "lastVisibleOffset", "graphemeBoundaries", "addCandidate", "textOffset", "atomic", "whitespace", "match", "matchesSinceCheckpoint", "offset", "last", "childIndex", "endpoints", "text", "SegmenterConstructor", "boundaries", "segmentCount", "part", "lastCodeUnit", "beforeOffset", "beforeCodePoint", "afterCodePoint", "before", "after", "eastAsian", "codePointBefore", "nonBreaking", "allowInlineRoot", "unsupportedDescendants", "paragraphStyle", "descendant", "range", "retainSourceIdentity", "isFinalFragment", "contents", "head", "tail", "elements", "selector", "headIds", "headAnchors", "anchor", "fits", "largestFit", "lastEndpoint", "lastRange", "lastContents", "lastHead", "low", "high", "best", "middle", "endpoint", "headRange", "headContents", "tailRange", "tailContents", "effectiveMarginTopPt", "split", "headMeasured", "tailMeasured", "registry", "registryEl", "sectionWidths", "idx", "contentWidth", "defaultContentWidth", "entries", "entry", "hfType", "content", "measuredHeight", "footnoteId", "continuation", "fallback", "refs", "ids", "ref", "sourceIndex", "sourceContent", "sentinel", "footnote", "fittingElements", "number", "footnoteIds", "partialFootnotes", "hasContinuation", "measureContainer", "partialById", "partial", "fitting", "remaining", "sourcePosition", "canSplitCandidate", "footnoteElement", "forceProgress", "existingPayload", "layoutContext", "attachedFootnote", "measuredPartial", "measuredContent", "footnoteContent", "el", "overflow", "i", "measuredCandidate", "pageBox", "bands", "footnoteHeight", "maxFootnoteHeight", "footnotesDiv", "p", "sectionHf", "which", "even", "bodyHeightPt", "physicalPageNumber", "resolvePageBands", "startPageNumber", "sectionDimensions", "currentContent", "pageSectionIndex", "sectionStartPages", "physicalPage", "dimensionsFor", "markSectionPlaced", "previousBox", "ownerPageInSection", "ownerNumbering", "effectiveContentHeight", "remainingHeight", "prevMarginBottomPt", "currentFootnoteIds", "currentPageHasFootnoteReference", "currentFootnoteHeight", "currentContinuation", "nextPageContinuation", "currentContinuationPartitioned", "currentPageAdmitted", "deferredFootnoteIds", "currentPartialFootnotes", "adoptEmptyPageOwner", "admitCurrentPage", "prepareCurrentContinuation", "ownedDimensions", "pageBands", "partition", "finishPage", "nextPageSectionIndex", "forceEmptyPage", "hasCurrentContinuation", "pageContinuation", "ownedPageInSection", "ownedDisplayedPageNumber", "fittingIds", "candidateIds", "guardedCandidateHeight", "hasPageContinuation", "newBands", "nextDimensions", "pageHasPayload", "allBlockFootnoteIds", "ownerDimensions", "ownerMaxFootnoteArea", "fittingHeight", "candidateHeight", "blockDimensions", "nextBlockPageInSection", "freshBlockPageBodyHeight", "startsKeepChain", "keepChain", "newChainFootnoteIds", "additionalChainFootnoteHeight", "totalChainFootnoteHeight", "currentChainHeight", "currentAvailableHeight", "nextPageBands", "freshChainBodyHeight", "freshChainFootnoteHeight", "newFootnoteIds", "combinedFootnoteHeight", "additionalFootnoteHeight", "combinedFootnoteIds", "effectiveMarginTop", "columnFragments", "blockSpace", "effectiveRemainingHeight", "bodyContentUsed", "maxFootnoteArea", "paragraphFragments", "blockSpaceWithoutFootnotes", "availableForFootnotes", "nextPageMaxFootnoteArea", "deferralCannotHelp", "noteIndex", "spaceLeftForFootnotes", "tableFragments", "contentArea", "anchors", "pixelsPerPoint", "staticRect", "paragraphRect", "toPageX", "x", "toPageY", "y", "context", "widthReference", "widthPercent", "heightReference", "heightPercent", "sizedRect", "width", "horizontal", "vertical", "hostElement", "suffix", "raw", "relativeFrom", "leftMargin", "rightMargin", "topMargin", "bottomMargin", "objectSize", "alignment", "fraction", "prospectivePageCount", "isSectionFiller", "pageAlreadyAdmitted", "heightReductionPt", "widthReductionPt", "heightReductionPx", "ptToPx", "widthReductionPx", "headerSource", "headerDiv", "clonedheaderDiv", "contentAreaTop", "contentAreaHeight", "pageCommentIds", "marginColumn", "placedThreadRoots", "rootId", "footerSource", "footerDiv", "clonedfooterDiv", "pageNum", "notesEl", "notesHeightPt", "alignExactLineBaselines", "paper", "preserveExactLineInk", "paginateHtml", "html", "containerEl", "cssPrefix", "pageContainer"]
7
+ }