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.
- package/README.md +8 -5
- package/dist/canonical.d.ts.map +1 -1
- package/dist/canonical.js +7 -3
- package/dist/canonical.js.map +1 -1
- package/dist/conversion-options.d.ts +16 -0
- package/dist/conversion-options.d.ts.map +1 -0
- package/dist/conversion-options.js +31 -0
- package/dist/conversion-options.js.map +1 -0
- package/dist/core.d.ts +17 -9
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +23 -24
- package/dist/core.js.map +1 -1
- package/dist/docxodus.worker.js +2 -1073
- package/dist/docxodus.worker.js.map +7 -1
- package/dist/editor.bundle.js +35 -14296
- package/dist/editor.bundle.js.map +7 -0
- package/dist/editor.d.ts +33 -2
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +6739 -4773
- package/dist/editor.js.map +7 -1
- package/dist/embed.bundle.js +159 -18703
- package/dist/embed.bundle.js.map +7 -0
- package/dist/embed.d.ts +1 -1
- package/dist/embed.iife.js +37 -18595
- package/dist/embed.iife.js.map +7 -0
- package/dist/embed.js +1 -1
- package/dist/export-assets.json +27 -27
- package/dist/export-browser.bundle.js +1388 -477
- package/dist/export-browser.d.ts +20 -0
- package/dist/export-browser.d.ts.map +1 -1
- package/dist/export-browser.js +132 -37
- package/dist/export-browser.js.map +1 -1
- package/dist/font-runtime.d.ts.map +1 -1
- package/dist/font-runtime.js +54 -0
- package/dist/font-runtime.js.map +1 -1
- package/dist/line-metrics.d.ts +60 -0
- package/dist/line-metrics.d.ts.map +1 -0
- package/dist/line-metrics.js +343 -0
- package/dist/line-metrics.js.map +1 -0
- package/dist/pagination.bundle.js +3 -3214
- package/dist/pagination.bundle.js.map +7 -0
- package/dist/pagination.d.ts +86 -9
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +358 -55
- package/dist/pagination.js.map +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +63 -12
- package/dist/react.js.map +1 -1
- package/dist/render-report-v2.schema.json +29 -0
- package/dist/session.bundle.js +2 -2013
- package/dist/session.bundle.js.map +7 -0
- package/dist/session.d.ts +14 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +62 -29
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +137 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +11 -11
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/dist/word-text.d.ts +14 -0
- package/dist/word-text.d.ts.map +1 -0
- package/dist/word-text.js +377 -0
- package/dist/word-text.js.map +1 -0
- package/dist/worker-proxy.bundle.js +2 -536
- package/dist/worker-proxy.bundle.js.map +7 -0
- package/dist/worker-proxy.d.ts +14 -1
- package/dist/worker-proxy.d.ts.map +1 -1
- package/dist/worker-proxy.js +357 -339
- package/dist/worker-proxy.js.map +1 -1
- package/package.json +13 -12
|
@@ -1,3214 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var DocxodusPagination = (() => {
|
|
3
|
-
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
-
|
|
21
|
-
// src/pagination.ts
|
|
22
|
-
var pagination_exports = {};
|
|
23
|
-
__export(pagination_exports, {
|
|
24
|
-
PaginationEngine: () => PaginationEngine,
|
|
25
|
-
clearPageCitationHighlight: () => clearPageCitationHighlight,
|
|
26
|
-
createUnavailablePageMap: () => createUnavailablePageMap,
|
|
27
|
-
navigateToPageCitation: () => navigateToPageCitation,
|
|
28
|
-
paginateHtml: () => paginateHtml
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// src/page-number-format.ts
|
|
32
|
-
var ROMAN_ONES = ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"];
|
|
33
|
-
var ROMAN_TENS = ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"];
|
|
34
|
-
var ROMAN_HUNDREDS = ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"];
|
|
35
|
-
var ROMAN_THOUSANDS = ["", "m", "mm", "mmm"];
|
|
36
|
-
function toRoman(value) {
|
|
37
|
-
if (value <= 0 || value >= 4e3) return String(value);
|
|
38
|
-
return ROMAN_THOUSANDS[Math.floor(value / 1e3)] + ROMAN_HUNDREDS[Math.floor(value % 1e3 / 100)] + ROMAN_TENS[Math.floor(value % 100 / 10)] + ROMAN_ONES[value % 10];
|
|
39
|
-
}
|
|
40
|
-
function toLetter(value) {
|
|
41
|
-
if (value <= 0) return String(value);
|
|
42
|
-
const wrapped = value % 780 === 0 ? 780 : value % 780;
|
|
43
|
-
const repeats = Math.floor((wrapped - 1) / 26) + 1;
|
|
44
|
-
return "abcdefghijklmnopqrstuvwxyz".charAt((wrapped - 1) % 26).repeat(repeats);
|
|
45
|
-
}
|
|
46
|
-
var RENDERERS = {
|
|
47
|
-
// ST_NumberFormat tokens (w:pgNumType/@w:fmt, w:numFmt).
|
|
48
|
-
decimal: (v) => String(v),
|
|
49
|
-
lowerRoman: toRoman,
|
|
50
|
-
upperRoman: (v) => toRoman(v).toUpperCase(),
|
|
51
|
-
lowerLetter: toLetter,
|
|
52
|
-
upperLetter: (v) => toLetter(v).toUpperCase(),
|
|
53
|
-
// Field `\*` general-formatting switch arguments.
|
|
54
|
-
Arabic: (v) => String(v),
|
|
55
|
-
roman: toRoman,
|
|
56
|
-
ROMAN: (v) => toRoman(v).toUpperCase(),
|
|
57
|
-
alphabetic: toLetter,
|
|
58
|
-
ALPHABETIC: (v) => toLetter(v).toUpperCase()
|
|
59
|
-
};
|
|
60
|
-
function formatPageNumber(value, format) {
|
|
61
|
-
const renderer = format ? RENDERERS[format] : void 0;
|
|
62
|
-
return (renderer ?? RENDERERS.decimal)(value);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/page-geometry.ts
|
|
66
|
-
var DEFAULT_PAGE_WIDTH = 612;
|
|
67
|
-
var DEFAULT_PAGE_HEIGHT = 792;
|
|
68
|
-
var DEFAULT_MARGIN = 72;
|
|
69
|
-
var DEFAULT_HEADER_FOOTER_DISTANCE = 36;
|
|
70
|
-
function pxToPt(px) {
|
|
71
|
-
return px * 0.75;
|
|
72
|
-
}
|
|
73
|
-
function ptToPx(pt) {
|
|
74
|
-
return pt / 0.75;
|
|
75
|
-
}
|
|
76
|
-
function finiteDataNumber(value, fallback) {
|
|
77
|
-
if (value === void 0 || value.trim() === "") return fallback;
|
|
78
|
-
const parsed = Number(value);
|
|
79
|
-
return Number.isFinite(parsed) ? parsed : fallback;
|
|
80
|
-
}
|
|
81
|
-
function positiveDataNumber(value, fallback) {
|
|
82
|
-
const parsed = finiteDataNumber(value, fallback);
|
|
83
|
-
return parsed > 0 ? parsed : fallback;
|
|
84
|
-
}
|
|
85
|
-
function nonNegativeDataNumber(value, fallback) {
|
|
86
|
-
const parsed = finiteDataNumber(value, fallback);
|
|
87
|
-
return parsed >= 0 ? parsed : fallback;
|
|
88
|
-
}
|
|
89
|
-
function positiveFinite(value, fallback) {
|
|
90
|
-
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
91
|
-
}
|
|
92
|
-
function parseSectionDimensions(section) {
|
|
93
|
-
const pageWidth = positiveDataNumber(section.dataset.pageWidth, DEFAULT_PAGE_WIDTH);
|
|
94
|
-
const pageHeight = positiveDataNumber(section.dataset.pageHeight, DEFAULT_PAGE_HEIGHT);
|
|
95
|
-
const marginTop = finiteDataNumber(section.dataset.marginTop, DEFAULT_MARGIN);
|
|
96
|
-
const marginRight = finiteDataNumber(section.dataset.marginRight, DEFAULT_MARGIN);
|
|
97
|
-
const marginBottom = finiteDataNumber(section.dataset.marginBottom, DEFAULT_MARGIN);
|
|
98
|
-
const marginLeft = finiteDataNumber(section.dataset.marginLeft, DEFAULT_MARGIN);
|
|
99
|
-
const derivedContentWidth = positiveFinite(pageWidth - marginLeft - marginRight, pageWidth);
|
|
100
|
-
const derivedContentHeight = positiveFinite(pageHeight - marginTop - marginBottom, pageHeight);
|
|
101
|
-
const contentWidth = positiveDataNumber(section.dataset.contentWidth, derivedContentWidth);
|
|
102
|
-
const contentHeight = positiveDataNumber(section.dataset.contentHeight, derivedContentHeight);
|
|
103
|
-
const headerDistance = nonNegativeDataNumber(
|
|
104
|
-
section.dataset.headerHeight,
|
|
105
|
-
DEFAULT_HEADER_FOOTER_DISTANCE
|
|
106
|
-
);
|
|
107
|
-
const footerDistance = nonNegativeDataNumber(
|
|
108
|
-
section.dataset.footerHeight,
|
|
109
|
-
DEFAULT_HEADER_FOOTER_DISTANCE
|
|
110
|
-
);
|
|
111
|
-
return {
|
|
112
|
-
pageWidth,
|
|
113
|
-
pageHeight,
|
|
114
|
-
contentWidth,
|
|
115
|
-
contentHeight,
|
|
116
|
-
marginTop,
|
|
117
|
-
marginRight,
|
|
118
|
-
marginBottom,
|
|
119
|
-
marginLeft,
|
|
120
|
-
headerDistance,
|
|
121
|
-
footerDistance,
|
|
122
|
-
headerHeight: headerDistance,
|
|
123
|
-
footerHeight: footerDistance
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
var MIN_BODY_BAND_PT = 12;
|
|
127
|
-
function resolvePageBands(dims, headerContentHeight, footerContentHeight) {
|
|
128
|
-
const headerTop = dims.headerDistance;
|
|
129
|
-
const footerBottom = dims.pageHeight - dims.footerDistance;
|
|
130
|
-
const headerReach = headerContentHeight > 0 ? headerTop + headerContentHeight : 0;
|
|
131
|
-
const footerReach = footerContentHeight > 0 ? footerBottom - footerContentHeight : dims.pageHeight;
|
|
132
|
-
let bodyTop = Math.max(dims.marginTop, headerReach);
|
|
133
|
-
let bodyBottom = Math.min(dims.pageHeight - dims.marginBottom, footerReach);
|
|
134
|
-
const shortfall = MIN_BODY_BAND_PT - (bodyBottom - bodyTop);
|
|
135
|
-
if (shortfall > 0) {
|
|
136
|
-
const headerGrowth = bodyTop - dims.marginTop;
|
|
137
|
-
const footerGrowth = dims.pageHeight - dims.marginBottom - bodyBottom;
|
|
138
|
-
const growth = headerGrowth + footerGrowth;
|
|
139
|
-
if (growth > 0) {
|
|
140
|
-
bodyTop -= shortfall * headerGrowth / growth;
|
|
141
|
-
bodyBottom += shortfall * footerGrowth / growth;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
headerTop,
|
|
146
|
-
headerHeight: Math.max(0, Math.min(headerContentHeight, bodyTop - headerTop)),
|
|
147
|
-
bodyTop,
|
|
148
|
-
bodyHeight: Math.max(0, bodyBottom - bodyTop),
|
|
149
|
-
footerTop: footerBottom - Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom)),
|
|
150
|
-
footerHeight: Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom))
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// src/pagination.ts
|
|
155
|
-
function samePageBox(a, b) {
|
|
156
|
-
return a.pageWidth === b.pageWidth && a.pageHeight === b.pageHeight && a.marginTop === b.marginTop && a.marginRight === b.marginRight && a.marginBottom === b.marginBottom && a.marginLeft === b.marginLeft;
|
|
157
|
-
}
|
|
158
|
-
function createUnavailablePageMap(documentVersion, rendererFingerprint, mode = "continuous") {
|
|
159
|
-
if (!rendererFingerprint) throw new Error("rendererFingerprint must be non-empty");
|
|
160
|
-
return {
|
|
161
|
-
schemaVersion: 1,
|
|
162
|
-
mode,
|
|
163
|
-
availability: "unavailable",
|
|
164
|
-
documentVersion,
|
|
165
|
-
rendererFingerprint,
|
|
166
|
-
pages: [],
|
|
167
|
-
fragments: []
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
var activePageCitationHighlights = /* @__PURE__ */ new WeakMap();
|
|
171
|
-
function clearPageCitationHighlight(root) {
|
|
172
|
-
const active = activePageCitationHighlights.get(root);
|
|
173
|
-
if (!active) return;
|
|
174
|
-
if (active.highlightClass && active.addedClass) {
|
|
175
|
-
active.target.classList.remove(active.highlightClass);
|
|
176
|
-
}
|
|
177
|
-
for (const style of active.inlineStyles ?? []) {
|
|
178
|
-
if (style.value) {
|
|
179
|
-
active.target.style.setProperty(style.name, style.value, style.priority);
|
|
180
|
-
} else {
|
|
181
|
-
active.target.style.removeProperty(style.name);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
activePageCitationHighlights.delete(root);
|
|
185
|
-
}
|
|
186
|
-
function navigateToPageCitation(root, citation, options = {}) {
|
|
187
|
-
clearPageCitationHighlight(root);
|
|
188
|
-
if (citation.availability !== "available" || citation.fragments.length === 0) {
|
|
189
|
-
return { navigated: false, unavailableReason: "citation_unavailable" };
|
|
190
|
-
}
|
|
191
|
-
const byAttribute = (name, value, within = root) => {
|
|
192
|
-
for (const node of Array.from(within.querySelectorAll(`[${name}]`))) {
|
|
193
|
-
if (node.getAttribute(name) === value) return node;
|
|
194
|
-
}
|
|
195
|
-
return null;
|
|
196
|
-
};
|
|
197
|
-
const fragment = citation.fragments[0];
|
|
198
|
-
let target = byAttribute("data-page-fragment-id", fragment.fragmentId);
|
|
199
|
-
if (!target) {
|
|
200
|
-
const page = byAttribute("data-page-number", String(fragment.pageNumber));
|
|
201
|
-
if (page) target = byAttribute("data-source-anchor-id", citation.anchorId, page) ?? page;
|
|
202
|
-
}
|
|
203
|
-
if (!target) {
|
|
204
|
-
return {
|
|
205
|
-
navigated: false,
|
|
206
|
-
pageNumber: fragment.pageNumber,
|
|
207
|
-
fragmentId: fragment.fragmentId,
|
|
208
|
-
unavailableReason: "fragment_not_found"
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
if (options.highlightClass) {
|
|
212
|
-
const addedClass = !target.classList.contains(options.highlightClass);
|
|
213
|
-
target.classList.add(options.highlightClass);
|
|
214
|
-
activePageCitationHighlights.set(root, {
|
|
215
|
-
target,
|
|
216
|
-
highlightClass: options.highlightClass,
|
|
217
|
-
addedClass
|
|
218
|
-
});
|
|
219
|
-
} else if (options.highlight !== false) {
|
|
220
|
-
const names = ["outline", "outline-offset", "background-color"];
|
|
221
|
-
const inlineStyles = names.map((name) => ({
|
|
222
|
-
name,
|
|
223
|
-
value: target.style.getPropertyValue(name),
|
|
224
|
-
priority: target.style.getPropertyPriority(name)
|
|
225
|
-
}));
|
|
226
|
-
target.style.setProperty("outline", "3px solid #f4b400", "important");
|
|
227
|
-
target.style.setProperty("outline-offset", "2px", "important");
|
|
228
|
-
target.style.setProperty("background-color", "rgba(255, 235, 59, .18)", "important");
|
|
229
|
-
activePageCitationHighlights.set(root, { target, inlineStyles });
|
|
230
|
-
}
|
|
231
|
-
target.scrollIntoView({
|
|
232
|
-
behavior: options.behavior ?? "smooth",
|
|
233
|
-
block: options.block ?? "center"
|
|
234
|
-
});
|
|
235
|
-
return {
|
|
236
|
-
navigated: true,
|
|
237
|
-
target,
|
|
238
|
-
pageNumber: fragment.pageNumber,
|
|
239
|
-
fragmentId: fragment.fragmentId
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
var MAX_FOOTNOTE_AREA_RATIO = 0.6;
|
|
243
|
-
var FOOTNOTE_MEASUREMENT_GUARD_PT = 2;
|
|
244
|
-
var FOOTNOTE_SOURCE_POSITION_ATTR = "data-pagination-footnote-source-position";
|
|
245
|
-
var PaginationEngine = class {
|
|
246
|
-
/**
|
|
247
|
-
* Creates a new pagination engine.
|
|
248
|
-
*
|
|
249
|
-
* @param staging - The staging element or its ID containing the content to paginate
|
|
250
|
-
* @param container - The container element or its ID where pages will be rendered
|
|
251
|
-
* @param options - Pagination options
|
|
252
|
-
*/
|
|
253
|
-
constructor(staging, container, options = {}) {
|
|
254
|
-
this.createdPageCount = 0;
|
|
255
|
-
this.footnoteSeparator = null;
|
|
256
|
-
this.footnoteContinuationSeparator = null;
|
|
257
|
-
this.footnoteLayoutLikeWord8 = false;
|
|
258
|
-
this.pendingFootnoteContinuation = null;
|
|
259
|
-
/** Per-section `w:pgNumType` (start / format), read off the section wrappers. */
|
|
260
|
-
this.pageNumbering = /* @__PURE__ */ new Map();
|
|
261
|
-
this.lastPages = [];
|
|
262
|
-
this.expectedPageMapAnchorIds = /* @__PURE__ */ new Set();
|
|
263
|
-
this.state = "ready";
|
|
264
|
-
const ownerDocument = typeof staging !== "string" ? staging.ownerDocument : typeof container !== "string" ? container.ownerDocument : globalThis.document;
|
|
265
|
-
this.stagingElement = typeof staging === "string" ? ownerDocument.getElementById(staging) : staging;
|
|
266
|
-
this.containerElement = typeof container === "string" ? ownerDocument.getElementById(container) : container;
|
|
267
|
-
if (!this.stagingElement) {
|
|
268
|
-
throw new Error("Staging element not found");
|
|
269
|
-
}
|
|
270
|
-
if (!this.containerElement) {
|
|
271
|
-
throw new Error("Container element not found");
|
|
272
|
-
}
|
|
273
|
-
if (this.stagingElement.ownerDocument !== this.containerElement.ownerDocument) {
|
|
274
|
-
throw new Error("Staging and container elements must belong to the same document");
|
|
275
|
-
}
|
|
276
|
-
const view = ownerDocument.defaultView;
|
|
277
|
-
if (!view) {
|
|
278
|
-
throw new Error("Pagination requires an attached document with a defaultView");
|
|
279
|
-
}
|
|
280
|
-
this.document = ownerDocument;
|
|
281
|
-
this.view = view;
|
|
282
|
-
this.scale = options.scale ?? 1;
|
|
283
|
-
this.cssPrefix = options.cssPrefix ?? "page-";
|
|
284
|
-
this.showPageNumbers = options.showPageNumbers ?? true;
|
|
285
|
-
this.pageGap = options.pageGap ?? 20;
|
|
286
|
-
this.fragmentParagraphs = options.fragmentParagraphs ?? false;
|
|
287
|
-
this.deferFragmentIdentities = options.deferFragmentIdentities ?? false;
|
|
288
|
-
this.cancellationCheckpoint = options.checkCancellation;
|
|
289
|
-
this.pageCountCheckpoint = options.checkPageCount;
|
|
290
|
-
this.layoutToken = options.layoutToken;
|
|
291
|
-
this.hfRegistry = /* @__PURE__ */ new Map();
|
|
292
|
-
this.footnoteRegistry = /* @__PURE__ */ new Map();
|
|
293
|
-
this.commentMarginRegistry = /* @__PURE__ */ new Map();
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Runs the pagination process.
|
|
297
|
-
*
|
|
298
|
-
* @returns PaginationResult with page information
|
|
299
|
-
*/
|
|
300
|
-
paginate() {
|
|
301
|
-
this.checkpoint();
|
|
302
|
-
if (this.state !== "ready") {
|
|
303
|
-
throw new Error(`PaginationEngine is one-shot and is already ${this.state}`);
|
|
304
|
-
}
|
|
305
|
-
if (!this.stagingElement.isConnected || !this.containerElement.isConnected) {
|
|
306
|
-
throw new Error("Pagination requires staging and container elements attached to their document");
|
|
307
|
-
}
|
|
308
|
-
if (this.document.documentElement.getBoundingClientRect().width <= 0) {
|
|
309
|
-
throw new Error("Pagination requires a browsing context with non-zero layout");
|
|
310
|
-
}
|
|
311
|
-
this.state = "running";
|
|
312
|
-
try {
|
|
313
|
-
const pages = [];
|
|
314
|
-
let pageNumber = 1;
|
|
315
|
-
this.hfRegistry = this.parseHeaderFooterRegistry();
|
|
316
|
-
this.footnoteRegistry = this.parseFootnoteRegistry();
|
|
317
|
-
({
|
|
318
|
-
normal: this.footnoteSeparator,
|
|
319
|
-
continuation: this.footnoteContinuationSeparator
|
|
320
|
-
} = this.parseFootnoteSeparators());
|
|
321
|
-
this.commentMarginRegistry = this.parseCommentMarginRegistry();
|
|
322
|
-
this.footnoteLayoutLikeWord8 = this.stagingElement.dataset.footnoteLayoutLikeWord8 === "true";
|
|
323
|
-
const sections = this.stagingElement.querySelectorAll(
|
|
324
|
-
"[data-section-index]"
|
|
325
|
-
);
|
|
326
|
-
this.pageNumbering = this.parsePageNumbering(sections);
|
|
327
|
-
const sectionsToProcess = sections.length > 0 ? Array.from(sections) : [this.stagingElement];
|
|
328
|
-
this.expectedPageMapAnchorIds = /* @__PURE__ */ new Set();
|
|
329
|
-
const referencedFootnoteIds = /* @__PURE__ */ new Set();
|
|
330
|
-
const referencedCommentIds = /* @__PURE__ */ new Set();
|
|
331
|
-
for (const section of sectionsToProcess) {
|
|
332
|
-
this.checkpoint();
|
|
333
|
-
this.collectExpectedSourceAnchors(section, this.expectedPageMapAnchorIds, true);
|
|
334
|
-
for (const reference of Array.from(section.querySelectorAll("[data-footnote-id]"))) {
|
|
335
|
-
this.checkpoint();
|
|
336
|
-
if (reference.closest("#pagination-footnote-registry, #pagination-hf-registry")) continue;
|
|
337
|
-
const id = reference.dataset.footnoteId;
|
|
338
|
-
if (id) referencedFootnoteIds.add(id);
|
|
339
|
-
}
|
|
340
|
-
for (const reference of Array.from(section.querySelectorAll("[data-comment-id]"))) {
|
|
341
|
-
this.checkpoint();
|
|
342
|
-
if (reference.closest(
|
|
343
|
-
"#pagination-comment-margin-registry, #pagination-footnote-registry, #pagination-hf-registry"
|
|
344
|
-
)) continue;
|
|
345
|
-
const id = reference.dataset.commentId;
|
|
346
|
-
if (id) referencedCommentIds.add(id);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
for (const id of referencedFootnoteIds) {
|
|
350
|
-
this.checkpoint();
|
|
351
|
-
const source = this.footnoteRegistry.get(id);
|
|
352
|
-
if (!source) continue;
|
|
353
|
-
this.collectExpectedSourceAnchors(source, this.expectedPageMapAnchorIds);
|
|
354
|
-
for (const reference of Array.from(
|
|
355
|
-
source.querySelectorAll("[data-comment-id]")
|
|
356
|
-
)) {
|
|
357
|
-
this.checkpoint();
|
|
358
|
-
const commentId = reference.dataset.commentId;
|
|
359
|
-
if (commentId) referencedCommentIds.add(commentId);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
for (const id of referencedCommentIds) {
|
|
363
|
-
this.checkpoint();
|
|
364
|
-
const source = this.commentMarginRegistry.get(id);
|
|
365
|
-
if (source) this.collectExpectedSourceAnchors(source, this.expectedPageMapAnchorIds);
|
|
366
|
-
}
|
|
367
|
-
const runs = [];
|
|
368
|
-
for (const section of sectionsToProcess) {
|
|
369
|
-
this.checkpoint();
|
|
370
|
-
const dims = parseSectionDimensions(section);
|
|
371
|
-
const previous = runs[runs.length - 1];
|
|
372
|
-
if (previous && section.dataset.sectionType === "continuous" && samePageBox(previous.dims, dims)) {
|
|
373
|
-
previous.sections.push(section);
|
|
374
|
-
previous.sectionDimensions.set(
|
|
375
|
-
parseInt(section.dataset.sectionIndex || "0", 10),
|
|
376
|
-
dims
|
|
377
|
-
);
|
|
378
|
-
} else {
|
|
379
|
-
const sectionIndex = parseInt(section.dataset.sectionIndex || "0", 10);
|
|
380
|
-
runs.push({
|
|
381
|
-
sections: [section],
|
|
382
|
-
sectionIndex,
|
|
383
|
-
sectionType: section.dataset.sectionType ?? "nextPage",
|
|
384
|
-
dims,
|
|
385
|
-
sectionDimensions: /* @__PURE__ */ new Map([[sectionIndex, dims]])
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
for (const run of runs) {
|
|
390
|
-
this.checkpoint();
|
|
391
|
-
const needsParityFiller = pages.length > 0 && (run.sectionType === "oddPage" && pageNumber % 2 === 0 || run.sectionType === "evenPage" && pageNumber % 2 === 1);
|
|
392
|
-
if (needsParityFiller) {
|
|
393
|
-
const precedingPage = pages[pages.length - 1];
|
|
394
|
-
const precedingPageInSection = parseInt(
|
|
395
|
-
precedingPage.element.dataset.pageInSection ?? "1",
|
|
396
|
-
10
|
|
397
|
-
);
|
|
398
|
-
const precedingDisplayedPageNumber = parseInt(
|
|
399
|
-
precedingPage.element.dataset.displayedPageNumber ?? String(precedingPage.pageNumber),
|
|
400
|
-
10
|
|
401
|
-
);
|
|
402
|
-
const filler = this.createPage(
|
|
403
|
-
precedingPage.dimensions,
|
|
404
|
-
pageNumber,
|
|
405
|
-
precedingPage.sectionIndex,
|
|
406
|
-
precedingDisplayedPageNumber + 1,
|
|
407
|
-
[],
|
|
408
|
-
precedingPageInSection + 1,
|
|
409
|
-
[],
|
|
410
|
-
0,
|
|
411
|
-
null,
|
|
412
|
-
void 0,
|
|
413
|
-
true
|
|
414
|
-
);
|
|
415
|
-
pages.push(filler);
|
|
416
|
-
pageNumber++;
|
|
417
|
-
}
|
|
418
|
-
this.stagingElement.style.visibility = "hidden";
|
|
419
|
-
this.stagingElement.style.position = "absolute";
|
|
420
|
-
this.stagingElement.style.left = "-9999px";
|
|
421
|
-
this.stagingElement.style.display = "block";
|
|
422
|
-
const blocks = [];
|
|
423
|
-
for (const section of run.sections) {
|
|
424
|
-
this.checkpoint();
|
|
425
|
-
const sectionIndex = parseInt(section.dataset.sectionIndex || "0", 10);
|
|
426
|
-
const sectionDims = run.sectionDimensions.get(sectionIndex) ?? run.dims;
|
|
427
|
-
section.style.width = `${sectionDims.contentWidth}pt`;
|
|
428
|
-
const columnCount = parseInt(section.dataset.cols || "1", 10);
|
|
429
|
-
if (columnCount > 1) {
|
|
430
|
-
const gap = parseFloat(section.dataset.colGap || "");
|
|
431
|
-
blocks.push(...this.buildColumnBlocks(
|
|
432
|
-
section,
|
|
433
|
-
sectionDims,
|
|
434
|
-
sectionIndex,
|
|
435
|
-
columnCount,
|
|
436
|
-
Number.isFinite(gap) ? gap : 36
|
|
437
|
-
));
|
|
438
|
-
} else {
|
|
439
|
-
blocks.push(...this.measureBlocks(section, sectionDims, sectionIndex));
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
const sectionPages = this.flowToPages(
|
|
443
|
-
blocks,
|
|
444
|
-
run.dims,
|
|
445
|
-
pageNumber,
|
|
446
|
-
run.sectionIndex,
|
|
447
|
-
run.sectionDimensions
|
|
448
|
-
);
|
|
449
|
-
this.checkpoint();
|
|
450
|
-
pages.push(...sectionPages);
|
|
451
|
-
pageNumber += sectionPages.length;
|
|
452
|
-
}
|
|
453
|
-
this.stagingElement.style.display = "none";
|
|
454
|
-
this.substitutePageNumberFields(pages.length);
|
|
455
|
-
for (const page of pages) {
|
|
456
|
-
this.checkpoint();
|
|
457
|
-
if (page.element.dataset.sectionFiller === "true") continue;
|
|
458
|
-
const pageInSection = parseInt(page.element.dataset.pageInSection || "1", 10);
|
|
459
|
-
const displayedPageNumber = parseInt(
|
|
460
|
-
page.element.dataset.displayedPageNumber || String(page.pageNumber),
|
|
461
|
-
10
|
|
462
|
-
);
|
|
463
|
-
const header = this.selectHeader(page.sectionIndex, pageInSection, displayedPageNumber);
|
|
464
|
-
const footer = this.selectFooter(page.sectionIndex, pageInSection, displayedPageNumber);
|
|
465
|
-
if (header) this.collectExpectedSourceAnchors(header, this.expectedPageMapAnchorIds);
|
|
466
|
-
if (footer) this.collectExpectedSourceAnchors(footer, this.expectedPageMapAnchorIds);
|
|
467
|
-
}
|
|
468
|
-
this.qualifyPageFragments(pages);
|
|
469
|
-
this.transferVisibleFragmentTargets();
|
|
470
|
-
if (!this.deferFragmentIdentities) this.normalizeVisiblePageFragments(pages);
|
|
471
|
-
this.lastPages = pages;
|
|
472
|
-
const result = {
|
|
473
|
-
totalPages: pages.length,
|
|
474
|
-
pages,
|
|
475
|
-
pageMap: this.layoutToken ? this.materializePageMap(
|
|
476
|
-
this.layoutToken.documentVersion,
|
|
477
|
-
this.layoutToken.rendererFingerprint
|
|
478
|
-
) : void 0
|
|
479
|
-
};
|
|
480
|
-
this.state = "complete";
|
|
481
|
-
return result;
|
|
482
|
-
} catch (error) {
|
|
483
|
-
this.state = "failed";
|
|
484
|
-
throw error;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
checkpoint() {
|
|
488
|
-
this.cancellationCheckpoint?.();
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Normalize visible fragment identities after a caller applies final standalone styles. This
|
|
492
|
-
* deliberately runs before the stability barrier; materializePageMap is read-only so PageMap
|
|
493
|
-
* measurement cannot mutate a tree after it was declared stable.
|
|
494
|
-
*/
|
|
495
|
-
normalizePageMapFragmentIdentities() {
|
|
496
|
-
if (this.lastPages.length === 0) {
|
|
497
|
-
throw new Error("paginate() must complete before fragment identities can be normalized");
|
|
498
|
-
}
|
|
499
|
-
this.normalizeVisiblePageFragments(this.lastPages);
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Materialize the last completed browser layout as portable page-relative point geometry.
|
|
503
|
-
* The caller supplies both invalidation tokens; this engine never guesses a document version
|
|
504
|
-
* or renderer fingerprint.
|
|
505
|
-
*/
|
|
506
|
-
materializePageMap(documentVersion, rendererFingerprint) {
|
|
507
|
-
if (!Number.isSafeInteger(documentVersion) || documentVersion < 0) {
|
|
508
|
-
throw new Error("documentVersion must be a non-negative safe integer");
|
|
509
|
-
}
|
|
510
|
-
if (!rendererFingerprint) throw new Error("rendererFingerprint must be non-empty");
|
|
511
|
-
if (this.lastPages.length === 0) throw new Error("paginate() must complete before materializePageMap()");
|
|
512
|
-
const pages = this.lastPages.map((page) => ({
|
|
513
|
-
pageNumber: page.pageNumber,
|
|
514
|
-
pageInSection: parseInt(page.element.dataset.pageInSection || "1", 10),
|
|
515
|
-
width: page.dimensions.pageWidth,
|
|
516
|
-
height: page.dimensions.pageHeight,
|
|
517
|
-
sectionIndex: page.sectionIndex,
|
|
518
|
-
pageName: `docxodus-section-${page.sectionIndex}`
|
|
519
|
-
}));
|
|
520
|
-
const fragments = [];
|
|
521
|
-
const requiredAnchorIds = new Set(this.expectedPageMapAnchorIds);
|
|
522
|
-
if (requiredAnchorIds.size === 0) {
|
|
523
|
-
throw new Error("cannot publish an available PageMap without canonical source inventory");
|
|
524
|
-
}
|
|
525
|
-
const measuredAnchorIds = /* @__PURE__ */ new Set();
|
|
526
|
-
const emittedFragmentCounts = /* @__PURE__ */ new Map();
|
|
527
|
-
for (const page of this.lastPages) {
|
|
528
|
-
this.checkpoint();
|
|
529
|
-
const pageRect = page.element.getBoundingClientRect();
|
|
530
|
-
if (pageRect.width <= 0 || pageRect.height <= 0) {
|
|
531
|
-
throw new Error(`page ${page.pageNumber} has no measurable geometry`);
|
|
532
|
-
}
|
|
533
|
-
const pointPerRenderedX = page.dimensions.pageWidth / pageRect.width;
|
|
534
|
-
const pointPerRenderedY = page.dimensions.pageHeight / pageRect.height;
|
|
535
|
-
const nodes = page.element.querySelectorAll("[data-source-anchor-id]");
|
|
536
|
-
for (const element of Array.from(nodes)) {
|
|
537
|
-
this.checkpoint();
|
|
538
|
-
if (this.isDeliberatelyUnrenderedSource(element, page.element)) continue;
|
|
539
|
-
const anchorId = element.dataset.sourceAnchorId;
|
|
540
|
-
if (!anchorId || !element.dataset.pageFragmentId || !Number.isInteger(parseInt(element.dataset.fragmentIndex || "", 10))) {
|
|
541
|
-
throw new Error(`page ${page.pageNumber} contains an unqualified source anchor`);
|
|
542
|
-
}
|
|
543
|
-
const rect = element.getBoundingClientRect();
|
|
544
|
-
const style = this.view.getComputedStyle(element);
|
|
545
|
-
const deliberatelyHidden = style.display === "none" || style.visibility === "hidden";
|
|
546
|
-
if (deliberatelyHidden) continue;
|
|
547
|
-
requiredAnchorIds.add(anchorId);
|
|
548
|
-
const visibleRect = this.intersectWithClippingAncestors(element, page.element, pageRect, rect);
|
|
549
|
-
const left = visibleRect.left;
|
|
550
|
-
const top = visibleRect.top;
|
|
551
|
-
const right = visibleRect.right;
|
|
552
|
-
const bottom = visibleRect.bottom;
|
|
553
|
-
if (rect.width <= 0 || rect.height <= 0 || right <= left || bottom <= top) {
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
measuredAnchorIds.add(anchorId);
|
|
557
|
-
const fragmentIndex = emittedFragmentCounts.get(anchorId) ?? 0;
|
|
558
|
-
emittedFragmentCounts.set(anchorId, fragmentIndex + 1);
|
|
559
|
-
const fragmentId = `p${page.pageNumber}-f${fragmentIndex}-${anchorId}`;
|
|
560
|
-
if (element.dataset.fragmentIndex !== String(fragmentIndex) || element.dataset.pageFragmentId !== fragmentId || element.dataset.pageNumber !== String(page.pageNumber)) {
|
|
561
|
-
throw new Error(
|
|
562
|
-
`page ${page.pageNumber} fragment identity changed after final-tree normalization`
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
fragments.push({
|
|
566
|
-
fragmentId,
|
|
567
|
-
anchorId,
|
|
568
|
-
fragmentIndex,
|
|
569
|
-
pageNumber: page.pageNumber,
|
|
570
|
-
geometry: {
|
|
571
|
-
x: (left - pageRect.left) * pointPerRenderedX,
|
|
572
|
-
y: (top - pageRect.top) * pointPerRenderedY,
|
|
573
|
-
width: (right - left) * pointPerRenderedX,
|
|
574
|
-
height: (bottom - top) * pointPerRenderedY
|
|
575
|
-
},
|
|
576
|
-
story: this.storyForCanonicalAnchor(anchorId),
|
|
577
|
-
inTableCell: element.matches("td,th") || element.closest("td,th") !== null
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
const missingAnchor = Array.from(requiredAnchorIds).find((id) => !measuredAnchorIds.has(id));
|
|
582
|
-
if (missingAnchor) {
|
|
583
|
-
throw new Error(`source anchor ${missingAnchor} has no measurable fragment in the paginated layout`);
|
|
584
|
-
}
|
|
585
|
-
return {
|
|
586
|
-
schemaVersion: 1,
|
|
587
|
-
mode: "paginated",
|
|
588
|
-
availability: "available",
|
|
589
|
-
documentVersion,
|
|
590
|
-
rendererFingerprint,
|
|
591
|
-
pages,
|
|
592
|
-
fragments
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
normalizeVisiblePageFragments(pages) {
|
|
596
|
-
const emittedFragmentCounts = /* @__PURE__ */ new Map();
|
|
597
|
-
for (const page of pages) {
|
|
598
|
-
this.checkpoint();
|
|
599
|
-
const pageRect = page.element.getBoundingClientRect();
|
|
600
|
-
for (const element of Array.from(
|
|
601
|
-
page.element.querySelectorAll("[data-source-anchor-id]")
|
|
602
|
-
)) {
|
|
603
|
-
this.checkpoint();
|
|
604
|
-
if (this.isDeliberatelyUnrenderedSource(element, page.element)) continue;
|
|
605
|
-
const anchorId = element.dataset.sourceAnchorId;
|
|
606
|
-
if (!anchorId) continue;
|
|
607
|
-
const style = this.view.getComputedStyle(element);
|
|
608
|
-
if (style.display === "none" || style.visibility === "hidden") continue;
|
|
609
|
-
const rect = element.getBoundingClientRect();
|
|
610
|
-
const visible = this.intersectWithClippingAncestors(element, page.element, pageRect, rect);
|
|
611
|
-
if (rect.width <= 0 || rect.height <= 0 || visible.right <= visible.left || visible.bottom <= visible.top) continue;
|
|
612
|
-
const fragmentIndex = emittedFragmentCounts.get(anchorId) ?? 0;
|
|
613
|
-
emittedFragmentCounts.set(anchorId, fragmentIndex + 1);
|
|
614
|
-
element.dataset.pageNumber = String(page.pageNumber);
|
|
615
|
-
element.dataset.fragmentIndex = String(fragmentIndex);
|
|
616
|
-
element.dataset.pageFragmentId = `p${page.pageNumber}-f${fragmentIndex}-${anchorId}`;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Intersect an element with every ancestor that establishes an overflow clip before the page
|
|
622
|
-
* root. getBoundingClientRect() reports layout outside those clips, which is not rendered and
|
|
623
|
-
* therefore must not satisfy PageMap completeness or inflate portable geometry.
|
|
624
|
-
*/
|
|
625
|
-
intersectWithClippingAncestors(element, page, pageRect, rect) {
|
|
626
|
-
let left = Math.max(rect.left, pageRect.left);
|
|
627
|
-
let top = Math.max(rect.top, pageRect.top);
|
|
628
|
-
let right = Math.min(rect.right, pageRect.right);
|
|
629
|
-
let bottom = Math.min(rect.bottom, pageRect.bottom);
|
|
630
|
-
const clips = (value) => value === "hidden" || value === "clip" || value === "scroll" || value === "auto";
|
|
631
|
-
for (let ancestor = element.parentElement; ancestor && ancestor !== page; ancestor = ancestor.parentElement) {
|
|
632
|
-
const style = this.view.getComputedStyle(ancestor);
|
|
633
|
-
const clipsX = clips(style.overflowX);
|
|
634
|
-
const clipsY = clips(style.overflowY);
|
|
635
|
-
if (!clipsX && !clipsY) continue;
|
|
636
|
-
const ancestorRect = ancestor.getBoundingClientRect();
|
|
637
|
-
if (clipsX) {
|
|
638
|
-
left = Math.max(left, ancestorRect.left);
|
|
639
|
-
right = Math.min(right, ancestorRect.right);
|
|
640
|
-
}
|
|
641
|
-
if (clipsY) {
|
|
642
|
-
top = Math.max(top, ancestorRect.top);
|
|
643
|
-
bottom = Math.min(bottom, ancestorRect.bottom);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
return { left, top, right, bottom };
|
|
647
|
-
}
|
|
648
|
-
storyForCanonicalAnchor(anchorId) {
|
|
649
|
-
const first = anchorId.indexOf(":");
|
|
650
|
-
const second = first < 0 ? -1 : anchorId.indexOf(":", first + 1);
|
|
651
|
-
const scope = first >= 0 && second > first ? anchorId.slice(first + 1, second) : "body";
|
|
652
|
-
if (scope.startsWith("hdr")) return "header";
|
|
653
|
-
if (scope.startsWith("ftr")) return "footer";
|
|
654
|
-
if (scope === "fn") return "footnote";
|
|
655
|
-
if (scope === "en") return "endnote";
|
|
656
|
-
if (scope === "cmt") return "comment";
|
|
657
|
-
return "body";
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Add canonical IDs from an addressable source subtree to the pre-pagination inventory.
|
|
661
|
-
* Registry wrappers are excluded when scanning staging because selectable registry contents are
|
|
662
|
-
* inventoried separately. Producers may explicitly mark content that has no visual substrate
|
|
663
|
-
* with `data-page-map-exclude="true"`; native hidden semantics carry the same signal.
|
|
664
|
-
*/
|
|
665
|
-
collectExpectedSourceAnchors(source, destination, excludeRegistries = false) {
|
|
666
|
-
const candidates = source.matches("[data-source-anchor-id]") ? [source] : [];
|
|
667
|
-
candidates.push(...Array.from(source.querySelectorAll("[data-source-anchor-id]")));
|
|
668
|
-
for (const element of candidates) {
|
|
669
|
-
if (excludeRegistries && element.closest(
|
|
670
|
-
"#pagination-hf-registry, #pagination-footnote-registry, #pagination-comment-margin-registry"
|
|
671
|
-
)) {
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
if (this.isZeroHeightExplicitBreakCarrier(element)) {
|
|
675
|
-
element.dataset.pageMapExclude = "true";
|
|
676
|
-
}
|
|
677
|
-
if (this.isDeliberatelyUnrenderedSource(element, source)) continue;
|
|
678
|
-
const anchorId = element.dataset.sourceAnchorId;
|
|
679
|
-
if (anchorId) destination.add(anchorId);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
isZeroHeightExplicitBreakCarrier(element) {
|
|
683
|
-
const next = element.nextElementSibling;
|
|
684
|
-
const bounds = element.getBoundingClientRect();
|
|
685
|
-
return element.hasAttribute("data-source-anchor-id") && (element.textContent ?? "").replace(/\u00a0/g, "").trim() === "" && bounds.height <= 0.01 && !element.querySelector("[data-source-anchor-id]") && !element.querySelector("img,svg,canvas,table,hr,input,textarea,select") && (next?.dataset.pageBreak === "true" || next?.classList.contains(`${this.cssPrefix}break`) === true);
|
|
686
|
-
}
|
|
687
|
-
isDeliberatelyUnrenderedSource(element, sourceRoot) {
|
|
688
|
-
for (let current = element; current; current = current.parentElement) {
|
|
689
|
-
const zeroHeightExplicitBreakCarrier = current === element && this.isZeroHeightExplicitBreakCarrier(current);
|
|
690
|
-
if (current.dataset.pageMapExclude === "true" || current.dataset.pageBreak === "true" || current.classList.contains(`${this.cssPrefix}break`) || zeroHeightExplicitBreakCarrier || current.hidden || current.getAttribute("aria-hidden") === "true" || current.style.display === "none" || current.style.visibility === "hidden") {
|
|
691
|
-
return true;
|
|
692
|
-
}
|
|
693
|
-
if (current === sourceRoot) break;
|
|
694
|
-
}
|
|
695
|
-
return false;
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Keep exactly one active bare-Unid editor anchor per source block. Presentation clones use
|
|
699
|
-
* canonical source identity plus page/fragment qualification instead.
|
|
700
|
-
*/
|
|
701
|
-
qualifyPageFragments(pages) {
|
|
702
|
-
const fragmentCounts = /* @__PURE__ */ new Map();
|
|
703
|
-
const activeCanonicalIds = /* @__PURE__ */ new Set();
|
|
704
|
-
const activeBareAnchorIds = /* @__PURE__ */ new Set();
|
|
705
|
-
const makeInactive = (element) => {
|
|
706
|
-
element.removeAttribute("data-anchor");
|
|
707
|
-
element.removeAttribute("data-committed-text");
|
|
708
|
-
if (element.hasAttribute("contenteditable")) element.setAttribute("contenteditable", "false");
|
|
709
|
-
};
|
|
710
|
-
for (const page of pages) {
|
|
711
|
-
const nodes = page.element.querySelectorAll("[data-source-anchor-id]");
|
|
712
|
-
for (const element of Array.from(nodes)) {
|
|
713
|
-
const anchorId = element.dataset.sourceAnchorId;
|
|
714
|
-
if (!anchorId) continue;
|
|
715
|
-
const fragmentIndex = fragmentCounts.get(anchorId) ?? 0;
|
|
716
|
-
fragmentCounts.set(anchorId, fragmentIndex + 1);
|
|
717
|
-
element.dataset.pageNumber = String(page.pageNumber);
|
|
718
|
-
element.dataset.fragmentIndex = String(fragmentIndex);
|
|
719
|
-
element.dataset.pageFragmentId = `p${page.pageNumber}-f${fragmentIndex}-${anchorId}`;
|
|
720
|
-
const story = this.storyForCanonicalAnchor(anchorId);
|
|
721
|
-
const mayOwnActiveEditorAnchor = story === "body" || story === "comment" || story === "footnote" || story === "endnote";
|
|
722
|
-
if (element.hasAttribute("data-anchor")) {
|
|
723
|
-
const bareAnchorId = element.dataset.anchor;
|
|
724
|
-
if (mayOwnActiveEditorAnchor && !activeCanonicalIds.has(anchorId) && !activeBareAnchorIds.has(bareAnchorId)) {
|
|
725
|
-
activeCanonicalIds.add(anchorId);
|
|
726
|
-
activeBareAnchorIds.add(bareAnchorId);
|
|
727
|
-
} else {
|
|
728
|
-
makeInactive(element);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
const activeStagingCanonicalIds = /* @__PURE__ */ new Set();
|
|
734
|
-
for (const element of Array.from(
|
|
735
|
-
this.stagingElement.querySelectorAll("[data-source-anchor-id][data-anchor]")
|
|
736
|
-
)) {
|
|
737
|
-
const anchorId = element.dataset.sourceAnchorId;
|
|
738
|
-
if (!anchorId) continue;
|
|
739
|
-
const bareAnchorId = element.dataset.anchor;
|
|
740
|
-
if (activeCanonicalIds.has(anchorId) || activeStagingCanonicalIds.has(anchorId) || activeBareAnchorIds.has(bareAnchorId)) {
|
|
741
|
-
makeInactive(element);
|
|
742
|
-
} else {
|
|
743
|
-
activeStagingCanonicalIds.add(anchorId);
|
|
744
|
-
activeBareAnchorIds.add(bareAnchorId);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Page flow clones source blocks while the hidden staging tree stays in the document. Any HTML
|
|
750
|
-
* fragment target copied into a visible page would therefore resolve to its earlier hidden
|
|
751
|
-
* source. Transfer target ownership to the page presentation after flow is complete; registry
|
|
752
|
-
* and wrapper IDs that have no visible counterpart remain available to pagination internals.
|
|
753
|
-
*/
|
|
754
|
-
transferVisibleFragmentTargets() {
|
|
755
|
-
const visibleIds = new Set(Array.from(
|
|
756
|
-
this.containerElement.querySelectorAll("[id]")
|
|
757
|
-
).map((element) => element.id).filter(Boolean));
|
|
758
|
-
if (visibleIds.size === 0) return;
|
|
759
|
-
for (const source of Array.from(
|
|
760
|
-
this.stagingElement.querySelectorAll("[id]")
|
|
761
|
-
)) {
|
|
762
|
-
if (visibleIds.has(source.id)) source.removeAttribute("id");
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
/** Read each section's `w:pgNumType` off its wrapper (see {@link SectionPageNumbering}). */
|
|
766
|
-
parsePageNumbering(sections) {
|
|
767
|
-
const map = /* @__PURE__ */ new Map();
|
|
768
|
-
for (const section of Array.from(sections)) {
|
|
769
|
-
const index = parseInt(section.dataset.sectionIndex || "0", 10);
|
|
770
|
-
const rawStart = section.dataset.pageNumStart;
|
|
771
|
-
const start = rawStart === void 0 ? void 0 : parseInt(rawStart, 10);
|
|
772
|
-
map.set(index, {
|
|
773
|
-
start: start !== void 0 && Number.isFinite(start) ? start : void 0,
|
|
774
|
-
format: section.dataset.pageNumFmt
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
return map;
|
|
778
|
-
}
|
|
779
|
-
/**
|
|
780
|
-
* Fill in the page-number fields inside every page's cloned header/footer.
|
|
781
|
-
*
|
|
782
|
-
* A header/footer is authored once and cloned onto each page, so a PAGE field's single cached
|
|
783
|
-
* result would otherwise show the same number on every page — the whole reason the converter
|
|
784
|
-
* marks these. `data-field-format` (the field's own `\*` switch) wins over the section's format
|
|
785
|
-
* when present, which is exactly how Word resolves the two.
|
|
786
|
-
*
|
|
787
|
-
* Runs after layout because NUMPAGES cannot be known before the last page exists. The
|
|
788
|
-
* substituted text can therefore be marginally wider than the cached result the header was
|
|
789
|
-
* measured with; the header band clips, so the failure mode is a hair of overflow rather than
|
|
790
|
-
* a layout that disagrees with itself.
|
|
791
|
-
*
|
|
792
|
-
* Scoped to the CLONED header/footer regions on purpose. A page-number field in body text is
|
|
793
|
-
* ordinary run content that the editor may make editable, and committing an edited block writes
|
|
794
|
-
* back whatever text the DOM holds — rewriting it here would mean a body field commits a number
|
|
795
|
-
* the document never contained. Body content is also not cloned, so it does not have the problem
|
|
796
|
-
* this method exists to solve.
|
|
797
|
-
*/
|
|
798
|
-
substitutePageNumberFields(totalPages) {
|
|
799
|
-
const boxes = this.containerElement.querySelectorAll(`.${this.cssPrefix}box`);
|
|
800
|
-
for (const box of Array.from(boxes)) {
|
|
801
|
-
const markers = box.querySelectorAll(
|
|
802
|
-
`.${this.cssPrefix}header [data-field], .${this.cssPrefix}footer [data-field]`
|
|
803
|
-
);
|
|
804
|
-
if (markers.length === 0) continue;
|
|
805
|
-
const sectionIndex = parseInt(box.dataset.sectionIndex || "0", 10);
|
|
806
|
-
const pageNumber = parseInt(box.dataset.pageNumber || "1", 10);
|
|
807
|
-
const numbering = this.pageNumbering.get(sectionIndex) ?? {};
|
|
808
|
-
const displayed = parseInt(box.dataset.displayedPageNumber || String(pageNumber), 10);
|
|
809
|
-
for (const marker of Array.from(markers)) {
|
|
810
|
-
const kind = marker.dataset.field;
|
|
811
|
-
if (kind !== "PAGE" && kind !== "NUMPAGES") continue;
|
|
812
|
-
const format = marker.dataset.fieldFormat ?? numbering.format;
|
|
813
|
-
marker.textContent = formatPageNumber(kind === "PAGE" ? displayed : totalPages, format);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Measures all content blocks in a section.
|
|
819
|
-
*/
|
|
820
|
-
measureBlocks(section, dims, sectionIndex) {
|
|
821
|
-
const blocks = [];
|
|
822
|
-
const children = Array.from(section.children);
|
|
823
|
-
for (const child of children) {
|
|
824
|
-
this.checkpoint();
|
|
825
|
-
if (child.dataset.sectionIndex !== void 0) {
|
|
826
|
-
const nestedSectionIndex = parseInt(child.dataset.sectionIndex || String(sectionIndex), 10);
|
|
827
|
-
const nestedBlocks = this.measureBlocks(child, dims, nestedSectionIndex);
|
|
828
|
-
blocks.push(...nestedBlocks);
|
|
829
|
-
continue;
|
|
830
|
-
}
|
|
831
|
-
if (child.matches("section.endnotes")) {
|
|
832
|
-
const endnoteBlocks = this.measureSafeEndnoteBlocks(child, dims, sectionIndex);
|
|
833
|
-
if (endnoteBlocks) {
|
|
834
|
-
blocks.push(...endnoteBlocks);
|
|
835
|
-
continue;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
const rect = child.getBoundingClientRect();
|
|
839
|
-
const style = this.view.getComputedStyle(child);
|
|
840
|
-
const marginTopPx = parseFloat(style.marginTop) || 0;
|
|
841
|
-
const marginBottomPx = parseFloat(style.marginBottom) || 0;
|
|
842
|
-
const heightPt = pxToPt(rect.height);
|
|
843
|
-
const marginTopPt = pxToPt(marginTopPx);
|
|
844
|
-
const marginBottomPt = pxToPt(marginBottomPx);
|
|
845
|
-
const isPageBreak = child.dataset.pageBreak === "true" || child.classList.contains(`${this.cssPrefix}break`);
|
|
846
|
-
blocks.push({
|
|
847
|
-
element: child,
|
|
848
|
-
sectionIndex,
|
|
849
|
-
heightPt,
|
|
850
|
-
marginTopPt,
|
|
851
|
-
marginBottomPt,
|
|
852
|
-
keepWithNext: child.dataset.keepWithNext === "true",
|
|
853
|
-
keepLines: child.dataset.keepLines === "true",
|
|
854
|
-
pageBreakBefore: child.dataset.pageBreakBefore === "true",
|
|
855
|
-
isPageBreak,
|
|
856
|
-
isWordParagraph: this.isWordParagraphElement(child)
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
return blocks;
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* Flatten the converter's `section.endnotes > ol > li > p` presentation into
|
|
863
|
-
* ordinary paragraph blocks. This preserves paragraph formatting and canonical
|
|
864
|
-
* p:en/en:en identities while allowing the existing paragraph fragmenter to
|
|
865
|
-
* split a long endnote across page boundaries.
|
|
866
|
-
*/
|
|
867
|
-
measureSafeEndnoteBlocks(section, dims, sectionIndex) {
|
|
868
|
-
const sectionChildren = Array.from(section.children);
|
|
869
|
-
const list = sectionChildren.find((child) => child.tagName === "OL");
|
|
870
|
-
if (!list || sectionChildren.some((child) => child.tagName !== "HR" && child !== list) || Array.from(list.children).some((child) => child.tagName !== "LI")) {
|
|
871
|
-
return null;
|
|
872
|
-
}
|
|
873
|
-
const items = Array.from(list.children);
|
|
874
|
-
if (items.length === 0 || items.some(
|
|
875
|
-
(item) => item.children.length === 0 || Array.from(item.children).some((child) => child.tagName !== "P")
|
|
876
|
-
)) {
|
|
877
|
-
return null;
|
|
878
|
-
}
|
|
879
|
-
const blocks = [];
|
|
880
|
-
const sectionStyle = this.view.getComputedStyle(section);
|
|
881
|
-
for (const rule of sectionChildren.filter((child) => child.tagName === "HR")) {
|
|
882
|
-
this.checkpoint();
|
|
883
|
-
const clonedRule = rule.cloneNode(true);
|
|
884
|
-
if (blocks.length === 0) clonedRule.style.marginTop = sectionStyle.marginTop;
|
|
885
|
-
blocks.push(this.measureElement(clonedRule, dims, sectionIndex));
|
|
886
|
-
}
|
|
887
|
-
const listStyle = this.view.getComputedStyle(list);
|
|
888
|
-
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
889
|
-
this.checkpoint();
|
|
890
|
-
const item = items[itemIndex];
|
|
891
|
-
const ownerAnchorId = item.dataset.sourceAnchorId;
|
|
892
|
-
const paragraphs = Array.from(item.children);
|
|
893
|
-
for (let paragraphIndex = 0; paragraphIndex < paragraphs.length; paragraphIndex++) {
|
|
894
|
-
this.checkpoint();
|
|
895
|
-
const paragraph = paragraphs[paragraphIndex];
|
|
896
|
-
if (!this.hasRangeFragmentSafeLayout(paragraph)) {
|
|
897
|
-
return null;
|
|
898
|
-
}
|
|
899
|
-
const clone = paragraph.cloneNode(true);
|
|
900
|
-
clone.dataset.paginationSafeEndnote = "true";
|
|
901
|
-
clone.style.fontSize ||= sectionStyle.fontSize;
|
|
902
|
-
clone.style.lineHeight ||= sectionStyle.lineHeight;
|
|
903
|
-
clone.style.paddingLeft ||= listStyle.paddingLeft;
|
|
904
|
-
if (ownerAnchorId && !Array.from(
|
|
905
|
-
clone.querySelectorAll("[data-source-anchor-id]")
|
|
906
|
-
).some((node) => node.dataset.sourceAnchorId === ownerAnchorId)) {
|
|
907
|
-
const owner = this.document.createElement("span");
|
|
908
|
-
owner.dataset.sourceAnchorId = ownerAnchorId;
|
|
909
|
-
while (clone.firstChild) owner.appendChild(clone.firstChild);
|
|
910
|
-
clone.appendChild(owner);
|
|
911
|
-
}
|
|
912
|
-
if (paragraphIndex === 0) {
|
|
913
|
-
const itemId = item.id;
|
|
914
|
-
if (itemId) {
|
|
915
|
-
if (clone.id && clone.id !== itemId) return null;
|
|
916
|
-
clone.id = itemId;
|
|
917
|
-
}
|
|
918
|
-
const value = parseInt(item.getAttribute("value") || String(itemIndex + 1), 10);
|
|
919
|
-
const marker = this.formatOrderedListMarker(
|
|
920
|
-
Number.isFinite(value) ? value : itemIndex + 1,
|
|
921
|
-
listStyle.listStyleType
|
|
922
|
-
);
|
|
923
|
-
clone.insertBefore(this.document.createTextNode(`${marker}. `), clone.firstChild);
|
|
924
|
-
}
|
|
925
|
-
blocks.push(this.measureElement(clone, dims, sectionIndex));
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
return blocks;
|
|
929
|
-
}
|
|
930
|
-
/** Render the CSS ordered-list formats emitted by the converter after an endnote is flattened. */
|
|
931
|
-
formatOrderedListMarker(value, listStyleType) {
|
|
932
|
-
const format = (() => {
|
|
933
|
-
switch (listStyleType) {
|
|
934
|
-
case "lower-roman":
|
|
935
|
-
return "lowerRoman";
|
|
936
|
-
case "upper-roman":
|
|
937
|
-
return "upperRoman";
|
|
938
|
-
case "lower-alpha":
|
|
939
|
-
case "lower-latin":
|
|
940
|
-
return "lowerLetter";
|
|
941
|
-
case "upper-alpha":
|
|
942
|
-
case "upper-latin":
|
|
943
|
-
return "upperLetter";
|
|
944
|
-
default:
|
|
945
|
-
return "decimal";
|
|
946
|
-
}
|
|
947
|
-
})();
|
|
948
|
-
return formatPageNumber(value, format);
|
|
949
|
-
}
|
|
950
|
-
/**
|
|
951
|
-
* Flows a multi-column (`w:cols`) section's children into CSS-multicol container
|
|
952
|
-
* blocks. Word lays such a section out as N columns inside the same body extent;
|
|
953
|
-
* a balanced `column-count` container reproduces that geometry, and the paginator
|
|
954
|
-
* then places each container as one ordinary measured block. A container grows
|
|
955
|
-
* greedily until its balanced height would exceed the smallest page body available
|
|
956
|
-
* to the section, so a long columned section still splits across pages at block
|
|
957
|
-
* boundaries. Each child lands in exactly one container, so anchors never
|
|
958
|
-
* duplicate. An explicit page break child passes through as its own block, which
|
|
959
|
-
* ends the current container and lets the normal flow logic turn the page.
|
|
960
|
-
*/
|
|
961
|
-
buildColumnBlocks(section, dims, sectionIndex, columnCount, columnGapPt) {
|
|
962
|
-
const children = Array.from(section.children);
|
|
963
|
-
const blocks = [];
|
|
964
|
-
const maxFragmentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);
|
|
965
|
-
const isBreak = (child) => child.dataset.pageBreak === "true" || child.classList.contains(`${this.cssPrefix}break`);
|
|
966
|
-
const makeContainer = (slice) => {
|
|
967
|
-
const container = this.document.createElement("div");
|
|
968
|
-
container.style.columnCount = String(columnCount);
|
|
969
|
-
container.style.columnGap = `${columnGapPt}pt`;
|
|
970
|
-
for (const child of slice) {
|
|
971
|
-
container.appendChild(child.cloneNode(true));
|
|
972
|
-
}
|
|
973
|
-
return container;
|
|
974
|
-
};
|
|
975
|
-
let start = 0;
|
|
976
|
-
while (start < children.length) {
|
|
977
|
-
this.checkpoint();
|
|
978
|
-
if (isBreak(children[start])) {
|
|
979
|
-
blocks.push(this.measureElement(children[start], dims, sectionIndex));
|
|
980
|
-
start++;
|
|
981
|
-
continue;
|
|
982
|
-
}
|
|
983
|
-
let end = start + 1;
|
|
984
|
-
let container = makeContainer(children.slice(start, end));
|
|
985
|
-
let measured = this.measureElement(container, dims, sectionIndex);
|
|
986
|
-
while (end < children.length && !isBreak(children[end])) {
|
|
987
|
-
this.checkpoint();
|
|
988
|
-
const candidate = makeContainer(children.slice(start, end + 1));
|
|
989
|
-
const candidateMeasured = this.measureElement(candidate, dims, sectionIndex);
|
|
990
|
-
if (candidateMeasured.heightPt > maxFragmentHeight) break;
|
|
991
|
-
container = candidate;
|
|
992
|
-
measured = candidateMeasured;
|
|
993
|
-
end++;
|
|
994
|
-
}
|
|
995
|
-
blocks.push({
|
|
996
|
-
element: container,
|
|
997
|
-
sectionIndex,
|
|
998
|
-
heightPt: measured.heightPt,
|
|
999
|
-
marginTopPt: measured.marginTopPt,
|
|
1000
|
-
marginBottomPt: measured.marginBottomPt,
|
|
1001
|
-
keepWithNext: false,
|
|
1002
|
-
keepLines: false,
|
|
1003
|
-
pageBreakBefore: false,
|
|
1004
|
-
isPageBreak: false,
|
|
1005
|
-
isWordParagraph: false
|
|
1006
|
-
});
|
|
1007
|
-
start = end;
|
|
1008
|
-
}
|
|
1009
|
-
return blocks;
|
|
1010
|
-
}
|
|
1011
|
-
/**
|
|
1012
|
-
* Measures one element in the same hidden staging context used for the source blocks.
|
|
1013
|
-
* This is intentionally DOM-based: table row heights cannot be inferred from individual
|
|
1014
|
-
* rows because wrapping and collapsed borders change the height of a fragment.
|
|
1015
|
-
*/
|
|
1016
|
-
measureElement(element, dims, sectionIndex) {
|
|
1017
|
-
const measurementHost = this.document.createElement("div");
|
|
1018
|
-
measurementHost.style.position = "absolute";
|
|
1019
|
-
measurementHost.style.visibility = "hidden";
|
|
1020
|
-
measurementHost.style.left = "-9999px";
|
|
1021
|
-
measurementHost.style.width = `${dims.contentWidth}pt`;
|
|
1022
|
-
const measuredElement = element.cloneNode(true);
|
|
1023
|
-
measurementHost.appendChild(measuredElement);
|
|
1024
|
-
this.stagingElement.appendChild(measurementHost);
|
|
1025
|
-
const rect = measuredElement.getBoundingClientRect();
|
|
1026
|
-
const style = this.view.getComputedStyle(measuredElement);
|
|
1027
|
-
const measured = {
|
|
1028
|
-
element,
|
|
1029
|
-
sectionIndex,
|
|
1030
|
-
heightPt: pxToPt(rect.height),
|
|
1031
|
-
marginTopPt: pxToPt(parseFloat(style.marginTop) || 0),
|
|
1032
|
-
marginBottomPt: pxToPt(parseFloat(style.marginBottom) || 0),
|
|
1033
|
-
keepWithNext: element.dataset.keepWithNext === "true",
|
|
1034
|
-
keepLines: element.dataset.keepLines === "true",
|
|
1035
|
-
pageBreakBefore: element.dataset.pageBreakBefore === "true",
|
|
1036
|
-
isPageBreak: element.dataset.pageBreak === "true" || element.classList.contains(`${this.cssPrefix}break`),
|
|
1037
|
-
isWordParagraph: this.isWordParagraphElement(element)
|
|
1038
|
-
};
|
|
1039
|
-
this.stagingElement.removeChild(measurementHost);
|
|
1040
|
-
return measured;
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Returns the contiguous keep-with-next chain beginning at a block.
|
|
1044
|
-
*
|
|
1045
|
-
* A hard page break or a page-break-before directive is stronger than a
|
|
1046
|
-
* keep-with-next directive, so it terminates the chain. The caller only
|
|
1047
|
-
* keeps a chain together when the whole chain can fit on a fresh page.
|
|
1048
|
-
*/
|
|
1049
|
-
getKeepWithNextChain(blocks, startIndex) {
|
|
1050
|
-
const firstBlock = blocks[startIndex];
|
|
1051
|
-
if (!firstBlock) return [];
|
|
1052
|
-
const chain = [firstBlock];
|
|
1053
|
-
let lastIndex = startIndex;
|
|
1054
|
-
while (blocks[lastIndex].keepWithNext) {
|
|
1055
|
-
const nextBlock = blocks[lastIndex + 1];
|
|
1056
|
-
if (!nextBlock || nextBlock.isPageBreak || nextBlock.pageBreakBefore) {
|
|
1057
|
-
break;
|
|
1058
|
-
}
|
|
1059
|
-
chain.push(nextBlock);
|
|
1060
|
-
lastIndex++;
|
|
1061
|
-
}
|
|
1062
|
-
return chain;
|
|
1063
|
-
}
|
|
1064
|
-
/**
|
|
1065
|
-
* Measures the visible body height of a keep-with-next chain using the same
|
|
1066
|
-
* collapsed-margin rules as normal block placement. The trailing margin is
|
|
1067
|
-
* intentionally excluded, matching the individual block fit check.
|
|
1068
|
-
*/
|
|
1069
|
-
measureKeepWithNextChainBodyHeight(chain, previousMarginBottomPt, isFirstOnPage, pageInSection) {
|
|
1070
|
-
const firstBlock = chain[0];
|
|
1071
|
-
if (!firstBlock) return 0;
|
|
1072
|
-
const firstMarginTop = this.effectiveBlockMarginTop(
|
|
1073
|
-
firstBlock,
|
|
1074
|
-
previousMarginBottomPt,
|
|
1075
|
-
isFirstOnPage,
|
|
1076
|
-
pageInSection
|
|
1077
|
-
);
|
|
1078
|
-
let bodyHeight = firstMarginTop + firstBlock.heightPt;
|
|
1079
|
-
for (let index = 1; index < chain.length; index++) {
|
|
1080
|
-
const previousBlock = chain[index - 1];
|
|
1081
|
-
const block = chain[index];
|
|
1082
|
-
bodyHeight += Math.max(previousBlock.marginBottomPt, block.marginTopPt) + block.heightPt;
|
|
1083
|
-
}
|
|
1084
|
-
return bodyHeight;
|
|
1085
|
-
}
|
|
1086
|
-
/** Word paragraphs render as `p`, or as `h1`–`h6` when their style has an outline level. */
|
|
1087
|
-
isWordParagraphElement(element) {
|
|
1088
|
-
return element.tagName === "P" || /^H[1-6]$/.test(element.tagName);
|
|
1089
|
-
}
|
|
1090
|
-
shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection) {
|
|
1091
|
-
return isFirstOnPage && pageInSection > 1 && block.isWordParagraph === true;
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Resolves the part of a block's top margin that consumes the current page.
|
|
1095
|
-
*
|
|
1096
|
-
* In Word's native DOCX layout, paragraph space-before is suppressed when a paragraph is the
|
|
1097
|
-
* first body block on a later page of the SAME section. The first page of a document/section is
|
|
1098
|
-
* the exception and keeps its spacing. Tables and other block margins are not paragraph spacing,
|
|
1099
|
-
* so they continue to use the ordinary CSS collapsing rule.
|
|
1100
|
-
*/
|
|
1101
|
-
effectiveBlockMarginTop(block, previousMarginBottomPt, isFirstOnPage, pageInSection) {
|
|
1102
|
-
if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {
|
|
1103
|
-
return 0;
|
|
1104
|
-
}
|
|
1105
|
-
return isFirstOnPage ? block.marginTopPt : Math.max(block.marginTopPt, previousMarginBottomPt) - previousMarginBottomPt;
|
|
1106
|
-
}
|
|
1107
|
-
/** Clone a source block with the same page-top spacing decision used by the height budget. */
|
|
1108
|
-
cloneBlockForPage(block, isFirstOnPage, pageInSection) {
|
|
1109
|
-
const clone = block.element.cloneNode(true);
|
|
1110
|
-
if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {
|
|
1111
|
-
clone.style.setProperty("margin-top", "0", "important");
|
|
1112
|
-
}
|
|
1113
|
-
return clone;
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Finds footnote references introduced by a sequence of blocks, preserving
|
|
1117
|
-
* document order and excluding references already assigned to the page.
|
|
1118
|
-
*/
|
|
1119
|
-
collectNewFootnoteIds(blocks, existingFootnoteIds) {
|
|
1120
|
-
const knownIds = new Set(existingFootnoteIds);
|
|
1121
|
-
const newIds = [];
|
|
1122
|
-
for (const block of blocks) {
|
|
1123
|
-
this.checkpoint();
|
|
1124
|
-
for (const id of this.extractFootnoteRefs(block.element)) {
|
|
1125
|
-
if (!knownIds.has(id)) {
|
|
1126
|
-
knownIds.add(id);
|
|
1127
|
-
newIds.push(id);
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
return newIds;
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* The shortest body available to a section's first, default, or even page.
|
|
1135
|
-
* A row fragment must fit every variant, otherwise a later header/footer could
|
|
1136
|
-
* send it through the oversized-block fallback again.
|
|
1137
|
-
*/
|
|
1138
|
-
smallestEffectiveContentHeight(dims, sectionIndex) {
|
|
1139
|
-
return Math.min(
|
|
1140
|
-
this.getPageBands(dims, sectionIndex, 1, 1).bodyHeight,
|
|
1141
|
-
this.getPageBands(dims, sectionIndex, 2, 2).bodyHeight,
|
|
1142
|
-
this.getPageBands(dims, sectionIndex, 3, 3).bodyHeight
|
|
1143
|
-
);
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Builds a clone of a simple table wrapper containing a contiguous run of rows.
|
|
1147
|
-
* Complex table features are deliberately rejected by the caller: a split across
|
|
1148
|
-
* vertically merged cells (rowspan), nested tables, or footnotes cannot be made
|
|
1149
|
-
* correct by cloning rows. Horizontal merges (colspan) need no rejection: a colspan
|
|
1150
|
-
* is confined to its own row, and rows are only ever cloned whole.
|
|
1151
|
-
*/
|
|
1152
|
-
createSimpleTableFragment(wrapper, table, body, rows, retainAnchor) {
|
|
1153
|
-
const wrapperClone = wrapper.cloneNode(false);
|
|
1154
|
-
const tableClone = table.cloneNode(false);
|
|
1155
|
-
for (const child of Array.from(table.children)) {
|
|
1156
|
-
if (child !== body) {
|
|
1157
|
-
tableClone.appendChild(child.cloneNode(true));
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
const bodyClone = body.cloneNode(false);
|
|
1161
|
-
for (const row of rows) {
|
|
1162
|
-
bodyClone.appendChild(row.cloneNode(true));
|
|
1163
|
-
}
|
|
1164
|
-
tableClone.appendChild(bodyClone);
|
|
1165
|
-
if (!retainAnchor) {
|
|
1166
|
-
wrapperClone.removeAttribute("data-anchor");
|
|
1167
|
-
tableClone.removeAttribute("data-anchor");
|
|
1168
|
-
}
|
|
1169
|
-
wrapperClone.appendChild(tableClone);
|
|
1170
|
-
return wrapperClone;
|
|
1171
|
-
}
|
|
1172
|
-
/**
|
|
1173
|
-
* Splits an oversized, ordinary table at row boundaries. This only participates
|
|
1174
|
-
* in the existing oversized-block fallback; unsupported tables keep the previous
|
|
1175
|
-
* overflow behavior rather than risking broken table semantics.
|
|
1176
|
-
*/
|
|
1177
|
-
trySplitSimpleOversizedTable(block, dims, sectionIndex) {
|
|
1178
|
-
const wrapper = block.element;
|
|
1179
|
-
if (wrapper.tagName !== "DIV" || wrapper.children.length !== 1 || block.keepWithNext || block.keepLines || block.pageBreakBefore || block.isPageBreak) {
|
|
1180
|
-
return null;
|
|
1181
|
-
}
|
|
1182
|
-
const tableElement = wrapper.firstElementChild;
|
|
1183
|
-
if (!tableElement || tableElement.localName !== "table") {
|
|
1184
|
-
return null;
|
|
1185
|
-
}
|
|
1186
|
-
const table = tableElement;
|
|
1187
|
-
const body = table.tBodies.length === 1 ? table.tBodies[0] : null;
|
|
1188
|
-
if (!body || table.tHead || table.tFoot || body.rows.length < 2 || Array.from(table.children).some((child) => child !== body && child.tagName !== "COLGROUP") || table.querySelector("table, [rowspan], [data-footnote-id]") || wrapper.querySelector("[data-footnote-id]")) {
|
|
1189
|
-
return null;
|
|
1190
|
-
}
|
|
1191
|
-
const rows = Array.from(body.rows);
|
|
1192
|
-
const minimumContentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);
|
|
1193
|
-
const maximumFragmentHeight = minimumContentHeight - block.marginTopPt - block.marginBottomPt;
|
|
1194
|
-
if (maximumFragmentHeight <= 0) {
|
|
1195
|
-
return null;
|
|
1196
|
-
}
|
|
1197
|
-
const groups = [];
|
|
1198
|
-
let start = 0;
|
|
1199
|
-
while (start < rows.length) {
|
|
1200
|
-
this.checkpoint();
|
|
1201
|
-
let end = start;
|
|
1202
|
-
while (end < rows.length) {
|
|
1203
|
-
this.checkpoint();
|
|
1204
|
-
const candidate = this.createSimpleTableFragment(
|
|
1205
|
-
wrapper,
|
|
1206
|
-
table,
|
|
1207
|
-
body,
|
|
1208
|
-
rows.slice(start, end + 1),
|
|
1209
|
-
start === 0
|
|
1210
|
-
);
|
|
1211
|
-
const measured = this.measureElement(candidate, dims, block.sectionIndex);
|
|
1212
|
-
if (measured.heightPt > maximumFragmentHeight) {
|
|
1213
|
-
break;
|
|
1214
|
-
}
|
|
1215
|
-
end++;
|
|
1216
|
-
}
|
|
1217
|
-
if (end === start) {
|
|
1218
|
-
return null;
|
|
1219
|
-
}
|
|
1220
|
-
groups.push(rows.slice(start, end));
|
|
1221
|
-
start = end;
|
|
1222
|
-
}
|
|
1223
|
-
if (groups.length < 2) {
|
|
1224
|
-
return null;
|
|
1225
|
-
}
|
|
1226
|
-
const fragments = [];
|
|
1227
|
-
for (let index = 0; index < groups.length; index++) {
|
|
1228
|
-
const isFirst = index === 0;
|
|
1229
|
-
const isLast = index === groups.length - 1;
|
|
1230
|
-
const fragment = this.createSimpleTableFragment(
|
|
1231
|
-
wrapper,
|
|
1232
|
-
table,
|
|
1233
|
-
body,
|
|
1234
|
-
groups[index],
|
|
1235
|
-
isFirst
|
|
1236
|
-
);
|
|
1237
|
-
if (!isFirst) {
|
|
1238
|
-
fragment.style.setProperty("margin-top", "0", "important");
|
|
1239
|
-
}
|
|
1240
|
-
if (!isLast) {
|
|
1241
|
-
fragment.style.setProperty("margin-bottom", "0", "important");
|
|
1242
|
-
}
|
|
1243
|
-
const measured = this.measureElement(fragment, dims, block.sectionIndex);
|
|
1244
|
-
if (measured.heightPt + measured.marginTopPt + measured.marginBottomPt > minimumContentHeight) {
|
|
1245
|
-
return null;
|
|
1246
|
-
}
|
|
1247
|
-
fragments.push({
|
|
1248
|
-
...measured,
|
|
1249
|
-
keepWithNext: false,
|
|
1250
|
-
keepLines: false,
|
|
1251
|
-
pageBreakBefore: false,
|
|
1252
|
-
isPageBreak: false
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
|
-
return fragments;
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* DOM endpoints that can finish a paragraph fragment. The flattened UTF-16
|
|
1259
|
-
* offsets are checked against the browser's Unicode grapheme segmenter, so a
|
|
1260
|
-
* formatting-run boundary can never bisect a surrogate pair, combining
|
|
1261
|
-
* sequence, or joined emoji. NBSP/word-joiner boundaries remain indivisible.
|
|
1262
|
-
* PAGE/NUMPAGES field results are atomic because splitting their marker would
|
|
1263
|
-
* make later substitution duplicate or replace only half of the field.
|
|
1264
|
-
*/
|
|
1265
|
-
paragraphFragmentEndpoints(paragraph, emergencyGraphemeBreaks = false) {
|
|
1266
|
-
const textNodes = [];
|
|
1267
|
-
const textStarts = /* @__PURE__ */ new Map();
|
|
1268
|
-
const lastTextByField = /* @__PURE__ */ new Map();
|
|
1269
|
-
const walker = this.document.createTreeWalker(paragraph, this.view.NodeFilter.SHOW_TEXT);
|
|
1270
|
-
const flattenedChunks = [];
|
|
1271
|
-
let flattenedLength = 0;
|
|
1272
|
-
let textNode;
|
|
1273
|
-
while (textNode = walker.nextNode()) {
|
|
1274
|
-
this.checkpoint();
|
|
1275
|
-
textStarts.set(textNode, flattenedLength);
|
|
1276
|
-
textNodes.push(textNode);
|
|
1277
|
-
flattenedChunks.push(textNode.data);
|
|
1278
|
-
flattenedLength += textNode.data.length;
|
|
1279
|
-
let field = textNode.parentElement?.closest("[data-field]") ?? null;
|
|
1280
|
-
while (field?.parentElement?.closest("[data-field]")) {
|
|
1281
|
-
field = field.parentElement.closest("[data-field]");
|
|
1282
|
-
}
|
|
1283
|
-
if (field && paragraph.contains(field)) lastTextByField.set(field, textNode);
|
|
1284
|
-
}
|
|
1285
|
-
const flattenedText = flattenedChunks.join("");
|
|
1286
|
-
if (!this.hasVisibleText(flattenedText)) return [];
|
|
1287
|
-
const invisible = /[\s\u200B-\u200F\uFEFF]/;
|
|
1288
|
-
let firstVisibleOffset = -1;
|
|
1289
|
-
let lastVisibleOffset = -1;
|
|
1290
|
-
for (let index = 0; index < flattenedText.length; index++) {
|
|
1291
|
-
if (index % 4096 === 0) this.checkpoint();
|
|
1292
|
-
if (invisible.test(flattenedText[index])) continue;
|
|
1293
|
-
if (firstVisibleOffset < 0) firstVisibleOffset = index;
|
|
1294
|
-
lastVisibleOffset = index;
|
|
1295
|
-
}
|
|
1296
|
-
const graphemeBoundaries = this.graphemeBoundaryOffsets(flattenedText);
|
|
1297
|
-
const candidates = [];
|
|
1298
|
-
const addCandidate = (candidate) => {
|
|
1299
|
-
const { textOffset } = candidate;
|
|
1300
|
-
if (textOffset <= 0 || textOffset >= flattenedText.length) return;
|
|
1301
|
-
if (textOffset <= firstVisibleOffset || textOffset > lastVisibleOffset) return;
|
|
1302
|
-
if (this.isNonBreakingTextBoundary(flattenedText, textOffset)) return;
|
|
1303
|
-
if (!this.isLegalParagraphLineBoundary(paragraph, flattenedText, textOffset)) return;
|
|
1304
|
-
if (graphemeBoundaries) {
|
|
1305
|
-
if (!graphemeBoundaries.has(textOffset)) return;
|
|
1306
|
-
} else if (!this.isConservativeFallbackTextBoundary(flattenedText, textOffset)) {
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
candidates.push(candidate);
|
|
1310
|
-
};
|
|
1311
|
-
for (const node of textNodes) {
|
|
1312
|
-
this.checkpoint();
|
|
1313
|
-
const start = textStarts.get(node);
|
|
1314
|
-
const atomic = node.parentElement?.closest(
|
|
1315
|
-
"[data-field], [data-list-marker], a[data-comment-id]"
|
|
1316
|
-
);
|
|
1317
|
-
if (atomic && paragraph.contains(atomic)) continue;
|
|
1318
|
-
const whitespace = /[\u0009-\u000D\u0020]+/g;
|
|
1319
|
-
let match;
|
|
1320
|
-
let matchesSinceCheckpoint = 0;
|
|
1321
|
-
while ((match = whitespace.exec(node.data)) !== null) {
|
|
1322
|
-
if (++matchesSinceCheckpoint % 256 === 0) this.checkpoint();
|
|
1323
|
-
const offset = match.index + match[0].length;
|
|
1324
|
-
addCandidate({ node, offset, textOffset: start + offset, priority: 0 });
|
|
1325
|
-
}
|
|
1326
|
-
addCandidate({
|
|
1327
|
-
node,
|
|
1328
|
-
offset: node.data.length,
|
|
1329
|
-
textOffset: start + node.data.length,
|
|
1330
|
-
priority: 0
|
|
1331
|
-
});
|
|
1332
|
-
if (emergencyGraphemeBreaks) {
|
|
1333
|
-
for (let offset = 1; offset < node.data.length; offset++) {
|
|
1334
|
-
if (offset % 256 === 0) this.checkpoint();
|
|
1335
|
-
addCandidate({
|
|
1336
|
-
node,
|
|
1337
|
-
offset,
|
|
1338
|
-
textOffset: start + offset,
|
|
1339
|
-
priority: -1
|
|
1340
|
-
});
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
for (const field of Array.from(paragraph.querySelectorAll("[data-field]"))) {
|
|
1345
|
-
this.checkpoint();
|
|
1346
|
-
if (field.parentElement?.closest("[data-field]")) continue;
|
|
1347
|
-
const last = lastTextByField.get(field);
|
|
1348
|
-
const parent = field.parentNode;
|
|
1349
|
-
if (!last || !parent) continue;
|
|
1350
|
-
const childIndex = Array.prototype.indexOf.call(parent.childNodes, field);
|
|
1351
|
-
if (childIndex < 0) continue;
|
|
1352
|
-
addCandidate({
|
|
1353
|
-
node: parent,
|
|
1354
|
-
offset: childIndex + 1,
|
|
1355
|
-
textOffset: textStarts.get(last) + last.data.length,
|
|
1356
|
-
priority: 1
|
|
1357
|
-
});
|
|
1358
|
-
}
|
|
1359
|
-
candidates.sort((left, right) => left.textOffset - right.textOffset || right.priority - left.priority);
|
|
1360
|
-
const endpoints = [];
|
|
1361
|
-
for (const candidate of candidates) {
|
|
1362
|
-
if (endpoints[endpoints.length - 1]?.textOffset !== candidate.textOffset) {
|
|
1363
|
-
endpoints.push(candidate);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
return endpoints;
|
|
1367
|
-
}
|
|
1368
|
-
/** Browser-native UAX #29 boundaries; null keeps older runtimes conservative. */
|
|
1369
|
-
graphemeBoundaryOffsets(text) {
|
|
1370
|
-
const SegmenterConstructor = this.view.Intl.Segmenter;
|
|
1371
|
-
if (!SegmenterConstructor) return null;
|
|
1372
|
-
const boundaries = /* @__PURE__ */ new Set([0, text.length]);
|
|
1373
|
-
let segmentCount = 0;
|
|
1374
|
-
for (const part of new SegmenterConstructor("en", { granularity: "grapheme" }).segment(text)) {
|
|
1375
|
-
if (++segmentCount % 256 === 0) this.checkpoint();
|
|
1376
|
-
boundaries.add(part.index);
|
|
1377
|
-
boundaries.add(part.index + part.segment.length);
|
|
1378
|
-
}
|
|
1379
|
-
return boundaries;
|
|
1380
|
-
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Conservative UAX #14/CSS wrapping opportunities used for synthetic page
|
|
1383
|
-
* boundaries. In particular, a formatting-run boundary is not itself a word
|
|
1384
|
-
* boundary, and Japanese opening/closing punctuation stays with its pair.
|
|
1385
|
-
* Arbitrary grapheme breaks are admitted only when the paragraph's CSS asks
|
|
1386
|
-
* the browser to wrap anywhere.
|
|
1387
|
-
*/
|
|
1388
|
-
isLegalParagraphLineBoundary(paragraph, text, offset) {
|
|
1389
|
-
const lastCodeUnit = text.charCodeAt(offset - 1);
|
|
1390
|
-
const beforeOffset = lastCodeUnit >= 56320 && lastCodeUnit <= 57343 ? Math.max(0, offset - 2) : offset - 1;
|
|
1391
|
-
const beforeCodePoint = text.codePointAt(beforeOffset);
|
|
1392
|
-
const afterCodePoint = text.codePointAt(offset);
|
|
1393
|
-
const before = beforeCodePoint === void 0 ? "" : String.fromCodePoint(beforeCodePoint);
|
|
1394
|
-
const after = afterCodePoint === void 0 ? "" : String.fromCodePoint(afterCodePoint);
|
|
1395
|
-
if (/^[\u0009-\u000D\u0020]$/.test(before) || /^[\u0009-\u000D\u0020]$/.test(after)) return true;
|
|
1396
|
-
if (/[-\u00AD\u2010]$/u.test(before)) return true;
|
|
1397
|
-
const style = this.view.getComputedStyle(paragraph);
|
|
1398
|
-
if (style.wordBreak === "break-all" || style.overflowWrap === "anywhere" || style.overflowWrap === "break-word") return true;
|
|
1399
|
-
const eastAsian = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
1400
|
-
if (!eastAsian.test(before) && !eastAsian.test(after)) return false;
|
|
1401
|
-
const opening = "([{<\u2018\u201C\u3008\u300A\u300C\u300E\u3010\u3014\u3016\u3018\u301A\uFF08\uFF3B\uFF5B";
|
|
1402
|
-
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";
|
|
1403
|
-
return !opening.includes(before) && !closing.includes(after);
|
|
1404
|
-
}
|
|
1405
|
-
/** Never fragment across characters whose line-break meaning is explicitly non-breaking. */
|
|
1406
|
-
isNonBreakingTextBoundary(text, offset) {
|
|
1407
|
-
const codePointBefore = (index) => {
|
|
1408
|
-
if (index <= 0) return void 0;
|
|
1409
|
-
const last = text.charCodeAt(index - 1);
|
|
1410
|
-
const start = last >= 56320 && last <= 57343 ? index - 2 : index - 1;
|
|
1411
|
-
return text.codePointAt(Math.max(0, start));
|
|
1412
|
-
};
|
|
1413
|
-
const nonBreaking = /* @__PURE__ */ new Set([
|
|
1414
|
-
160,
|
|
1415
|
-
8206,
|
|
1416
|
-
8207,
|
|
1417
|
-
8209,
|
|
1418
|
-
8234,
|
|
1419
|
-
8235,
|
|
1420
|
-
8236,
|
|
1421
|
-
8237,
|
|
1422
|
-
8238,
|
|
1423
|
-
8239,
|
|
1424
|
-
8288,
|
|
1425
|
-
8294,
|
|
1426
|
-
8295,
|
|
1427
|
-
8296,
|
|
1428
|
-
8297,
|
|
1429
|
-
65279
|
|
1430
|
-
]);
|
|
1431
|
-
return nonBreaking.has(codePointBefore(offset) ?? -1) || nonBreaking.has(text.codePointAt(offset) ?? -1);
|
|
1432
|
-
}
|
|
1433
|
-
/**
|
|
1434
|
-
* Without Intl.Segmenter, admit only an all-ASCII boundary. This still
|
|
1435
|
-
* fragments ordinary prose while refusing to guess about Unicode clusters.
|
|
1436
|
-
*/
|
|
1437
|
-
isConservativeFallbackTextBoundary(text, offset) {
|
|
1438
|
-
return text.charCodeAt(offset - 1) <= 127 && text.charCodeAt(offset) <= 127;
|
|
1439
|
-
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Whether a range contains visible text after ignoring bidi/zero-width marks.
|
|
1442
|
-
* Paragraph fragmentation deliberately excludes non-textual descendants, so
|
|
1443
|
-
* this is enough to reject empty head or tail fragments.
|
|
1444
|
-
*/
|
|
1445
|
-
hasVisibleFragmentText(fragment) {
|
|
1446
|
-
return this.hasVisibleText(fragment.textContent || "");
|
|
1447
|
-
}
|
|
1448
|
-
hasVisibleText(text) {
|
|
1449
|
-
return text.replace(/[\u200B-\u200F\uFEFF]/g, "").trim().length > 0;
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* Phase one only handles text-like paragraph descendants. Objects, explicit
|
|
1453
|
-
* line breaks, list markers, notes, and out-of-flow/inline-block content all
|
|
1454
|
-
* require their own line-layout rules and retain the established whole-block
|
|
1455
|
-
* fallback instead of risking broken content or duplicate anchors.
|
|
1456
|
-
*/
|
|
1457
|
-
canFragmentParagraph(block) {
|
|
1458
|
-
if (!this.fragmentParagraphs) {
|
|
1459
|
-
return false;
|
|
1460
|
-
}
|
|
1461
|
-
const paragraph = block.element;
|
|
1462
|
-
if (block.keepWithNext || block.keepLines || block.pageBreakBefore || block.isPageBreak || paragraph.dataset.widowControl === "true" || paragraph.hasAttribute("contenteditable")) {
|
|
1463
|
-
return false;
|
|
1464
|
-
}
|
|
1465
|
-
return this.canRangeFragmentParagraph(paragraph);
|
|
1466
|
-
}
|
|
1467
|
-
/**
|
|
1468
|
-
* Shared structural gate for body and note paragraph fragmentation. Footnote
|
|
1469
|
-
* first paragraphs are inline beside their marker, so that one known layout
|
|
1470
|
-
* context may opt into an inline root while retaining every descendant and
|
|
1471
|
-
* break-safety restriction used for body text.
|
|
1472
|
-
*/
|
|
1473
|
-
canRangeFragmentParagraph(paragraph, allowInlineRoot = false) {
|
|
1474
|
-
if (paragraph.tagName !== "P" || paragraph.dataset.keepWithNext === "true" || paragraph.dataset.keepLines === "true" || paragraph.dataset.pageBreakBefore === "true" || paragraph.dataset.widowControl === "true" || paragraph.hasAttribute("contenteditable")) {
|
|
1475
|
-
return false;
|
|
1476
|
-
}
|
|
1477
|
-
const unsupportedDescendants = [
|
|
1478
|
-
"br",
|
|
1479
|
-
"img",
|
|
1480
|
-
"picture",
|
|
1481
|
-
"svg",
|
|
1482
|
-
"math",
|
|
1483
|
-
"canvas",
|
|
1484
|
-
"video",
|
|
1485
|
-
"audio",
|
|
1486
|
-
"iframe",
|
|
1487
|
-
"object",
|
|
1488
|
-
"embed",
|
|
1489
|
-
"input",
|
|
1490
|
-
"button",
|
|
1491
|
-
"select",
|
|
1492
|
-
"textarea",
|
|
1493
|
-
"table",
|
|
1494
|
-
"ol",
|
|
1495
|
-
"ul",
|
|
1496
|
-
"li",
|
|
1497
|
-
"dl",
|
|
1498
|
-
"div",
|
|
1499
|
-
"p",
|
|
1500
|
-
"section",
|
|
1501
|
-
"article",
|
|
1502
|
-
"aside",
|
|
1503
|
-
"figure",
|
|
1504
|
-
"fieldset",
|
|
1505
|
-
"details",
|
|
1506
|
-
"[data-footnote-id]",
|
|
1507
|
-
"[contenteditable]"
|
|
1508
|
-
].join(", ");
|
|
1509
|
-
if (paragraph.querySelector(unsupportedDescendants)) {
|
|
1510
|
-
return false;
|
|
1511
|
-
}
|
|
1512
|
-
const isValidatedEndnote = paragraph.dataset.paginationSafeEndnote === "true";
|
|
1513
|
-
return isValidatedEndnote || this.hasRangeFragmentSafeLayout(paragraph, allowInlineRoot);
|
|
1514
|
-
}
|
|
1515
|
-
/**
|
|
1516
|
-
* A range clone preserves nested inline formatting exactly. Anything that establishes its own
|
|
1517
|
-
* box/layout context is deferred until a future fragmenter can model it accurately. Callers
|
|
1518
|
-
* must invoke this while the paragraph is attached to the styled document.
|
|
1519
|
-
*/
|
|
1520
|
-
hasRangeFragmentSafeLayout(paragraph, allowInlineRoot = false) {
|
|
1521
|
-
const paragraphStyle = this.view.getComputedStyle(paragraph);
|
|
1522
|
-
if (paragraphStyle.display !== "block" && !(allowInlineRoot && paragraphStyle.display === "inline") || paragraphStyle.position !== "static" || paragraphStyle.float !== "none" || paragraphStyle.whiteSpace !== "normal" && paragraphStyle.whiteSpace !== "pre-wrap" || paragraphStyle.breakBefore !== "auto" || paragraphStyle.breakAfter !== "auto" || paragraphStyle.breakInside === "avoid" || paragraphStyle.pageBreakBefore !== "auto" || paragraphStyle.pageBreakAfter !== "auto" || paragraphStyle.pageBreakInside === "avoid") {
|
|
1523
|
-
return false;
|
|
1524
|
-
}
|
|
1525
|
-
for (const descendant of Array.from(paragraph.querySelectorAll("*"))) {
|
|
1526
|
-
const style = this.view.getComputedStyle(descendant);
|
|
1527
|
-
if (style.display !== "inline" || style.position !== "static" || style.float !== "none" || style.whiteSpace !== "normal" && style.whiteSpace !== "pre-wrap") {
|
|
1528
|
-
return false;
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
return true;
|
|
1532
|
-
}
|
|
1533
|
-
/**
|
|
1534
|
-
* Builds one range-cloned paragraph fragment. Only the leading fragment keeps
|
|
1535
|
-
* the source paragraph's addressability; continuations must not duplicate an
|
|
1536
|
-
* id/data-anchor in the rendered document.
|
|
1537
|
-
*/
|
|
1538
|
-
createParagraphFragment(paragraph, range, retainSourceIdentity, isFinalFragment) {
|
|
1539
|
-
const fragment = paragraph.cloneNode(false);
|
|
1540
|
-
const contents = range.cloneContents();
|
|
1541
|
-
fragment.appendChild(contents);
|
|
1542
|
-
if (!retainSourceIdentity) {
|
|
1543
|
-
fragment.removeAttribute("id");
|
|
1544
|
-
fragment.removeAttribute("data-anchor");
|
|
1545
|
-
fragment.style.setProperty("margin-top", "0", "important");
|
|
1546
|
-
fragment.style.setProperty("text-indent", "0", "important");
|
|
1547
|
-
}
|
|
1548
|
-
if (!isFinalFragment) {
|
|
1549
|
-
fragment.style.setProperty("margin-bottom", "0", "important");
|
|
1550
|
-
}
|
|
1551
|
-
return fragment;
|
|
1552
|
-
}
|
|
1553
|
-
/**
|
|
1554
|
-
* Range clones repeat an inline ancestor when the split lands inside it. Keep
|
|
1555
|
-
* semantic wrappers (links, comments, formatting) on both sides, but retain a
|
|
1556
|
-
* duplicated HTML/editor identity only on the leading fragment. Targets that
|
|
1557
|
-
* occur wholly after the split are absent from `head` and remain on `tail`.
|
|
1558
|
-
*/
|
|
1559
|
-
reconcileParagraphFragmentIdentities(head, tail) {
|
|
1560
|
-
const elements = (root, selector) => [
|
|
1561
|
-
...root.matches(selector) ? [root] : [],
|
|
1562
|
-
...Array.from(root.querySelectorAll(selector))
|
|
1563
|
-
];
|
|
1564
|
-
const headIds = new Set(elements(head, "[id]").map((element) => element.id));
|
|
1565
|
-
for (const element of elements(tail, "[id]")) {
|
|
1566
|
-
if (headIds.has(element.id)) element.removeAttribute("id");
|
|
1567
|
-
}
|
|
1568
|
-
const headAnchors = new Set(elements(head, "[data-anchor]").map((element) => element.dataset.anchor).filter((anchor) => Boolean(anchor)));
|
|
1569
|
-
for (const element of elements(tail, "[data-anchor]")) {
|
|
1570
|
-
if (!element.dataset.anchor || !headAnchors.has(element.dataset.anchor)) continue;
|
|
1571
|
-
element.removeAttribute("data-anchor");
|
|
1572
|
-
element.removeAttribute("data-committed-text");
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
/**
|
|
1576
|
-
* Range-clone the largest safe prefix accepted by `fits`. The measurement
|
|
1577
|
-
* policy stays with the caller, allowing body blocks and note bands to share
|
|
1578
|
-
* one DOM fragmenter while measuring in their respective layout contexts.
|
|
1579
|
-
*/
|
|
1580
|
-
splitParagraphAtLargestFit(paragraph, fits, options = {}) {
|
|
1581
|
-
const largestFit = (endpoints2) => {
|
|
1582
|
-
const lastEndpoint = endpoints2[endpoints2.length - 1];
|
|
1583
|
-
if (lastEndpoint) {
|
|
1584
|
-
const lastRange = this.document.createRange();
|
|
1585
|
-
lastRange.setStart(paragraph, 0);
|
|
1586
|
-
lastRange.setEnd(lastEndpoint.node, lastEndpoint.offset);
|
|
1587
|
-
const lastContents = lastRange.cloneContents();
|
|
1588
|
-
if (this.hasVisibleFragmentText(lastContents)) {
|
|
1589
|
-
const lastHead = this.createParagraphFragment(paragraph, lastRange, true, false);
|
|
1590
|
-
if (fits(lastHead)) return { endpoint: lastEndpoint, head: lastHead };
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
let low = 0;
|
|
1594
|
-
let high = endpoints2.length - 2;
|
|
1595
|
-
let best2 = null;
|
|
1596
|
-
while (low <= high) {
|
|
1597
|
-
this.checkpoint();
|
|
1598
|
-
const middle = Math.floor((low + high) / 2);
|
|
1599
|
-
const endpoint = endpoints2[middle];
|
|
1600
|
-
const headRange = this.document.createRange();
|
|
1601
|
-
headRange.setStart(paragraph, 0);
|
|
1602
|
-
headRange.setEnd(endpoint.node, endpoint.offset);
|
|
1603
|
-
const headContents = headRange.cloneContents();
|
|
1604
|
-
if (!this.hasVisibleFragmentText(headContents)) {
|
|
1605
|
-
low = middle + 1;
|
|
1606
|
-
continue;
|
|
1607
|
-
}
|
|
1608
|
-
const head = this.createParagraphFragment(paragraph, headRange, true, false);
|
|
1609
|
-
if (fits(head)) {
|
|
1610
|
-
best2 = { endpoint, head };
|
|
1611
|
-
low = middle + 1;
|
|
1612
|
-
} else {
|
|
1613
|
-
high = middle - 1;
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
return best2;
|
|
1617
|
-
};
|
|
1618
|
-
let endpoints = this.paragraphFragmentEndpoints(paragraph);
|
|
1619
|
-
let best = largestFit(endpoints);
|
|
1620
|
-
if (!best && options.emergencyGraphemeBreaks) {
|
|
1621
|
-
endpoints = this.paragraphFragmentEndpoints(paragraph, true);
|
|
1622
|
-
best = largestFit(endpoints);
|
|
1623
|
-
}
|
|
1624
|
-
if (!best && options.forceFirstOnNoFit && endpoints.length > 0) {
|
|
1625
|
-
const endpoint = endpoints[0];
|
|
1626
|
-
const headRange = this.document.createRange();
|
|
1627
|
-
headRange.setStart(paragraph, 0);
|
|
1628
|
-
headRange.setEnd(endpoint.node, endpoint.offset);
|
|
1629
|
-
best = {
|
|
1630
|
-
endpoint,
|
|
1631
|
-
head: this.createParagraphFragment(paragraph, headRange, true, false)
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
if (!best) {
|
|
1635
|
-
return endpoints.length > 0 ? { kind: "no-fit" } : { kind: "indivisible" };
|
|
1636
|
-
}
|
|
1637
|
-
const tailRange = this.document.createRange();
|
|
1638
|
-
tailRange.setStart(best.endpoint.node, best.endpoint.offset);
|
|
1639
|
-
tailRange.setEnd(paragraph, paragraph.childNodes.length);
|
|
1640
|
-
const tailContents = tailRange.cloneContents();
|
|
1641
|
-
if (!this.hasVisibleFragmentText(tailContents)) return { kind: "indivisible" };
|
|
1642
|
-
const tail = this.createParagraphFragment(paragraph, tailRange, false, true);
|
|
1643
|
-
this.reconcileParagraphFragmentIdentities(best.head, tail);
|
|
1644
|
-
return {
|
|
1645
|
-
kind: "split",
|
|
1646
|
-
head: best.head,
|
|
1647
|
-
tail
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
/**
|
|
1651
|
-
* Splits a simple paragraph at the largest DOM Range endpoint that fits the
|
|
1652
|
-
* currently available body space. The caller then processes the tail normally,
|
|
1653
|
-
* allowing it to fragment again on later pages when necessary.
|
|
1654
|
-
*/
|
|
1655
|
-
tryFragmentParagraph(block, dims, availableHeightPt, effectiveMarginTopPt) {
|
|
1656
|
-
if (!this.canFragmentParagraph(block) || availableHeightPt <= effectiveMarginTopPt) {
|
|
1657
|
-
return null;
|
|
1658
|
-
}
|
|
1659
|
-
const split = this.splitParagraphAtLargestFit(block.element, (head) => {
|
|
1660
|
-
const measured = this.measureElement(head, dims, block.sectionIndex);
|
|
1661
|
-
return effectiveMarginTopPt + measured.heightPt <= availableHeightPt;
|
|
1662
|
-
});
|
|
1663
|
-
if (split.kind !== "split") return null;
|
|
1664
|
-
const headMeasured = this.measureElement(split.head, dims, block.sectionIndex);
|
|
1665
|
-
const tailMeasured = this.measureElement(split.tail, dims, block.sectionIndex);
|
|
1666
|
-
return [
|
|
1667
|
-
{
|
|
1668
|
-
...headMeasured,
|
|
1669
|
-
element: split.head,
|
|
1670
|
-
keepWithNext: false,
|
|
1671
|
-
keepLines: false,
|
|
1672
|
-
pageBreakBefore: false,
|
|
1673
|
-
isPageBreak: false
|
|
1674
|
-
},
|
|
1675
|
-
{
|
|
1676
|
-
...tailMeasured,
|
|
1677
|
-
element: split.tail,
|
|
1678
|
-
keepWithNext: false,
|
|
1679
|
-
keepLines: false,
|
|
1680
|
-
pageBreakBefore: false,
|
|
1681
|
-
isPageBreak: false
|
|
1682
|
-
}
|
|
1683
|
-
];
|
|
1684
|
-
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Parses the header/footer registry from the staging element.
|
|
1687
|
-
* Also measures heights during parsing for lazy-loading compatibility.
|
|
1688
|
-
*/
|
|
1689
|
-
parseHeaderFooterRegistry() {
|
|
1690
|
-
const registry = /* @__PURE__ */ new Map();
|
|
1691
|
-
const registryEl = this.stagingElement.querySelector("#pagination-hf-registry");
|
|
1692
|
-
if (!registryEl) return registry;
|
|
1693
|
-
const sectionWidths = /* @__PURE__ */ new Map();
|
|
1694
|
-
const sections = Array.from(this.stagingElement.querySelectorAll("[data-section-index]"));
|
|
1695
|
-
for (const section of sections) {
|
|
1696
|
-
const idx = parseInt(section.dataset.sectionIndex || "0", 10);
|
|
1697
|
-
const contentWidth = parseFloat(section.dataset.contentWidth || "") || DEFAULT_PAGE_WIDTH - 2 * DEFAULT_MARGIN;
|
|
1698
|
-
sectionWidths.set(idx, contentWidth);
|
|
1699
|
-
}
|
|
1700
|
-
const defaultContentWidth = sectionWidths.get(0) || DEFAULT_PAGE_WIDTH - 2 * DEFAULT_MARGIN;
|
|
1701
|
-
const entries = Array.from(registryEl.querySelectorAll("[data-section][data-hf-type]"));
|
|
1702
|
-
for (const entry of entries) {
|
|
1703
|
-
const sectionIndex = parseInt(entry.dataset.section || "0", 10);
|
|
1704
|
-
const hfType = entry.dataset.hfType;
|
|
1705
|
-
if (!registry.has(sectionIndex)) {
|
|
1706
|
-
registry.set(sectionIndex, {});
|
|
1707
|
-
}
|
|
1708
|
-
const section = registry.get(sectionIndex);
|
|
1709
|
-
const content = entry.cloneNode(true);
|
|
1710
|
-
const contentWidth = sectionWidths.get(sectionIndex) || defaultContentWidth;
|
|
1711
|
-
const measuredHeight = this.measureHeaderFooterHeight(content, contentWidth);
|
|
1712
|
-
switch (hfType) {
|
|
1713
|
-
case "header-default":
|
|
1714
|
-
section.headerDefault = content;
|
|
1715
|
-
section.headerDefaultHeight = measuredHeight;
|
|
1716
|
-
break;
|
|
1717
|
-
case "header-first":
|
|
1718
|
-
section.headerFirst = content;
|
|
1719
|
-
section.headerFirstHeight = measuredHeight;
|
|
1720
|
-
break;
|
|
1721
|
-
case "header-even":
|
|
1722
|
-
section.headerEven = content;
|
|
1723
|
-
section.headerEvenHeight = measuredHeight;
|
|
1724
|
-
break;
|
|
1725
|
-
case "footer-default":
|
|
1726
|
-
section.footerDefault = content;
|
|
1727
|
-
section.footerDefaultHeight = measuredHeight;
|
|
1728
|
-
break;
|
|
1729
|
-
case "footer-first":
|
|
1730
|
-
section.footerFirst = content;
|
|
1731
|
-
section.footerFirstHeight = measuredHeight;
|
|
1732
|
-
break;
|
|
1733
|
-
case "footer-even":
|
|
1734
|
-
section.footerEven = content;
|
|
1735
|
-
section.footerEvenHeight = measuredHeight;
|
|
1736
|
-
break;
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
return registry;
|
|
1740
|
-
}
|
|
1741
|
-
/**
|
|
1742
|
-
* Parses the footnote registry from the staging element.
|
|
1743
|
-
*/
|
|
1744
|
-
parseFootnoteRegistry() {
|
|
1745
|
-
const registry = /* @__PURE__ */ new Map();
|
|
1746
|
-
const registryEl = this.stagingElement.querySelector("#pagination-footnote-registry");
|
|
1747
|
-
if (!registryEl) return registry;
|
|
1748
|
-
const entries = Array.from(registryEl.children).filter((entry) => entry instanceof this.view.HTMLElement && entry.hasAttribute("data-footnote-id"));
|
|
1749
|
-
for (const entry of entries) {
|
|
1750
|
-
const footnoteId = entry.dataset.footnoteId;
|
|
1751
|
-
if (footnoteId) {
|
|
1752
|
-
registry.set(footnoteId, entry.cloneNode(true));
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
return registry;
|
|
1756
|
-
}
|
|
1757
|
-
/** Read Word's optional normal and continuation separator stories. */
|
|
1758
|
-
parseFootnoteSeparators() {
|
|
1759
|
-
const registry = this.stagingElement.querySelector("#pagination-footnote-registry");
|
|
1760
|
-
const clone = (kind) => {
|
|
1761
|
-
const source = Array.from(registry?.children ?? []).find(
|
|
1762
|
-
(entry) => entry instanceof this.view.HTMLElement && entry.getAttribute("data-footnote-separator") === kind
|
|
1763
|
-
);
|
|
1764
|
-
return source?.cloneNode(true);
|
|
1765
|
-
};
|
|
1766
|
-
return {
|
|
1767
|
-
normal: clone("normal") ?? null,
|
|
1768
|
-
continuation: clone("continuation") ?? null
|
|
1769
|
-
};
|
|
1770
|
-
}
|
|
1771
|
-
/** Append the exact separator story selected for this initial/continued note band. */
|
|
1772
|
-
appendFootnoteSeparator(container, continuation) {
|
|
1773
|
-
const kind = continuation ? "continuation" : "normal";
|
|
1774
|
-
const source = continuation ? this.footnoteContinuationSeparator : this.footnoteSeparator;
|
|
1775
|
-
if (!source) {
|
|
1776
|
-
const fallback = this.document.createElement("hr");
|
|
1777
|
-
fallback.dataset.footnoteSeparator = kind;
|
|
1778
|
-
container.appendChild(fallback);
|
|
1779
|
-
return;
|
|
1780
|
-
}
|
|
1781
|
-
const clone = source.cloneNode(true);
|
|
1782
|
-
for (const element of [clone, ...Array.from(clone.querySelectorAll("*"))]) {
|
|
1783
|
-
element.removeAttribute("id");
|
|
1784
|
-
element.removeAttribute("data-anchor");
|
|
1785
|
-
element.removeAttribute("data-committed-text");
|
|
1786
|
-
element.removeAttribute("data-source-anchor-id");
|
|
1787
|
-
element.removeAttribute("data-page-fragment-id");
|
|
1788
|
-
element.removeAttribute("data-fragment-index");
|
|
1789
|
-
element.removeAttribute("data-footnote-id");
|
|
1790
|
-
element.removeAttribute("data-comment-id");
|
|
1791
|
-
element.removeAttribute("name");
|
|
1792
|
-
if (element instanceof this.view.HTMLAnchorElement && element.getAttribute("href")?.startsWith("#")) {
|
|
1793
|
-
element.removeAttribute("href");
|
|
1794
|
-
}
|
|
1795
|
-
element.setAttribute("contenteditable", "false");
|
|
1796
|
-
}
|
|
1797
|
-
container.appendChild(clone);
|
|
1798
|
-
}
|
|
1799
|
-
/** Parses the hidden source notes used to render paginated margin comments. */
|
|
1800
|
-
parseCommentMarginRegistry() {
|
|
1801
|
-
const registry = /* @__PURE__ */ new Map();
|
|
1802
|
-
const registryEl = this.stagingElement.querySelector(
|
|
1803
|
-
"#pagination-comment-margin-registry"
|
|
1804
|
-
);
|
|
1805
|
-
if (!registryEl) return registry;
|
|
1806
|
-
for (const entry of Array.from(
|
|
1807
|
-
registryEl.querySelectorAll("[data-comment-id]")
|
|
1808
|
-
)) {
|
|
1809
|
-
const commentId = entry.dataset.commentId;
|
|
1810
|
-
if (!commentId) continue;
|
|
1811
|
-
let root = entry;
|
|
1812
|
-
for (let parent = root.parentElement?.closest("[data-comment-id]"); parent && registryEl.contains(parent); parent = root.parentElement?.closest("[data-comment-id]")) {
|
|
1813
|
-
root = parent;
|
|
1814
|
-
}
|
|
1815
|
-
registry.set(commentId, root.cloneNode(true));
|
|
1816
|
-
}
|
|
1817
|
-
return registry;
|
|
1818
|
-
}
|
|
1819
|
-
/**
|
|
1820
|
-
* Extracts footnote reference IDs from an element.
|
|
1821
|
-
*/
|
|
1822
|
-
extractFootnoteRefs(element) {
|
|
1823
|
-
const refs = element.querySelectorAll("[data-footnote-id]");
|
|
1824
|
-
const ids = [];
|
|
1825
|
-
for (const ref of Array.from(refs)) {
|
|
1826
|
-
const id = ref.dataset.footnoteId;
|
|
1827
|
-
if (id && !ids.includes(id)) {
|
|
1828
|
-
ids.push(id);
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
return ids;
|
|
1832
|
-
}
|
|
1833
|
-
/** Clone a note child for the continuation queue with its original selector position. */
|
|
1834
|
-
cloneFootnoteElementForContinuation(element, sourceIndex) {
|
|
1835
|
-
const clone = element.cloneNode(true);
|
|
1836
|
-
clone.setAttribute(
|
|
1837
|
-
FOOTNOTE_SOURCE_POSITION_ATTR,
|
|
1838
|
-
sourceIndex === 0 ? "first" : "later"
|
|
1839
|
-
);
|
|
1840
|
-
return clone;
|
|
1841
|
-
}
|
|
1842
|
-
/** Remove identities that belong only to the initial registry presentation shell. */
|
|
1843
|
-
makeContinuationShellInert(element) {
|
|
1844
|
-
element.removeAttribute("id");
|
|
1845
|
-
element.removeAttribute("data-anchor");
|
|
1846
|
-
element.removeAttribute("data-committed-text");
|
|
1847
|
-
}
|
|
1848
|
-
/**
|
|
1849
|
-
* Build the exact continuation shape shared by measurement and paint.
|
|
1850
|
-
*
|
|
1851
|
-
* Keep the source `.footnote-item > .footnote-content` ancestry: document
|
|
1852
|
-
* CSS, inherited direction/language, and custom classes frequently target
|
|
1853
|
-
* those shells. Only the number and initial HTML/editor identities are
|
|
1854
|
-
* omitted. A hidden sentinel preserves `p:not(:first-of-type)` for a page
|
|
1855
|
-
* whose first carried element was a later source paragraph.
|
|
1856
|
-
*/
|
|
1857
|
-
createFootnoteContinuationWrapper(continuation, elements = continuation.remainingElements) {
|
|
1858
|
-
const source = this.footnoteRegistry.get(continuation.footnoteId);
|
|
1859
|
-
const wrapper = source ? source.cloneNode(false) : this.document.createElement("div");
|
|
1860
|
-
this.makeContinuationShellInert(wrapper);
|
|
1861
|
-
wrapper.classList.add("footnote-continuation");
|
|
1862
|
-
wrapper.dataset.footnoteId = continuation.footnoteId;
|
|
1863
|
-
if (continuation.sourceAnchorId) {
|
|
1864
|
-
wrapper.dataset.sourceAnchorId = continuation.sourceAnchorId;
|
|
1865
|
-
}
|
|
1866
|
-
const sourceContent = source?.querySelector(".footnote-content");
|
|
1867
|
-
const content = sourceContent ? sourceContent.cloneNode(false) : this.document.createElement("span");
|
|
1868
|
-
this.makeContinuationShellInert(content);
|
|
1869
|
-
if (!sourceContent) content.className = "footnote-content";
|
|
1870
|
-
const first = elements[0];
|
|
1871
|
-
if (first?.tagName === "P" && first.getAttribute(FOOTNOTE_SOURCE_POSITION_ATTR) === "later") {
|
|
1872
|
-
const sentinel = this.document.createElement("p");
|
|
1873
|
-
sentinel.className = "footnote-continuation-position-sentinel";
|
|
1874
|
-
sentinel.setAttribute("aria-hidden", "true");
|
|
1875
|
-
sentinel.style.setProperty("display", "none", "important");
|
|
1876
|
-
content.appendChild(sentinel);
|
|
1877
|
-
}
|
|
1878
|
-
for (let index = 0; index < elements.length; index++) {
|
|
1879
|
-
this.checkpoint();
|
|
1880
|
-
const element = elements[index];
|
|
1881
|
-
const clone = element.cloneNode(true);
|
|
1882
|
-
clone.removeAttribute(FOOTNOTE_SOURCE_POSITION_ATTR);
|
|
1883
|
-
if (index === 0 && clone.tagName === "P") {
|
|
1884
|
-
clone.style.setProperty("display", "block", "important");
|
|
1885
|
-
}
|
|
1886
|
-
content.appendChild(clone);
|
|
1887
|
-
}
|
|
1888
|
-
wrapper.appendChild(content);
|
|
1889
|
-
return wrapper;
|
|
1890
|
-
}
|
|
1891
|
-
/** Build the exact partial-note item shape shared by measurement and paint. */
|
|
1892
|
-
createPartialFootnoteItem(footnote, fittingElements) {
|
|
1893
|
-
const item = footnote.cloneNode(false);
|
|
1894
|
-
const number = footnote.querySelector(".footnote-number");
|
|
1895
|
-
if (number) item.appendChild(number.cloneNode(true));
|
|
1896
|
-
const sourceContent = footnote.querySelector(".footnote-content");
|
|
1897
|
-
const content = sourceContent ? sourceContent.cloneNode(false) : this.document.createElement("span");
|
|
1898
|
-
if (!sourceContent) content.className = "footnote-content";
|
|
1899
|
-
for (const element of fittingElements) {
|
|
1900
|
-
this.checkpoint();
|
|
1901
|
-
content.appendChild(element.cloneNode(true));
|
|
1902
|
-
}
|
|
1903
|
-
item.appendChild(content);
|
|
1904
|
-
return item;
|
|
1905
|
-
}
|
|
1906
|
-
/**
|
|
1907
|
-
* Where a note measurement tree is attached.
|
|
1908
|
-
*
|
|
1909
|
-
* A note band is reserved from a measurement and then painted; if the two
|
|
1910
|
-
* happen under different inherited typography the painted notes overflow the
|
|
1911
|
-
* reserve, which is the invisible clipping this engine exists to avoid. The
|
|
1912
|
-
* registry lives in the staging tree but a band paints inside the output
|
|
1913
|
-
* container, so measure there. The host is never a page box, so a page's
|
|
1914
|
-
* `zoom` cannot scale a reserve that is accounted for in unscaled points.
|
|
1915
|
-
*/
|
|
1916
|
-
noteMeasurementHost() {
|
|
1917
|
-
return this.containerElement.isConnected ? this.containerElement : this.stagingElement;
|
|
1918
|
-
}
|
|
1919
|
-
/**
|
|
1920
|
-
* Measures the height of footnotes for given IDs (in points).
|
|
1921
|
-
* Creates a temporary container to measure the footnotes.
|
|
1922
|
-
* @param footnoteIds - IDs of footnotes to measure
|
|
1923
|
-
* @param contentWidth - Width for measurement
|
|
1924
|
-
* @param continuation - Optional continuation content to include first
|
|
1925
|
-
*/
|
|
1926
|
-
measureFootnotesHeight(footnoteIds, contentWidth, continuation, partialFootnotes) {
|
|
1927
|
-
const hasContinuation = continuation && continuation.remainingElements.length > 0;
|
|
1928
|
-
if (footnoteIds.length === 0 && !hasContinuation) {
|
|
1929
|
-
return 0;
|
|
1930
|
-
}
|
|
1931
|
-
if (this.footnoteRegistry.size === 0 && !hasContinuation) {
|
|
1932
|
-
return 0;
|
|
1933
|
-
}
|
|
1934
|
-
const measureContainer = this.document.createElement("div");
|
|
1935
|
-
measureContainer.style.position = "absolute";
|
|
1936
|
-
measureContainer.style.visibility = "hidden";
|
|
1937
|
-
measureContainer.style.width = `${contentWidth}pt`;
|
|
1938
|
-
measureContainer.style.left = "-9999px";
|
|
1939
|
-
measureContainer.className = this.cssPrefix + "footnotes";
|
|
1940
|
-
this.appendFootnoteSeparator(measureContainer, Boolean(hasContinuation));
|
|
1941
|
-
if (hasContinuation) {
|
|
1942
|
-
measureContainer.appendChild(this.createFootnoteContinuationWrapper(continuation));
|
|
1943
|
-
}
|
|
1944
|
-
const partialById = new Map(
|
|
1945
|
-
partialFootnotes?.map((partial) => [partial.footnoteId, partial]) ?? []
|
|
1946
|
-
);
|
|
1947
|
-
for (const id of footnoteIds) {
|
|
1948
|
-
this.checkpoint();
|
|
1949
|
-
const footnote = this.footnoteRegistry.get(id);
|
|
1950
|
-
if (footnote) {
|
|
1951
|
-
const partial = partialById.get(id);
|
|
1952
|
-
measureContainer.appendChild(partial ? this.createPartialFootnoteItem(footnote, partial.fittingElements) : footnote.cloneNode(true));
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
this.noteMeasurementHost().appendChild(measureContainer);
|
|
1956
|
-
try {
|
|
1957
|
-
return pxToPt(measureContainer.getBoundingClientRect().height);
|
|
1958
|
-
} finally {
|
|
1959
|
-
measureContainer.remove();
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
/**
|
|
1963
|
-
* Partition a continuation for one page's note band, preferring complete children
|
|
1964
|
-
* and range-fragmenting an eligible paragraph when necessary. Always advances by
|
|
1965
|
-
* at least one element so an indivisible oversized paragraph follows the established
|
|
1966
|
-
* clipped fallback without trapping pagination in a loop.
|
|
1967
|
-
*/
|
|
1968
|
-
splitContinuationForPage(continuation, availableHeightPt, contentWidth) {
|
|
1969
|
-
const fitting = [];
|
|
1970
|
-
let remaining = [];
|
|
1971
|
-
const measureContainer = this.document.createElement("div");
|
|
1972
|
-
measureContainer.style.position = "absolute";
|
|
1973
|
-
measureContainer.style.visibility = "hidden";
|
|
1974
|
-
measureContainer.style.width = `${contentWidth}pt`;
|
|
1975
|
-
measureContainer.style.left = "-9999px";
|
|
1976
|
-
measureContainer.className = this.cssPrefix + "footnotes";
|
|
1977
|
-
this.appendFootnoteSeparator(measureContainer, true);
|
|
1978
|
-
const wrapper = this.createFootnoteContinuationWrapper(continuation, []);
|
|
1979
|
-
const content = wrapper.querySelector(":scope > .footnote-content");
|
|
1980
|
-
if (!content) {
|
|
1981
|
-
throw new Error("Footnote continuation is missing its content shell");
|
|
1982
|
-
}
|
|
1983
|
-
measureContainer.appendChild(wrapper);
|
|
1984
|
-
this.noteMeasurementHost().appendChild(measureContainer);
|
|
1985
|
-
try {
|
|
1986
|
-
for (let index = 0; index < continuation.remainingElements.length; index++) {
|
|
1987
|
-
this.checkpoint();
|
|
1988
|
-
const element = continuation.remainingElements[index];
|
|
1989
|
-
const sourcePosition = element.getAttribute(FOOTNOTE_SOURCE_POSITION_ATTR);
|
|
1990
|
-
if (fitting.length === 0 && element.tagName === "P" && element.getAttribute(FOOTNOTE_SOURCE_POSITION_ATTR) === "later") {
|
|
1991
|
-
const sentinel = this.document.createElement("p");
|
|
1992
|
-
sentinel.className = "footnote-continuation-position-sentinel";
|
|
1993
|
-
sentinel.setAttribute("aria-hidden", "true");
|
|
1994
|
-
sentinel.style.setProperty("display", "none", "important");
|
|
1995
|
-
content.appendChild(sentinel);
|
|
1996
|
-
}
|
|
1997
|
-
const candidate = element.cloneNode(true);
|
|
1998
|
-
candidate.removeAttribute(FOOTNOTE_SOURCE_POSITION_ATTR);
|
|
1999
|
-
if (fitting.length === 0 && candidate.tagName === "P") {
|
|
2000
|
-
candidate.style.setProperty("display", "block", "important");
|
|
2001
|
-
}
|
|
2002
|
-
content.appendChild(candidate);
|
|
2003
|
-
const candidateHeight = pxToPt(measureContainer.getBoundingClientRect().height);
|
|
2004
|
-
if (candidateHeight <= availableHeightPt) {
|
|
2005
|
-
fitting.push(element);
|
|
2006
|
-
continue;
|
|
2007
|
-
}
|
|
2008
|
-
const canSplitCandidate = candidate.tagName === "P" && this.canRangeFragmentParagraph(candidate);
|
|
2009
|
-
candidate.remove();
|
|
2010
|
-
let split = null;
|
|
2011
|
-
if (canSplitCandidate) {
|
|
2012
|
-
split = this.splitParagraphAtLargestFit(candidate, (head) => {
|
|
2013
|
-
content.appendChild(head);
|
|
2014
|
-
try {
|
|
2015
|
-
return pxToPt(measureContainer.getBoundingClientRect().height) <= availableHeightPt;
|
|
2016
|
-
} finally {
|
|
2017
|
-
head.remove();
|
|
2018
|
-
}
|
|
2019
|
-
}, {
|
|
2020
|
-
// Both fallbacks below cut where ordinary line breaking would not,
|
|
2021
|
-
// so they are only ever right for an element that owns the whole
|
|
2022
|
-
// band: if this page already carries earlier content, a paragraph
|
|
2023
|
-
// that will not start here belongs intact in the next note band.
|
|
2024
|
-
emergencyGraphemeBreaks: fitting.length === 0,
|
|
2025
|
-
// A continuation page owns the full note band. If even one legal
|
|
2026
|
-
// grapheme is taller than it, clip only that unit and keep draining.
|
|
2027
|
-
forceFirstOnNoFit: fitting.length === 0
|
|
2028
|
-
});
|
|
2029
|
-
}
|
|
2030
|
-
if (split?.kind === "split") {
|
|
2031
|
-
if (sourcePosition) {
|
|
2032
|
-
split.head.setAttribute(FOOTNOTE_SOURCE_POSITION_ATTR, sourcePosition);
|
|
2033
|
-
split.tail.setAttribute(FOOTNOTE_SOURCE_POSITION_ATTR, sourcePosition);
|
|
2034
|
-
}
|
|
2035
|
-
fitting.push(split.head);
|
|
2036
|
-
remaining = [split.tail, ...continuation.remainingElements.slice(index + 1)];
|
|
2037
|
-
} else if (fitting.length === 0) {
|
|
2038
|
-
fitting.push(element);
|
|
2039
|
-
remaining = continuation.remainingElements.slice(index + 1);
|
|
2040
|
-
} else {
|
|
2041
|
-
remaining = continuation.remainingElements.slice(index);
|
|
2042
|
-
}
|
|
2043
|
-
break;
|
|
2044
|
-
}
|
|
2045
|
-
} finally {
|
|
2046
|
-
measureContainer.remove();
|
|
2047
|
-
}
|
|
2048
|
-
return {
|
|
2049
|
-
current: {
|
|
2050
|
-
footnoteId: continuation.footnoteId,
|
|
2051
|
-
sourceAnchorId: continuation.sourceAnchorId,
|
|
2052
|
-
remainingElements: fitting
|
|
2053
|
-
},
|
|
2054
|
-
overflow: remaining.length > 0 ? {
|
|
2055
|
-
footnoteId: continuation.footnoteId,
|
|
2056
|
-
sourceAnchorId: continuation.sourceAnchorId,
|
|
2057
|
-
remainingElements: remaining
|
|
2058
|
-
} : null
|
|
2059
|
-
};
|
|
2060
|
-
}
|
|
2061
|
-
/**
|
|
2062
|
-
* Splits a footnote element into parts that fit within the available height.
|
|
2063
|
-
* Returns the elements that fit and the elements that need to continue.
|
|
2064
|
-
*/
|
|
2065
|
-
splitFootnoteToFit(footnoteElement, availableHeightPt, contentWidth, forceProgress = false, existingPayload) {
|
|
2066
|
-
const layoutContext = this.document.createElement("div");
|
|
2067
|
-
layoutContext.style.position = "absolute";
|
|
2068
|
-
layoutContext.style.visibility = "hidden";
|
|
2069
|
-
layoutContext.style.width = `${contentWidth}pt`;
|
|
2070
|
-
layoutContext.style.left = "-9999px";
|
|
2071
|
-
layoutContext.className = this.cssPrefix + "footnotes";
|
|
2072
|
-
this.appendFootnoteSeparator(layoutContext, false);
|
|
2073
|
-
const attachedFootnote = footnoteElement.cloneNode(true);
|
|
2074
|
-
layoutContext.appendChild(attachedFootnote);
|
|
2075
|
-
this.noteMeasurementHost().appendChild(layoutContext);
|
|
2076
|
-
const measureContainer = this.document.createElement("div");
|
|
2077
|
-
measureContainer.style.position = "absolute";
|
|
2078
|
-
measureContainer.style.visibility = "hidden";
|
|
2079
|
-
measureContainer.style.width = `${contentWidth}pt`;
|
|
2080
|
-
measureContainer.style.left = "-9999px";
|
|
2081
|
-
measureContainer.className = this.cssPrefix + "footnotes";
|
|
2082
|
-
const hasContinuation = Boolean(existingPayload?.continuation?.remainingElements.length);
|
|
2083
|
-
this.appendFootnoteSeparator(measureContainer, hasContinuation);
|
|
2084
|
-
if (hasContinuation) {
|
|
2085
|
-
measureContainer.appendChild(this.createFootnoteContinuationWrapper(
|
|
2086
|
-
existingPayload.continuation
|
|
2087
|
-
));
|
|
2088
|
-
}
|
|
2089
|
-
const partialById = new Map(
|
|
2090
|
-
existingPayload?.partialFootnotes?.map((partial) => [partial.footnoteId, partial]) ?? []
|
|
2091
|
-
);
|
|
2092
|
-
for (const id of existingPayload?.footnoteIds ?? []) {
|
|
2093
|
-
this.checkpoint();
|
|
2094
|
-
const source = this.footnoteRegistry.get(id);
|
|
2095
|
-
if (!source) continue;
|
|
2096
|
-
const partial = partialById.get(id);
|
|
2097
|
-
measureContainer.appendChild(partial ? this.createPartialFootnoteItem(source, partial.fittingElements) : source.cloneNode(true));
|
|
2098
|
-
}
|
|
2099
|
-
const measuredPartial = this.createPartialFootnoteItem(attachedFootnote, []);
|
|
2100
|
-
const measuredContent = measuredPartial.querySelector(":scope > .footnote-content");
|
|
2101
|
-
if (!measuredContent) {
|
|
2102
|
-
layoutContext.remove();
|
|
2103
|
-
throw new Error("Footnote is missing its content shell");
|
|
2104
|
-
}
|
|
2105
|
-
measureContainer.appendChild(measuredPartial);
|
|
2106
|
-
this.noteMeasurementHost().appendChild(measureContainer);
|
|
2107
|
-
try {
|
|
2108
|
-
const footnoteContent = attachedFootnote.querySelector(".footnote-content");
|
|
2109
|
-
if (!footnoteContent) {
|
|
2110
|
-
return {
|
|
2111
|
-
fits: Array.from(attachedFootnote.children).map((el) => el.cloneNode(true)),
|
|
2112
|
-
overflow: []
|
|
2113
|
-
};
|
|
2114
|
-
}
|
|
2115
|
-
const children = Array.from(footnoteContent.children);
|
|
2116
|
-
const fits = [];
|
|
2117
|
-
let overflow = [];
|
|
2118
|
-
for (let i = 0; i < children.length; i++) {
|
|
2119
|
-
this.checkpoint();
|
|
2120
|
-
const child = children[i];
|
|
2121
|
-
const measuredCandidate = child.cloneNode(true);
|
|
2122
|
-
measuredContent.appendChild(measuredCandidate);
|
|
2123
|
-
const candidateFits = pxToPt(measureContainer.getBoundingClientRect().height) <= availableHeightPt;
|
|
2124
|
-
if (candidateFits) {
|
|
2125
|
-
fits.push(child.cloneNode(true));
|
|
2126
|
-
continue;
|
|
2127
|
-
}
|
|
2128
|
-
measuredCandidate.remove();
|
|
2129
|
-
const split = this.canRangeFragmentParagraph(child, true) ? this.splitParagraphAtLargestFit(child, (head) => {
|
|
2130
|
-
measuredContent.appendChild(head);
|
|
2131
|
-
try {
|
|
2132
|
-
return pxToPt(measureContainer.getBoundingClientRect().height) <= availableHeightPt;
|
|
2133
|
-
} finally {
|
|
2134
|
-
head.remove();
|
|
2135
|
-
}
|
|
2136
|
-
}, {
|
|
2137
|
-
// Both fallbacks cut where ordinary line breaking would not, so they
|
|
2138
|
-
// are only ever right for an element that owns the whole band: with
|
|
2139
|
-
// earlier siblings already packed here, a paragraph that will not
|
|
2140
|
-
// start belongs intact in the next note band.
|
|
2141
|
-
emergencyGraphemeBreaks: fits.length === 0 && forceProgress,
|
|
2142
|
-
forceFirstOnNoFit: fits.length === 0 && forceProgress
|
|
2143
|
-
}) : null;
|
|
2144
|
-
if (split?.kind === "split") {
|
|
2145
|
-
fits.push(split.head);
|
|
2146
|
-
split.tail.setAttribute(
|
|
2147
|
-
FOOTNOTE_SOURCE_POSITION_ATTR,
|
|
2148
|
-
i === 0 ? "first" : "later"
|
|
2149
|
-
);
|
|
2150
|
-
overflow = [split.tail, ...children.slice(i + 1).map((element, offset) => this.cloneFootnoteElementForContinuation(element, i + 1 + offset))];
|
|
2151
|
-
} else if (split?.kind === "no-fit") {
|
|
2152
|
-
overflow = children.slice(i).map((element, offset) => this.cloneFootnoteElementForContinuation(element, i + offset));
|
|
2153
|
-
} else if (fits.length === 0 && forceProgress) {
|
|
2154
|
-
fits.push(child.cloneNode(true));
|
|
2155
|
-
overflow = children.slice(i + 1).map((element, offset) => this.cloneFootnoteElementForContinuation(element, i + 1 + offset));
|
|
2156
|
-
} else {
|
|
2157
|
-
overflow = children.slice(i).map((element, offset) => this.cloneFootnoteElementForContinuation(element, i + offset));
|
|
2158
|
-
}
|
|
2159
|
-
break;
|
|
2160
|
-
}
|
|
2161
|
-
return { fits, overflow };
|
|
2162
|
-
} finally {
|
|
2163
|
-
measureContainer.remove();
|
|
2164
|
-
layoutContext.remove();
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
/**
|
|
2168
|
-
* Adds footnotes to a page container, including continuation content.
|
|
2169
|
-
*/
|
|
2170
|
-
addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes) {
|
|
2171
|
-
const hasContinuation = continuation && continuation.remainingElements.length > 0;
|
|
2172
|
-
if (footnoteIds.length === 0 && !hasContinuation) {
|
|
2173
|
-
return;
|
|
2174
|
-
}
|
|
2175
|
-
if (this.footnoteRegistry.size === 0 && !hasContinuation) {
|
|
2176
|
-
return;
|
|
2177
|
-
}
|
|
2178
|
-
const maxFootnoteHeight = Math.min(
|
|
2179
|
-
footnoteHeight,
|
|
2180
|
-
bands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO
|
|
2181
|
-
);
|
|
2182
|
-
const footnotesDiv = this.document.createElement("div");
|
|
2183
|
-
footnotesDiv.className = `${this.cssPrefix}footnotes`;
|
|
2184
|
-
footnotesDiv.style.position = "absolute";
|
|
2185
|
-
footnotesDiv.style.bottom = `${dims.pageHeight - (bands.bodyTop + bands.bodyHeight)}pt`;
|
|
2186
|
-
footnotesDiv.style.left = `${dims.marginLeft}pt`;
|
|
2187
|
-
footnotesDiv.style.width = `${dims.contentWidth}pt`;
|
|
2188
|
-
footnotesDiv.style.boxSizing = "border-box";
|
|
2189
|
-
footnotesDiv.style.maxHeight = `${maxFootnoteHeight}pt`;
|
|
2190
|
-
footnotesDiv.style.overflow = "hidden";
|
|
2191
|
-
this.appendFootnoteSeparator(footnotesDiv, Boolean(hasContinuation));
|
|
2192
|
-
if (hasContinuation) {
|
|
2193
|
-
footnotesDiv.appendChild(this.createFootnoteContinuationWrapper(continuation));
|
|
2194
|
-
}
|
|
2195
|
-
for (const id of footnoteIds) {
|
|
2196
|
-
const partial = partialFootnotes?.find((p) => p.footnoteId === id);
|
|
2197
|
-
if (partial) {
|
|
2198
|
-
const footnote = this.footnoteRegistry.get(id);
|
|
2199
|
-
if (footnote) {
|
|
2200
|
-
footnotesDiv.appendChild(
|
|
2201
|
-
this.createPartialFootnoteItem(footnote, partial.fittingElements)
|
|
2202
|
-
);
|
|
2203
|
-
}
|
|
2204
|
-
} else {
|
|
2205
|
-
const footnote = this.footnoteRegistry.get(id);
|
|
2206
|
-
if (footnote) {
|
|
2207
|
-
footnotesDiv.appendChild(footnote.cloneNode(true));
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
pageBox.appendChild(footnotesDiv);
|
|
2212
|
-
}
|
|
2213
|
-
/** Select the section's first/odd/even header from its one-based page position. */
|
|
2214
|
-
selectHeader(sectionIndex, pageInSection, displayedPageNumber) {
|
|
2215
|
-
const sectionHf = this.hfRegistry.get(sectionIndex);
|
|
2216
|
-
if (!sectionHf) return void 0;
|
|
2217
|
-
if (pageInSection === 1 && sectionHf.headerFirst) {
|
|
2218
|
-
return sectionHf.headerFirst;
|
|
2219
|
-
}
|
|
2220
|
-
if (displayedPageNumber % 2 === 0 && sectionHf.headerEven) {
|
|
2221
|
-
return sectionHf.headerEven;
|
|
2222
|
-
}
|
|
2223
|
-
return sectionHf.headerDefault;
|
|
2224
|
-
}
|
|
2225
|
-
/**
|
|
2226
|
-
* The story KIND (`"first"` / `"even"` / `"default"`) a page position selects — the same
|
|
2227
|
-
* decision {@link selectHeader} / {@link selectFooter} make, named rather than resolved to
|
|
2228
|
-
* an element, so a page can advertise which OOXML story it is showing.
|
|
2229
|
-
*/
|
|
2230
|
-
storyKindFor(which, sectionIndex, pageInSection, displayedPageNumber) {
|
|
2231
|
-
const sectionHf = this.hfRegistry.get(sectionIndex);
|
|
2232
|
-
const first = which === "header" ? sectionHf?.headerFirst : sectionHf?.footerFirst;
|
|
2233
|
-
const even = which === "header" ? sectionHf?.headerEven : sectionHf?.footerEven;
|
|
2234
|
-
if (pageInSection === 1 && first) return "first";
|
|
2235
|
-
if (displayedPageNumber % 2 === 0 && even) return "even";
|
|
2236
|
-
return "default";
|
|
2237
|
-
}
|
|
2238
|
-
/** Select the section's first/odd/even footer from its one-based page position. */
|
|
2239
|
-
selectFooter(sectionIndex, pageInSection, displayedPageNumber) {
|
|
2240
|
-
const sectionHf = this.hfRegistry.get(sectionIndex);
|
|
2241
|
-
if (!sectionHf) return void 0;
|
|
2242
|
-
if (pageInSection === 1 && sectionHf.footerFirst) {
|
|
2243
|
-
return sectionHf.footerFirst;
|
|
2244
|
-
}
|
|
2245
|
-
if (displayedPageNumber % 2 === 0 && sectionHf.footerEven) {
|
|
2246
|
-
return sectionHf.footerEven;
|
|
2247
|
-
}
|
|
2248
|
-
return sectionHf.footerDefault;
|
|
2249
|
-
}
|
|
2250
|
-
/**
|
|
2251
|
-
* The header, body, and footer bands for one page position.
|
|
2252
|
-
*
|
|
2253
|
-
* The single owner of "where does anything sit vertically on this page" — placement in
|
|
2254
|
-
* {@link createPage}, the body budget the flow loop spends, and the note area's anchor all
|
|
2255
|
-
* read it, so those three cannot disagree about where the body ends.
|
|
2256
|
-
*
|
|
2257
|
-
* Deterministic: it depends only on the section's page setup and the registry's pre-measured
|
|
2258
|
-
* story heights, never on the page's content, which is what keeps it lazy-loading compatible.
|
|
2259
|
-
*/
|
|
2260
|
-
getPageBands(dims, sectionIndex, pageInSection, displayedPageNumber) {
|
|
2261
|
-
const sectionHf = this.hfRegistry.get(sectionIndex);
|
|
2262
|
-
return resolvePageBands(
|
|
2263
|
-
dims,
|
|
2264
|
-
this.selectStoryHeight(
|
|
2265
|
-
sectionHf?.headerFirstHeight,
|
|
2266
|
-
sectionHf?.headerEvenHeight,
|
|
2267
|
-
sectionHf?.headerDefaultHeight,
|
|
2268
|
-
pageInSection,
|
|
2269
|
-
displayedPageNumber
|
|
2270
|
-
),
|
|
2271
|
-
this.selectStoryHeight(
|
|
2272
|
-
sectionHf?.footerFirstHeight,
|
|
2273
|
-
sectionHf?.footerEvenHeight,
|
|
2274
|
-
sectionHf?.footerDefaultHeight,
|
|
2275
|
-
pageInSection,
|
|
2276
|
-
displayedPageNumber
|
|
2277
|
-
)
|
|
2278
|
-
);
|
|
2279
|
-
}
|
|
2280
|
-
/**
|
|
2281
|
-
* The measured height of the running story this page position selects, mirroring
|
|
2282
|
-
* {@link selectHeader}/{@link selectFooter}. Zero when the page has no such story.
|
|
2283
|
-
*/
|
|
2284
|
-
selectStoryHeight(first, even, fallback, pageInSection, displayedPageNumber) {
|
|
2285
|
-
if (pageInSection === 1 && first != null) return first;
|
|
2286
|
-
if (displayedPageNumber % 2 === 0 && even != null) return even;
|
|
2287
|
-
return fallback ?? 0;
|
|
2288
|
-
}
|
|
2289
|
-
/**
|
|
2290
|
-
* Measures the content height of a header or footer element.
|
|
2291
|
-
*
|
|
2292
|
-
* This is what tells {@link resolvePageBands} whether the story stays inside its margin or
|
|
2293
|
-
* pushes the body, so it must measure the story ALONE — any padding added here would have to
|
|
2294
|
-
* be added to the rendered band too, and the two drifting apart is exactly how a header
|
|
2295
|
-
* silently starts overlapping body text.
|
|
2296
|
-
*/
|
|
2297
|
-
measureHeaderFooterHeight(source, contentWidth) {
|
|
2298
|
-
const measureContainer = this.document.createElement("div");
|
|
2299
|
-
measureContainer.style.position = "absolute";
|
|
2300
|
-
measureContainer.style.visibility = "hidden";
|
|
2301
|
-
measureContainer.style.width = `${contentWidth}pt`;
|
|
2302
|
-
measureContainer.style.left = "-9999px";
|
|
2303
|
-
for (const child of Array.from(source.childNodes)) {
|
|
2304
|
-
measureContainer.appendChild(child.cloneNode(true));
|
|
2305
|
-
}
|
|
2306
|
-
this.stagingElement.appendChild(measureContainer);
|
|
2307
|
-
const rect = measureContainer.getBoundingClientRect();
|
|
2308
|
-
const heightPt = pxToPt(rect.height);
|
|
2309
|
-
this.stagingElement.removeChild(measureContainer);
|
|
2310
|
-
return heightPt;
|
|
2311
|
-
}
|
|
2312
|
-
/**
|
|
2313
|
-
* Flows measured blocks into page containers.
|
|
2314
|
-
* Implements a single-pass, forward-only algorithm that is compatible with future lazy loading.
|
|
2315
|
-
* Supports footnote continuation - long footnotes can split across pages.
|
|
2316
|
-
*/
|
|
2317
|
-
flowToPages(blocks, dims, startPageNumber, sectionIndex, sectionDimensions) {
|
|
2318
|
-
const pages = [];
|
|
2319
|
-
let currentContent = [];
|
|
2320
|
-
let pageNumber = startPageNumber;
|
|
2321
|
-
let pageSectionIndex = sectionIndex;
|
|
2322
|
-
const sectionStartPages = /* @__PURE__ */ new Map([[sectionIndex, startPageNumber]]);
|
|
2323
|
-
const pageInSection = (owner = pageSectionIndex, physicalPage = pageNumber) => physicalPage - (sectionStartPages.get(owner) ?? physicalPage) + 1;
|
|
2324
|
-
const dimensionsFor = (owner = pageSectionIndex) => sectionDimensions.get(owner) ?? dims;
|
|
2325
|
-
const markSectionPlaced = (owner) => {
|
|
2326
|
-
if (!sectionStartPages.has(owner)) sectionStartPages.set(owner, pageNumber);
|
|
2327
|
-
};
|
|
2328
|
-
const previousBox = this.containerElement.querySelector(
|
|
2329
|
-
`.${this.cssPrefix}box:last-of-type`
|
|
2330
|
-
);
|
|
2331
|
-
let precedingDisplayedPageNumber = parseInt(
|
|
2332
|
-
previousBox?.dataset.displayedPageNumber ?? "0",
|
|
2333
|
-
10
|
|
2334
|
-
);
|
|
2335
|
-
const displayedPageNumber = (owner = pageSectionIndex, ownerPageInSection = pageInSection(owner), physicalPage = pageNumber) => {
|
|
2336
|
-
const numbering = this.pageNumbering.get(owner) ?? {};
|
|
2337
|
-
if (numbering.start !== void 0) {
|
|
2338
|
-
return numbering.start + ownerPageInSection - 1;
|
|
2339
|
-
}
|
|
2340
|
-
const ownerNumbering = this.pageNumbering.get(pageSectionIndex) ?? {};
|
|
2341
|
-
const currentPhysicalNumber = ownerNumbering.start !== void 0 ? ownerNumbering.start + pageInSection(pageSectionIndex) - 1 : precedingDisplayedPageNumber + 1;
|
|
2342
|
-
return currentPhysicalNumber + physicalPage - pageNumber;
|
|
2343
|
-
};
|
|
2344
|
-
let { bodyHeight: effectiveContentHeight } = this.getPageBands(
|
|
2345
|
-
dimensionsFor(),
|
|
2346
|
-
pageSectionIndex,
|
|
2347
|
-
pageInSection(),
|
|
2348
|
-
displayedPageNumber()
|
|
2349
|
-
);
|
|
2350
|
-
let remainingHeight = effectiveContentHeight;
|
|
2351
|
-
let prevMarginBottomPt = 0;
|
|
2352
|
-
let currentFootnoteIds = [];
|
|
2353
|
-
let currentPageHasFootnoteReference = false;
|
|
2354
|
-
let currentFootnoteHeight = 0;
|
|
2355
|
-
let currentContinuation = this.pendingFootnoteContinuation;
|
|
2356
|
-
let nextPageContinuation = null;
|
|
2357
|
-
let currentContinuationPartitioned = false;
|
|
2358
|
-
let currentPageAdmitted = false;
|
|
2359
|
-
let deferredFootnoteIds = [];
|
|
2360
|
-
let currentPartialFootnotes = [];
|
|
2361
|
-
const adoptEmptyPageOwner = (owner) => {
|
|
2362
|
-
pageSectionIndex = owner;
|
|
2363
|
-
const bands = this.getPageBands(
|
|
2364
|
-
dimensionsFor(owner),
|
|
2365
|
-
owner,
|
|
2366
|
-
pageInSection(owner),
|
|
2367
|
-
displayedPageNumber(owner)
|
|
2368
|
-
);
|
|
2369
|
-
effectiveContentHeight = bands.bodyHeight;
|
|
2370
|
-
remainingHeight = effectiveContentHeight;
|
|
2371
|
-
};
|
|
2372
|
-
const admitCurrentPage = () => {
|
|
2373
|
-
if (currentPageAdmitted) return;
|
|
2374
|
-
this.admitPageAllocation();
|
|
2375
|
-
currentPageAdmitted = true;
|
|
2376
|
-
};
|
|
2377
|
-
const prepareCurrentContinuation = () => {
|
|
2378
|
-
if (currentContinuationPartitioned || (currentContinuation?.remainingElements.length ?? 0) === 0) return;
|
|
2379
|
-
admitCurrentPage();
|
|
2380
|
-
const ownedDimensions = dimensionsFor();
|
|
2381
|
-
const pageBands = this.getPageBands(
|
|
2382
|
-
ownedDimensions,
|
|
2383
|
-
pageSectionIndex,
|
|
2384
|
-
pageInSection(),
|
|
2385
|
-
displayedPageNumber()
|
|
2386
|
-
);
|
|
2387
|
-
const partition = this.splitContinuationForPage(
|
|
2388
|
-
currentContinuation,
|
|
2389
|
-
pageBands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO,
|
|
2390
|
-
ownedDimensions.contentWidth
|
|
2391
|
-
);
|
|
2392
|
-
currentContinuation = partition.current;
|
|
2393
|
-
if (partition.overflow) nextPageContinuation = partition.overflow;
|
|
2394
|
-
currentContinuationPartitioned = true;
|
|
2395
|
-
currentFootnoteHeight = this.measureFootnotesHeight(
|
|
2396
|
-
currentFootnoteIds,
|
|
2397
|
-
ownedDimensions.contentWidth,
|
|
2398
|
-
currentContinuation,
|
|
2399
|
-
currentPartialFootnotes
|
|
2400
|
-
);
|
|
2401
|
-
};
|
|
2402
|
-
const finishPage = (nextPageSectionIndex = pageSectionIndex, forceEmptyPage = false) => {
|
|
2403
|
-
const hasCurrentContinuation = (currentContinuation?.remainingElements.length ?? 0) > 0;
|
|
2404
|
-
if (!forceEmptyPage && currentContent.length === 0 && currentFootnoteIds.length === 0 && !hasCurrentContinuation) {
|
|
2405
|
-
adoptEmptyPageOwner(nextPageSectionIndex);
|
|
2406
|
-
return;
|
|
2407
|
-
}
|
|
2408
|
-
prepareCurrentContinuation();
|
|
2409
|
-
admitCurrentPage();
|
|
2410
|
-
let pageContinuation = currentContinuation;
|
|
2411
|
-
const ownedPageInSection = pageInSection();
|
|
2412
|
-
const ownedDimensions = dimensionsFor();
|
|
2413
|
-
const ownedDisplayedPageNumber = displayedPageNumber(
|
|
2414
|
-
pageSectionIndex,
|
|
2415
|
-
ownedPageInSection
|
|
2416
|
-
);
|
|
2417
|
-
const pageBands = this.getPageBands(
|
|
2418
|
-
ownedDimensions,
|
|
2419
|
-
pageSectionIndex,
|
|
2420
|
-
ownedPageInSection,
|
|
2421
|
-
ownedDisplayedPageNumber
|
|
2422
|
-
);
|
|
2423
|
-
const maxFootnoteHeight = pageBands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO;
|
|
2424
|
-
pageContinuation = currentContinuation;
|
|
2425
|
-
currentFootnoteHeight = this.measureFootnotesHeight(
|
|
2426
|
-
currentFootnoteIds,
|
|
2427
|
-
ownedDimensions.contentWidth,
|
|
2428
|
-
pageContinuation,
|
|
2429
|
-
currentPartialFootnotes
|
|
2430
|
-
);
|
|
2431
|
-
if (currentContent.length === 0 && currentFootnoteIds.length > 0 && currentPartialFootnotes.length === 0) {
|
|
2432
|
-
const fittingIds = [];
|
|
2433
|
-
for (let index = 0; index < currentFootnoteIds.length; index++) {
|
|
2434
|
-
const footnoteId = currentFootnoteIds[index];
|
|
2435
|
-
const candidateIds = [...fittingIds, footnoteId];
|
|
2436
|
-
const candidateHeight = this.measureFootnotesHeight(
|
|
2437
|
-
candidateIds,
|
|
2438
|
-
ownedDimensions.contentWidth,
|
|
2439
|
-
pageContinuation
|
|
2440
|
-
);
|
|
2441
|
-
const guardedCandidateHeight = candidateHeight + FOOTNOTE_MEASUREMENT_GUARD_PT;
|
|
2442
|
-
if (guardedCandidateHeight <= maxFootnoteHeight) {
|
|
2443
|
-
fittingIds.push(footnoteId);
|
|
2444
|
-
currentFootnoteHeight = guardedCandidateHeight;
|
|
2445
|
-
continue;
|
|
2446
|
-
}
|
|
2447
|
-
const hasPageContinuation = (pageContinuation?.remainingElements.length ?? 0) > 0;
|
|
2448
|
-
if (fittingIds.length === 0 && !hasPageContinuation) {
|
|
2449
|
-
const source = this.footnoteRegistry.get(footnoteId);
|
|
2450
|
-
const split = source ? this.splitFootnoteToFit(
|
|
2451
|
-
source,
|
|
2452
|
-
maxFootnoteHeight - FOOTNOTE_MEASUREMENT_GUARD_PT,
|
|
2453
|
-
ownedDimensions.contentWidth,
|
|
2454
|
-
true
|
|
2455
|
-
) : null;
|
|
2456
|
-
fittingIds.push(footnoteId);
|
|
2457
|
-
if (source && split && split.fits.length > 0 && split.overflow.length > 0) {
|
|
2458
|
-
currentPartialFootnotes.push({ footnoteId, fittingElements: split.fits });
|
|
2459
|
-
nextPageContinuation = {
|
|
2460
|
-
footnoteId,
|
|
2461
|
-
sourceAnchorId: source.dataset.sourceAnchorId,
|
|
2462
|
-
remainingElements: split.overflow
|
|
2463
|
-
};
|
|
2464
|
-
currentFootnoteHeight = maxFootnoteHeight;
|
|
2465
|
-
} else {
|
|
2466
|
-
currentFootnoteHeight = guardedCandidateHeight;
|
|
2467
|
-
}
|
|
2468
|
-
deferredFootnoteIds.push(...currentFootnoteIds.slice(index + 1));
|
|
2469
|
-
} else {
|
|
2470
|
-
deferredFootnoteIds.push(...currentFootnoteIds.slice(index));
|
|
2471
|
-
}
|
|
2472
|
-
break;
|
|
2473
|
-
}
|
|
2474
|
-
currentFootnoteIds = fittingIds;
|
|
2475
|
-
}
|
|
2476
|
-
const page = this.createPage(
|
|
2477
|
-
ownedDimensions,
|
|
2478
|
-
pageNumber,
|
|
2479
|
-
pageSectionIndex,
|
|
2480
|
-
ownedDisplayedPageNumber,
|
|
2481
|
-
currentContent,
|
|
2482
|
-
ownedPageInSection,
|
|
2483
|
-
currentFootnoteIds,
|
|
2484
|
-
currentFootnoteHeight,
|
|
2485
|
-
pageContinuation,
|
|
2486
|
-
currentPartialFootnotes.length > 0 ? currentPartialFootnotes : void 0,
|
|
2487
|
-
false,
|
|
2488
|
-
currentPageAdmitted
|
|
2489
|
-
);
|
|
2490
|
-
pages.push(page);
|
|
2491
|
-
precedingDisplayedPageNumber = ownedDisplayedPageNumber;
|
|
2492
|
-
pageNumber++;
|
|
2493
|
-
currentContent = [];
|
|
2494
|
-
pageSectionIndex = nextPageSectionIndex;
|
|
2495
|
-
if (!sectionStartPages.has(pageSectionIndex)) {
|
|
2496
|
-
sectionStartPages.set(pageSectionIndex, pageNumber);
|
|
2497
|
-
}
|
|
2498
|
-
const newBands = this.getPageBands(
|
|
2499
|
-
dimensionsFor(),
|
|
2500
|
-
pageSectionIndex,
|
|
2501
|
-
pageInSection(),
|
|
2502
|
-
displayedPageNumber()
|
|
2503
|
-
);
|
|
2504
|
-
effectiveContentHeight = newBands.bodyHeight;
|
|
2505
|
-
remainingHeight = effectiveContentHeight;
|
|
2506
|
-
prevMarginBottomPt = 0;
|
|
2507
|
-
currentFootnoteIds = [];
|
|
2508
|
-
currentPageHasFootnoteReference = false;
|
|
2509
|
-
currentPartialFootnotes = [];
|
|
2510
|
-
currentContinuation = nextPageContinuation;
|
|
2511
|
-
nextPageContinuation = null;
|
|
2512
|
-
currentContinuationPartitioned = false;
|
|
2513
|
-
currentPageAdmitted = false;
|
|
2514
|
-
if (deferredFootnoteIds.length > 0) {
|
|
2515
|
-
currentFootnoteIds = [...deferredFootnoteIds];
|
|
2516
|
-
deferredFootnoteIds = [];
|
|
2517
|
-
}
|
|
2518
|
-
const nextDimensions = dimensionsFor();
|
|
2519
|
-
currentFootnoteHeight = currentContinuation ? 0 : this.measureFootnotesHeight(currentFootnoteIds, nextDimensions.contentWidth);
|
|
2520
|
-
};
|
|
2521
|
-
const pageHasPayload = () => currentContent.length > 0 || currentFootnoteIds.length > 0 || (currentContinuation?.remainingElements.length ?? 0) > 0;
|
|
2522
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
2523
|
-
this.checkpoint();
|
|
2524
|
-
const block = blocks[i];
|
|
2525
|
-
const allBlockFootnoteIds = this.extractFootnoteRefs(block.element);
|
|
2526
|
-
if (block.sectionIndex !== pageSectionIndex && currentPageHasFootnoteReference && (!this.footnoteLayoutLikeWord8 || allBlockFootnoteIds.length > 0)) {
|
|
2527
|
-
finishPage(block.sectionIndex);
|
|
2528
|
-
}
|
|
2529
|
-
const pageIsEmpty = currentContent.length === 0 && currentFootnoteIds.length === 0 && (currentContinuation?.remainingElements.length ?? 0) === 0;
|
|
2530
|
-
if (pageIsEmpty && block.sectionIndex !== pageSectionIndex) {
|
|
2531
|
-
adoptEmptyPageOwner(block.sectionIndex);
|
|
2532
|
-
}
|
|
2533
|
-
prepareCurrentContinuation();
|
|
2534
|
-
if (currentFootnoteHeight > 0) {
|
|
2535
|
-
const ownerDimensions = dimensionsFor();
|
|
2536
|
-
const ownerBands = this.getPageBands(
|
|
2537
|
-
ownerDimensions,
|
|
2538
|
-
pageSectionIndex,
|
|
2539
|
-
pageInSection(),
|
|
2540
|
-
displayedPageNumber()
|
|
2541
|
-
);
|
|
2542
|
-
const ownerMaxFootnoteArea = ownerBands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO;
|
|
2543
|
-
if (currentFootnoteHeight > ownerMaxFootnoteArea) {
|
|
2544
|
-
const fittingIds = [];
|
|
2545
|
-
let fittingHeight = 0;
|
|
2546
|
-
if ((currentContinuation?.remainingElements.length ?? 0) === 0 && currentPartialFootnotes.length === 0) {
|
|
2547
|
-
for (const footnoteId of currentFootnoteIds) {
|
|
2548
|
-
this.checkpoint();
|
|
2549
|
-
const candidateHeight = this.measureFootnotesHeight(
|
|
2550
|
-
[...fittingIds, footnoteId],
|
|
2551
|
-
ownerDimensions.contentWidth
|
|
2552
|
-
);
|
|
2553
|
-
if (candidateHeight + FOOTNOTE_MEASUREMENT_GUARD_PT > ownerMaxFootnoteArea) break;
|
|
2554
|
-
fittingIds.push(footnoteId);
|
|
2555
|
-
fittingHeight = candidateHeight;
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
if (fittingIds.length > 0) {
|
|
2559
|
-
deferredFootnoteIds = [
|
|
2560
|
-
...currentFootnoteIds.slice(fittingIds.length),
|
|
2561
|
-
...deferredFootnoteIds
|
|
2562
|
-
];
|
|
2563
|
-
currentFootnoteIds = fittingIds;
|
|
2564
|
-
currentFootnoteHeight = fittingHeight;
|
|
2565
|
-
} else {
|
|
2566
|
-
finishPage(block.sectionIndex);
|
|
2567
|
-
i--;
|
|
2568
|
-
continue;
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
}
|
|
2572
|
-
const blockDimensions = dimensionsFor(block.sectionIndex);
|
|
2573
|
-
const nextBlockPageInSection = pageInSection(block.sectionIndex, pageNumber + 1);
|
|
2574
|
-
const freshBlockPageBodyHeight = this.getPageBands(
|
|
2575
|
-
blockDimensions,
|
|
2576
|
-
block.sectionIndex,
|
|
2577
|
-
nextBlockPageInSection,
|
|
2578
|
-
displayedPageNumber(block.sectionIndex, nextBlockPageInSection, pageNumber + 1)
|
|
2579
|
-
).bodyHeight;
|
|
2580
|
-
if (block.isPageBreak) {
|
|
2581
|
-
finishPage(block.sectionIndex);
|
|
2582
|
-
continue;
|
|
2583
|
-
}
|
|
2584
|
-
if (block.pageBreakBefore && currentContent.length > 0) {
|
|
2585
|
-
finishPage(block.sectionIndex);
|
|
2586
|
-
}
|
|
2587
|
-
const previousBlock = blocks[i - 1];
|
|
2588
|
-
const startsKeepChain = !previousBlock || !previousBlock.keepWithNext || previousBlock.isPageBreak || block.pageBreakBefore;
|
|
2589
|
-
const pageHasOccupiedSpace = currentContent.length > 0 || currentFootnoteHeight > 0;
|
|
2590
|
-
if (pageHasOccupiedSpace && block.keepWithNext && startsKeepChain) {
|
|
2591
|
-
const keepChain = this.getKeepWithNextChain(blocks, i);
|
|
2592
|
-
if (keepChain.length > 1) {
|
|
2593
|
-
const newChainFootnoteIds = this.collectNewFootnoteIds(
|
|
2594
|
-
keepChain,
|
|
2595
|
-
currentFootnoteIds
|
|
2596
|
-
);
|
|
2597
|
-
let additionalChainFootnoteHeight = 0;
|
|
2598
|
-
if (newChainFootnoteIds.length > 0 && this.footnoteRegistry.size > 0) {
|
|
2599
|
-
const totalChainFootnoteHeight = this.measureFootnotesHeight(
|
|
2600
|
-
[...currentFootnoteIds, ...newChainFootnoteIds],
|
|
2601
|
-
blockDimensions.contentWidth,
|
|
2602
|
-
currentContinuation
|
|
2603
|
-
);
|
|
2604
|
-
additionalChainFootnoteHeight = Math.max(
|
|
2605
|
-
0,
|
|
2606
|
-
totalChainFootnoteHeight - currentFootnoteHeight
|
|
2607
|
-
);
|
|
2608
|
-
}
|
|
2609
|
-
const currentChainHeight = this.measureKeepWithNextChainBodyHeight(
|
|
2610
|
-
keepChain,
|
|
2611
|
-
prevMarginBottomPt,
|
|
2612
|
-
currentContent.length === 0,
|
|
2613
|
-
pageInSection(block.sectionIndex)
|
|
2614
|
-
) + additionalChainFootnoteHeight;
|
|
2615
|
-
const currentAvailableHeight = remainingHeight - currentFootnoteHeight;
|
|
2616
|
-
if (currentChainHeight > currentAvailableHeight) {
|
|
2617
|
-
const nextPageBands = this.getPageBands(
|
|
2618
|
-
dimensionsFor(block.sectionIndex),
|
|
2619
|
-
block.sectionIndex,
|
|
2620
|
-
pageInSection(block.sectionIndex, pageNumber + 1),
|
|
2621
|
-
displayedPageNumber(
|
|
2622
|
-
block.sectionIndex,
|
|
2623
|
-
pageInSection(block.sectionIndex, pageNumber + 1),
|
|
2624
|
-
pageNumber + 1
|
|
2625
|
-
)
|
|
2626
|
-
);
|
|
2627
|
-
const freshChainBodyHeight = this.measureKeepWithNextChainBodyHeight(
|
|
2628
|
-
keepChain,
|
|
2629
|
-
0,
|
|
2630
|
-
true,
|
|
2631
|
-
pageInSection(block.sectionIndex, pageNumber + 1)
|
|
2632
|
-
);
|
|
2633
|
-
const freshChainFootnoteHeight = this.measureFootnotesHeight(
|
|
2634
|
-
newChainFootnoteIds,
|
|
2635
|
-
blockDimensions.contentWidth,
|
|
2636
|
-
nextPageContinuation
|
|
2637
|
-
);
|
|
2638
|
-
if (freshChainBodyHeight + freshChainFootnoteHeight <= nextPageBands.bodyHeight) {
|
|
2639
|
-
finishPage(block.sectionIndex);
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
const newFootnoteIds = this.collectNewFootnoteIds([block], currentFootnoteIds);
|
|
2645
|
-
let combinedFootnoteHeight = currentFootnoteHeight;
|
|
2646
|
-
let additionalFootnoteHeight = 0;
|
|
2647
|
-
if (newFootnoteIds.length > 0 && this.footnoteRegistry.size > 0) {
|
|
2648
|
-
const combinedFootnoteIds = [...currentFootnoteIds, ...newFootnoteIds];
|
|
2649
|
-
combinedFootnoteHeight = this.measureFootnotesHeight(
|
|
2650
|
-
combinedFootnoteIds,
|
|
2651
|
-
blockDimensions.contentWidth,
|
|
2652
|
-
currentContinuation,
|
|
2653
|
-
currentPartialFootnotes
|
|
2654
|
-
);
|
|
2655
|
-
additionalFootnoteHeight = Math.max(
|
|
2656
|
-
0,
|
|
2657
|
-
combinedFootnoteHeight - currentFootnoteHeight
|
|
2658
|
-
);
|
|
2659
|
-
}
|
|
2660
|
-
const isFirstOnPage = currentContent.length === 0;
|
|
2661
|
-
const effectiveMarginTop = this.effectiveBlockMarginTop(
|
|
2662
|
-
block,
|
|
2663
|
-
prevMarginBottomPt,
|
|
2664
|
-
isFirstOnPage,
|
|
2665
|
-
pageInSection(block.sectionIndex)
|
|
2666
|
-
);
|
|
2667
|
-
const blockSpace = effectiveMarginTop + block.heightPt + additionalFootnoteHeight;
|
|
2668
|
-
const effectiveRemainingHeight = remainingHeight - currentFootnoteHeight;
|
|
2669
|
-
const bodyContentUsed = effectiveContentHeight - remainingHeight;
|
|
2670
|
-
const maxFootnoteArea = effectiveContentHeight * MAX_FOOTNOTE_AREA_RATIO;
|
|
2671
|
-
if (blockSpace > effectiveRemainingHeight) {
|
|
2672
|
-
const paragraphFragments = this.tryFragmentParagraph(
|
|
2673
|
-
block,
|
|
2674
|
-
blockDimensions,
|
|
2675
|
-
effectiveRemainingHeight,
|
|
2676
|
-
effectiveMarginTop
|
|
2677
|
-
);
|
|
2678
|
-
if (paragraphFragments) {
|
|
2679
|
-
blocks.splice(i, 1, ...paragraphFragments);
|
|
2680
|
-
i--;
|
|
2681
|
-
continue;
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
if (blockSpace <= effectiveRemainingHeight && (combinedFootnoteHeight === 0 || combinedFootnoteHeight + FOOTNOTE_MEASUREMENT_GUARD_PT <= maxFootnoteArea)) {
|
|
2685
|
-
markSectionPlaced(block.sectionIndex);
|
|
2686
|
-
currentContent.push(this.cloneBlockForPage(
|
|
2687
|
-
block,
|
|
2688
|
-
isFirstOnPage,
|
|
2689
|
-
pageInSection(block.sectionIndex)
|
|
2690
|
-
));
|
|
2691
|
-
remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
|
|
2692
|
-
prevMarginBottomPt = block.marginBottomPt;
|
|
2693
|
-
if (newFootnoteIds.length > 0) {
|
|
2694
|
-
currentFootnoteIds.push(...newFootnoteIds);
|
|
2695
|
-
currentFootnoteHeight = combinedFootnoteHeight;
|
|
2696
|
-
}
|
|
2697
|
-
currentPageHasFootnoteReference ||= allBlockFootnoteIds.length > 0;
|
|
2698
|
-
} else if (block.heightPt + this.effectiveBlockMarginTop(
|
|
2699
|
-
block,
|
|
2700
|
-
0,
|
|
2701
|
-
true,
|
|
2702
|
-
pageInSection(block.sectionIndex, pageNumber + 1)
|
|
2703
|
-
) <= freshBlockPageBodyHeight) {
|
|
2704
|
-
const blockSpaceWithoutFootnotes = effectiveMarginTop + block.heightPt;
|
|
2705
|
-
if (newFootnoteIds.length > 0 && blockSpaceWithoutFootnotes <= effectiveRemainingHeight) {
|
|
2706
|
-
markSectionPlaced(block.sectionIndex);
|
|
2707
|
-
currentContent.push(this.cloneBlockForPage(
|
|
2708
|
-
block,
|
|
2709
|
-
isFirstOnPage,
|
|
2710
|
-
pageInSection(block.sectionIndex)
|
|
2711
|
-
));
|
|
2712
|
-
remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
|
|
2713
|
-
prevMarginBottomPt = block.marginBottomPt;
|
|
2714
|
-
const availableForFootnotes = Math.min(
|
|
2715
|
-
maxFootnoteArea,
|
|
2716
|
-
effectiveContentHeight - bodyContentUsed - blockSpaceWithoutFootnotes
|
|
2717
|
-
);
|
|
2718
|
-
const nextPageMaxFootnoteArea = freshBlockPageBodyHeight * MAX_FOOTNOTE_AREA_RATIO;
|
|
2719
|
-
const deferralCannotHelp = availableForFootnotes >= nextPageMaxFootnoteArea;
|
|
2720
|
-
for (let noteIndex = 0; noteIndex < newFootnoteIds.length; noteIndex++) {
|
|
2721
|
-
this.checkpoint();
|
|
2722
|
-
const footnoteId = newFootnoteIds[noteIndex];
|
|
2723
|
-
const footnote = this.footnoteRegistry.get(footnoteId);
|
|
2724
|
-
if (!footnote) continue;
|
|
2725
|
-
if (nextPageContinuation) {
|
|
2726
|
-
deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex));
|
|
2727
|
-
break;
|
|
2728
|
-
}
|
|
2729
|
-
const candidateIds = [...currentFootnoteIds, footnoteId];
|
|
2730
|
-
const candidateHeight = this.measureFootnotesHeight(
|
|
2731
|
-
candidateIds,
|
|
2732
|
-
blockDimensions.contentWidth,
|
|
2733
|
-
currentContinuation,
|
|
2734
|
-
currentPartialFootnotes
|
|
2735
|
-
);
|
|
2736
|
-
const spaceLeftForFootnotes = availableForFootnotes - currentFootnoteHeight;
|
|
2737
|
-
if (candidateHeight + FOOTNOTE_MEASUREMENT_GUARD_PT <= availableForFootnotes) {
|
|
2738
|
-
currentFootnoteIds.push(footnoteId);
|
|
2739
|
-
currentFootnoteHeight = candidateHeight;
|
|
2740
|
-
} else {
|
|
2741
|
-
if (spaceLeftForFootnotes > 20) {
|
|
2742
|
-
const { fits, overflow } = this.splitFootnoteToFit(
|
|
2743
|
-
footnote,
|
|
2744
|
-
availableForFootnotes,
|
|
2745
|
-
blockDimensions.contentWidth,
|
|
2746
|
-
deferralCannotHelp,
|
|
2747
|
-
{
|
|
2748
|
-
footnoteIds: currentFootnoteIds,
|
|
2749
|
-
continuation: currentContinuation,
|
|
2750
|
-
partialFootnotes: currentPartialFootnotes
|
|
2751
|
-
}
|
|
2752
|
-
);
|
|
2753
|
-
if (fits.length > 0) {
|
|
2754
|
-
currentFootnoteIds.push(footnoteId);
|
|
2755
|
-
currentPartialFootnotes.push({
|
|
2756
|
-
footnoteId,
|
|
2757
|
-
fittingElements: fits
|
|
2758
|
-
});
|
|
2759
|
-
if (overflow.length > 0) {
|
|
2760
|
-
nextPageContinuation = {
|
|
2761
|
-
footnoteId,
|
|
2762
|
-
sourceAnchorId: footnote.dataset.sourceAnchorId,
|
|
2763
|
-
remainingElements: overflow
|
|
2764
|
-
};
|
|
2765
|
-
}
|
|
2766
|
-
currentFootnoteHeight = this.measureFootnotesHeight(
|
|
2767
|
-
currentFootnoteIds,
|
|
2768
|
-
blockDimensions.contentWidth,
|
|
2769
|
-
currentContinuation,
|
|
2770
|
-
currentPartialFootnotes
|
|
2771
|
-
);
|
|
2772
|
-
if (overflow.length > 0) {
|
|
2773
|
-
deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex + 1));
|
|
2774
|
-
break;
|
|
2775
|
-
}
|
|
2776
|
-
} else {
|
|
2777
|
-
deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex));
|
|
2778
|
-
break;
|
|
2779
|
-
}
|
|
2780
|
-
} else {
|
|
2781
|
-
deferredFootnoteIds.push(...newFootnoteIds.slice(noteIndex));
|
|
2782
|
-
break;
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
currentPageHasFootnoteReference ||= allBlockFootnoteIds.length > 0;
|
|
2787
|
-
} else {
|
|
2788
|
-
finishPage(block.sectionIndex, !pageHasPayload());
|
|
2789
|
-
i--;
|
|
2790
|
-
continue;
|
|
2791
|
-
}
|
|
2792
|
-
} else {
|
|
2793
|
-
const tableFragments = this.trySplitSimpleOversizedTable(
|
|
2794
|
-
block,
|
|
2795
|
-
dimensionsFor(block.sectionIndex),
|
|
2796
|
-
block.sectionIndex
|
|
2797
|
-
);
|
|
2798
|
-
if (tableFragments) {
|
|
2799
|
-
if (currentContent.length > 0 || currentContinuation) {
|
|
2800
|
-
finishPage(block.sectionIndex);
|
|
2801
|
-
}
|
|
2802
|
-
blocks.splice(i, 1, ...tableFragments);
|
|
2803
|
-
i--;
|
|
2804
|
-
continue;
|
|
2805
|
-
}
|
|
2806
|
-
if (pageHasPayload()) {
|
|
2807
|
-
finishPage(block.sectionIndex);
|
|
2808
|
-
i--;
|
|
2809
|
-
continue;
|
|
2810
|
-
}
|
|
2811
|
-
markSectionPlaced(block.sectionIndex);
|
|
2812
|
-
currentContent.push(this.cloneBlockForPage(
|
|
2813
|
-
block,
|
|
2814
|
-
true,
|
|
2815
|
-
pageInSection(block.sectionIndex)
|
|
2816
|
-
));
|
|
2817
|
-
deferredFootnoteIds.push(...newFootnoteIds);
|
|
2818
|
-
currentPageHasFootnoteReference ||= allBlockFootnoteIds.length > 0;
|
|
2819
|
-
finishPage(block.sectionIndex);
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2822
|
-
finishPage();
|
|
2823
|
-
while (currentFootnoteIds.length > 0 || deferredFootnoteIds.length > 0 || (currentContinuation?.remainingElements.length ?? 0) > 0) {
|
|
2824
|
-
finishPage();
|
|
2825
|
-
}
|
|
2826
|
-
this.pendingFootnoteContinuation = nextPageContinuation;
|
|
2827
|
-
return pages;
|
|
2828
|
-
}
|
|
2829
|
-
/**
|
|
2830
|
-
* Strip block addressing from a header/footer node cloned into a page box.
|
|
2831
|
-
*
|
|
2832
|
-
* A running story is authored ONCE and cloned onto every page, so the clones all carry the same
|
|
2833
|
-
* `data-anchor` — on this document, 42 page boxes claiming one footer paragraph. Left editable,
|
|
2834
|
-
* committing any one of them writes back through that single shared anchor, and the per-page
|
|
2835
|
-
* page-number substitution makes it worse: each clone shows a DIFFERENT number, so a commit
|
|
2836
|
-
* writes that page's number into the story as literal text and destroys the PAGE field.
|
|
2837
|
-
*
|
|
2838
|
-
* Page-box header/footer content is presentation. The docked editing bands
|
|
2839
|
-
* (`editor-headerfooter.ts`) are the addressable affordance, and they exist precisely because a
|
|
2840
|
-
* cloned node cannot be uniquely addressed.
|
|
2841
|
-
*/
|
|
2842
|
-
makeClonedStoryInert(root) {
|
|
2843
|
-
const nodes = [root, ...Array.from(root.querySelectorAll("*"))];
|
|
2844
|
-
for (const el of nodes) {
|
|
2845
|
-
el.removeAttribute("data-anchor");
|
|
2846
|
-
el.removeAttribute("data-committed-text");
|
|
2847
|
-
if (el.getAttribute("contenteditable") !== null) el.setAttribute("contenteditable", "false");
|
|
2848
|
-
}
|
|
2849
|
-
}
|
|
2850
|
-
/** A repeated margin note is presentation, not a second bookmark/link target. */
|
|
2851
|
-
makeClonedMarginCommentInert(root) {
|
|
2852
|
-
this.makeClonedStoryInert(root);
|
|
2853
|
-
const nodes = [root, ...Array.from(root.querySelectorAll("*"))];
|
|
2854
|
-
for (const element of nodes) {
|
|
2855
|
-
element.removeAttribute("id");
|
|
2856
|
-
if (element.localName === "a" && element.getAttribute("href")?.startsWith("#")) {
|
|
2857
|
-
element.removeAttribute("href");
|
|
2858
|
-
element.setAttribute("aria-disabled", "true");
|
|
2859
|
-
element.tabIndex = -1;
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
}
|
|
2863
|
-
/**
|
|
2864
|
-
* Resolves floating DrawingML objects after their anchor paragraphs have landed on a page.
|
|
2865
|
-
*
|
|
2866
|
-
* The converter deliberately carries the OOXML bases and offsets as data instead of flattening
|
|
2867
|
-
* them into CSS: page/margin/column bases belong to the page, while paragraph/line/character
|
|
2868
|
-
* bases belong to the laid-out anchor. Once both coordinate systems exist, promote the object
|
|
2869
|
-
* from the clipped text column into the page box and position it in one shared point space.
|
|
2870
|
-
*/
|
|
2871
|
-
positionDrawingAnchors(pageBox, contentArea, dims, pageNumber) {
|
|
2872
|
-
const anchors = Array.from(
|
|
2873
|
-
contentArea.querySelectorAll('[data-docx-drawing-anchor="true"]')
|
|
2874
|
-
);
|
|
2875
|
-
if (anchors.length === 0) return;
|
|
2876
|
-
const pageRect = pageBox.getBoundingClientRect();
|
|
2877
|
-
const pixelsPerPoint = pageRect.width / dims.pageWidth;
|
|
2878
|
-
if (!(pixelsPerPoint > 0)) return;
|
|
2879
|
-
for (const anchor of anchors) {
|
|
2880
|
-
const staticRect = anchor.getBoundingClientRect();
|
|
2881
|
-
const paragraph = anchor.closest("p, h1, h2, h3, h4, h5, h6");
|
|
2882
|
-
const paragraphRect = paragraph?.getBoundingClientRect() ?? staticRect;
|
|
2883
|
-
const toPageX = (x) => (x - pageRect.left) / pixelsPerPoint;
|
|
2884
|
-
const toPageY = (y) => (y - pageRect.top) / pixelsPerPoint;
|
|
2885
|
-
const context = {
|
|
2886
|
-
staticLeft: toPageX(staticRect.left),
|
|
2887
|
-
staticTop: toPageY(staticRect.top),
|
|
2888
|
-
lineHeight: paragraph ? (parseFloat(this.view.getComputedStyle(paragraph).lineHeight) || staticRect.height) / pixelsPerPoint : staticRect.height / pixelsPerPoint,
|
|
2889
|
-
paragraphLeft: toPageX(paragraphRect.left),
|
|
2890
|
-
paragraphTop: toPageY(paragraphRect.top),
|
|
2891
|
-
paragraphWidth: paragraphRect.width / pixelsPerPoint,
|
|
2892
|
-
paragraphHeight: paragraphRect.height / pixelsPerPoint
|
|
2893
|
-
};
|
|
2894
|
-
const widthReference = this.horizontalAnchorReference(
|
|
2895
|
-
anchor.getAttribute("data-docx-anchor-width-relative") ?? "margin",
|
|
2896
|
-
dims,
|
|
2897
|
-
pageNumber,
|
|
2898
|
-
context
|
|
2899
|
-
);
|
|
2900
|
-
const widthPercent = this.anchorNumber(anchor, "width-percent");
|
|
2901
|
-
if (widthPercent !== void 0) {
|
|
2902
|
-
anchor.style.width = `${widthReference.size * widthPercent / 100}pt`;
|
|
2903
|
-
}
|
|
2904
|
-
const heightReference = this.verticalAnchorReference(
|
|
2905
|
-
anchor.getAttribute("data-docx-anchor-height-relative") ?? "margin",
|
|
2906
|
-
dims,
|
|
2907
|
-
pageNumber,
|
|
2908
|
-
context
|
|
2909
|
-
);
|
|
2910
|
-
const heightPercent = this.anchorNumber(anchor, "height-percent");
|
|
2911
|
-
if (heightPercent !== void 0 && anchor.dataset.docxAnchorAutofit !== "true") {
|
|
2912
|
-
anchor.style.height = `${heightReference.size * heightPercent / 100}pt`;
|
|
2913
|
-
}
|
|
2914
|
-
const sizedRect = anchor.getBoundingClientRect();
|
|
2915
|
-
const width = sizedRect.width / pixelsPerPoint;
|
|
2916
|
-
const height = sizedRect.height / pixelsPerPoint;
|
|
2917
|
-
const horizontal = this.horizontalAnchorReference(
|
|
2918
|
-
anchor.getAttribute("data-docx-anchor-h-relative") ?? "column",
|
|
2919
|
-
dims,
|
|
2920
|
-
pageNumber,
|
|
2921
|
-
context
|
|
2922
|
-
);
|
|
2923
|
-
const vertical = this.verticalAnchorReference(
|
|
2924
|
-
anchor.getAttribute("data-docx-anchor-v-relative") ?? "paragraph",
|
|
2925
|
-
dims,
|
|
2926
|
-
pageNumber,
|
|
2927
|
-
context
|
|
2928
|
-
);
|
|
2929
|
-
const left = this.resolveAnchorAxis(
|
|
2930
|
-
horizontal,
|
|
2931
|
-
width,
|
|
2932
|
-
anchor.getAttribute("data-docx-anchor-h-align"),
|
|
2933
|
-
this.anchorNumber(anchor, "h-offset"),
|
|
2934
|
-
pageNumber
|
|
2935
|
-
);
|
|
2936
|
-
const top = this.resolveAnchorAxis(
|
|
2937
|
-
vertical,
|
|
2938
|
-
height,
|
|
2939
|
-
anchor.getAttribute("data-docx-anchor-v-align"),
|
|
2940
|
-
this.anchorNumber(anchor, "v-offset"),
|
|
2941
|
-
pageNumber
|
|
2942
|
-
);
|
|
2943
|
-
pageBox.appendChild(anchor);
|
|
2944
|
-
anchor.style.position = "absolute";
|
|
2945
|
-
anchor.style.left = `${left}pt`;
|
|
2946
|
-
anchor.style.top = `${top}pt`;
|
|
2947
|
-
anchor.style.margin = "0";
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
anchorNumber(anchor, suffix) {
|
|
2951
|
-
const raw = anchor.getAttribute(`data-docx-anchor-${suffix}`);
|
|
2952
|
-
if (raw === null) return void 0;
|
|
2953
|
-
const value = Number(raw);
|
|
2954
|
-
return Number.isFinite(value) ? value : void 0;
|
|
2955
|
-
}
|
|
2956
|
-
horizontalAnchorReference(relativeFrom, dims, pageNumber, context) {
|
|
2957
|
-
const leftMargin = { start: 0, size: dims.marginLeft };
|
|
2958
|
-
const rightMargin = {
|
|
2959
|
-
start: dims.marginLeft + dims.contentWidth,
|
|
2960
|
-
size: dims.marginRight
|
|
2961
|
-
};
|
|
2962
|
-
switch (relativeFrom) {
|
|
2963
|
-
case "page":
|
|
2964
|
-
return { start: 0, size: dims.pageWidth };
|
|
2965
|
-
case "character":
|
|
2966
|
-
return { start: context.staticLeft, size: 0 };
|
|
2967
|
-
case "leftMargin":
|
|
2968
|
-
return leftMargin;
|
|
2969
|
-
case "rightMargin":
|
|
2970
|
-
return rightMargin;
|
|
2971
|
-
case "insideMargin":
|
|
2972
|
-
return pageNumber % 2 === 1 ? leftMargin : rightMargin;
|
|
2973
|
-
case "outsideMargin":
|
|
2974
|
-
return pageNumber % 2 === 1 ? rightMargin : leftMargin;
|
|
2975
|
-
case "paragraph":
|
|
2976
|
-
return { start: context.paragraphLeft, size: context.paragraphWidth };
|
|
2977
|
-
case "column":
|
|
2978
|
-
case "margin":
|
|
2979
|
-
default:
|
|
2980
|
-
return { start: dims.marginLeft, size: dims.contentWidth };
|
|
2981
|
-
}
|
|
2982
|
-
}
|
|
2983
|
-
verticalAnchorReference(relativeFrom, dims, pageNumber, context) {
|
|
2984
|
-
const topMargin = { start: 0, size: dims.marginTop };
|
|
2985
|
-
const bottomMargin = {
|
|
2986
|
-
start: dims.marginTop + dims.contentHeight,
|
|
2987
|
-
size: dims.marginBottom
|
|
2988
|
-
};
|
|
2989
|
-
switch (relativeFrom) {
|
|
2990
|
-
case "page":
|
|
2991
|
-
return { start: 0, size: dims.pageHeight };
|
|
2992
|
-
case "paragraph":
|
|
2993
|
-
return { start: context.paragraphTop, size: context.paragraphHeight };
|
|
2994
|
-
case "line":
|
|
2995
|
-
return { start: context.staticTop, size: context.lineHeight };
|
|
2996
|
-
case "topMargin":
|
|
2997
|
-
return topMargin;
|
|
2998
|
-
case "bottomMargin":
|
|
2999
|
-
return bottomMargin;
|
|
3000
|
-
case "insideMargin":
|
|
3001
|
-
return pageNumber % 2 === 1 ? topMargin : bottomMargin;
|
|
3002
|
-
case "outsideMargin":
|
|
3003
|
-
return pageNumber % 2 === 1 ? bottomMargin : topMargin;
|
|
3004
|
-
case "margin":
|
|
3005
|
-
default:
|
|
3006
|
-
return { start: dims.marginTop, size: dims.contentHeight };
|
|
3007
|
-
}
|
|
3008
|
-
}
|
|
3009
|
-
resolveAnchorAxis(reference, objectSize, alignment, offset, pageNumber) {
|
|
3010
|
-
if (offset !== void 0) return reference.start + offset;
|
|
3011
|
-
let fraction = 0;
|
|
3012
|
-
if (alignment === "center") fraction = 0.5;
|
|
3013
|
-
else if (alignment === "right" || alignment === "bottom") fraction = 1;
|
|
3014
|
-
else if (alignment === "inside") fraction = pageNumber % 2 === 1 ? 0 : 1;
|
|
3015
|
-
else if (alignment === "outside") fraction = pageNumber % 2 === 1 ? 1 : 0;
|
|
3016
|
-
return reference.start + (reference.size - objectSize) * fraction;
|
|
3017
|
-
}
|
|
3018
|
-
/** Charge a physical page before any page-owned partitioning or DOM allocation. */
|
|
3019
|
-
admitPageAllocation() {
|
|
3020
|
-
const prospectivePageCount = this.createdPageCount + 1;
|
|
3021
|
-
this.pageCountCheckpoint?.(prospectivePageCount);
|
|
3022
|
-
this.createdPageCount = prospectivePageCount;
|
|
3023
|
-
}
|
|
3024
|
-
/**
|
|
3025
|
-
* Creates a page container element.
|
|
3026
|
-
*/
|
|
3027
|
-
createPage(dims, pageNumber, sectionIndex, displayedPageNumber, content, pageInSection, footnoteIds = [], footnoteHeight = 0, continuation, partialFootnotes, isSectionFiller = false, pageAlreadyAdmitted = false) {
|
|
3028
|
-
if (!pageAlreadyAdmitted) this.admitPageAllocation();
|
|
3029
|
-
const pageBox = this.document.createElement("div");
|
|
3030
|
-
pageBox.className = `${this.cssPrefix}box`;
|
|
3031
|
-
pageBox.style.width = `${dims.pageWidth}pt`;
|
|
3032
|
-
pageBox.style.height = `${dims.pageHeight}pt`;
|
|
3033
|
-
pageBox.style.overflow = "hidden";
|
|
3034
|
-
pageBox.style.position = "relative";
|
|
3035
|
-
if (this.scale !== 1) {
|
|
3036
|
-
if (this.view.CSS?.supports("zoom", "1")) {
|
|
3037
|
-
pageBox.style.zoom = String(this.scale);
|
|
3038
|
-
} else {
|
|
3039
|
-
pageBox.style.transform = `scale(${this.scale})`;
|
|
3040
|
-
pageBox.style.transformOrigin = "top left";
|
|
3041
|
-
const heightReductionPt = dims.pageHeight * (1 - this.scale);
|
|
3042
|
-
const widthReductionPt = dims.pageWidth * (1 - this.scale);
|
|
3043
|
-
const heightReductionPx = ptToPx(heightReductionPt);
|
|
3044
|
-
const widthReductionPx = ptToPx(widthReductionPt);
|
|
3045
|
-
pageBox.style.marginRight = `-${widthReductionPx}px`;
|
|
3046
|
-
pageBox.style.marginBottom = `${this.pageGap - heightReductionPx}px`;
|
|
3047
|
-
}
|
|
3048
|
-
}
|
|
3049
|
-
pageBox.style.willChange = "transform";
|
|
3050
|
-
pageBox.style.contain = "layout paint";
|
|
3051
|
-
pageBox.dataset.pageNumber = String(pageNumber);
|
|
3052
|
-
pageBox.dataset.sectionIndex = String(sectionIndex);
|
|
3053
|
-
pageBox.dataset.displayedPageNumber = String(displayedPageNumber);
|
|
3054
|
-
if (isSectionFiller) pageBox.dataset.sectionFiller = "true";
|
|
3055
|
-
pageBox.dataset.pageInSection = String(pageInSection);
|
|
3056
|
-
const bands = this.getPageBands(dims, sectionIndex, pageInSection, displayedPageNumber);
|
|
3057
|
-
const headerSource = isSectionFiller ? void 0 : this.selectHeader(sectionIndex, pageInSection, displayedPageNumber);
|
|
3058
|
-
if (headerSource) {
|
|
3059
|
-
const headerDiv = this.document.createElement("div");
|
|
3060
|
-
headerDiv.className = `${this.cssPrefix}header`;
|
|
3061
|
-
headerDiv.dataset.hfType = this.storyKindFor(
|
|
3062
|
-
"header",
|
|
3063
|
-
sectionIndex,
|
|
3064
|
-
pageInSection,
|
|
3065
|
-
displayedPageNumber
|
|
3066
|
-
);
|
|
3067
|
-
headerDiv.style.position = "absolute";
|
|
3068
|
-
headerDiv.style.top = `${bands.headerTop}pt`;
|
|
3069
|
-
headerDiv.style.bottom = "auto";
|
|
3070
|
-
headerDiv.style.left = `${dims.marginLeft}pt`;
|
|
3071
|
-
headerDiv.style.width = `${dims.contentWidth}pt`;
|
|
3072
|
-
headerDiv.style.height = `${bands.headerHeight}pt`;
|
|
3073
|
-
headerDiv.style.overflow = "hidden";
|
|
3074
|
-
headerDiv.style.boxSizing = "border-box";
|
|
3075
|
-
headerDiv.style.display = "flex";
|
|
3076
|
-
headerDiv.style.flexDirection = "column";
|
|
3077
|
-
headerDiv.style.justifyContent = "flex-start";
|
|
3078
|
-
for (const child of Array.from(headerSource.childNodes)) {
|
|
3079
|
-
const clonedheaderDiv = child.cloneNode(true);
|
|
3080
|
-
if (clonedheaderDiv.nodeType === 1) this.makeClonedStoryInert(clonedheaderDiv);
|
|
3081
|
-
headerDiv.appendChild(clonedheaderDiv);
|
|
3082
|
-
}
|
|
3083
|
-
pageBox.appendChild(headerDiv);
|
|
3084
|
-
}
|
|
3085
|
-
const contentAreaTop = bands.bodyTop;
|
|
3086
|
-
const contentAreaHeight = bands.bodyHeight;
|
|
3087
|
-
const contentArea = this.document.createElement("div");
|
|
3088
|
-
contentArea.className = `${this.cssPrefix}content`;
|
|
3089
|
-
contentArea.style.position = "absolute";
|
|
3090
|
-
contentArea.style.top = `${contentAreaTop}pt`;
|
|
3091
|
-
contentArea.style.left = `${dims.marginLeft}pt`;
|
|
3092
|
-
contentArea.style.width = `${dims.contentWidth}pt`;
|
|
3093
|
-
contentArea.style.height = `${contentAreaHeight}pt`;
|
|
3094
|
-
contentArea.style.overflowX = "visible";
|
|
3095
|
-
contentArea.style.overflowY = "clip";
|
|
3096
|
-
for (const el of content) {
|
|
3097
|
-
contentArea.appendChild(el);
|
|
3098
|
-
}
|
|
3099
|
-
pageBox.appendChild(contentArea);
|
|
3100
|
-
const hasContinuation = continuation && continuation.remainingElements.length > 0;
|
|
3101
|
-
if (!isSectionFiller && (footnoteIds.length > 0 || hasContinuation)) {
|
|
3102
|
-
this.addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes);
|
|
3103
|
-
}
|
|
3104
|
-
const pageCommentIds = [];
|
|
3105
|
-
for (const marker of Array.from(
|
|
3106
|
-
pageBox.querySelectorAll("[data-comment-id]")
|
|
3107
|
-
)) {
|
|
3108
|
-
if (marker.closest(`.${this.cssPrefix}comment-margin`)) continue;
|
|
3109
|
-
const id = marker.dataset.commentId;
|
|
3110
|
-
if (id && this.commentMarginRegistry.has(id) && !pageCommentIds.includes(id)) {
|
|
3111
|
-
pageCommentIds.push(id);
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
if (pageCommentIds.length > 0) {
|
|
3115
|
-
const marginColumn = this.document.createElement("aside");
|
|
3116
|
-
marginColumn.className = `${this.cssPrefix}comment-margin`;
|
|
3117
|
-
marginColumn.style.position = "absolute";
|
|
3118
|
-
marginColumn.style.top = `${contentAreaTop}pt`;
|
|
3119
|
-
marginColumn.style.left = `${dims.marginLeft + dims.contentWidth + 3}pt`;
|
|
3120
|
-
marginColumn.style.width = `${Math.max(12, dims.marginRight - 6)}pt`;
|
|
3121
|
-
marginColumn.style.maxHeight = `${contentAreaHeight}pt`;
|
|
3122
|
-
marginColumn.style.overflow = "hidden";
|
|
3123
|
-
marginColumn.style.boxSizing = "border-box";
|
|
3124
|
-
const placedThreadRoots = /* @__PURE__ */ new Set();
|
|
3125
|
-
for (const id of pageCommentIds) {
|
|
3126
|
-
const source = this.commentMarginRegistry.get(id);
|
|
3127
|
-
if (source) {
|
|
3128
|
-
const rootId = source.dataset.commentId ?? id;
|
|
3129
|
-
if (placedThreadRoots.has(rootId)) continue;
|
|
3130
|
-
placedThreadRoots.add(rootId);
|
|
3131
|
-
const clone = source.cloneNode(true);
|
|
3132
|
-
this.makeClonedMarginCommentInert(clone);
|
|
3133
|
-
marginColumn.appendChild(clone);
|
|
3134
|
-
}
|
|
3135
|
-
}
|
|
3136
|
-
pageBox.appendChild(marginColumn);
|
|
3137
|
-
}
|
|
3138
|
-
const footerSource = isSectionFiller ? void 0 : this.selectFooter(sectionIndex, pageInSection, displayedPageNumber);
|
|
3139
|
-
if (footerSource) {
|
|
3140
|
-
const footerDiv = this.document.createElement("div");
|
|
3141
|
-
footerDiv.className = `${this.cssPrefix}footer`;
|
|
3142
|
-
footerDiv.dataset.hfType = this.storyKindFor(
|
|
3143
|
-
"footer",
|
|
3144
|
-
sectionIndex,
|
|
3145
|
-
pageInSection,
|
|
3146
|
-
displayedPageNumber
|
|
3147
|
-
);
|
|
3148
|
-
footerDiv.style.position = "absolute";
|
|
3149
|
-
footerDiv.style.top = "auto";
|
|
3150
|
-
footerDiv.style.bottom = `${dims.footerDistance}pt`;
|
|
3151
|
-
footerDiv.style.left = `${dims.marginLeft}pt`;
|
|
3152
|
-
footerDiv.style.width = `${dims.contentWidth}pt`;
|
|
3153
|
-
footerDiv.style.height = `${bands.footerHeight}pt`;
|
|
3154
|
-
footerDiv.style.overflow = "hidden";
|
|
3155
|
-
footerDiv.style.boxSizing = "border-box";
|
|
3156
|
-
footerDiv.style.display = "flex";
|
|
3157
|
-
footerDiv.style.flexDirection = "column";
|
|
3158
|
-
footerDiv.style.justifyContent = "flex-end";
|
|
3159
|
-
for (const child of Array.from(footerSource.childNodes)) {
|
|
3160
|
-
const clonedfooterDiv = child.cloneNode(true);
|
|
3161
|
-
if (clonedfooterDiv.nodeType === 1) this.makeClonedStoryInert(clonedfooterDiv);
|
|
3162
|
-
footerDiv.appendChild(clonedfooterDiv);
|
|
3163
|
-
}
|
|
3164
|
-
pageBox.appendChild(footerDiv);
|
|
3165
|
-
}
|
|
3166
|
-
if (this.showPageNumbers && !isSectionFiller) {
|
|
3167
|
-
const pageNum = this.document.createElement("div");
|
|
3168
|
-
pageNum.className = `${this.cssPrefix}number`;
|
|
3169
|
-
pageNum.textContent = String(pageNumber);
|
|
3170
|
-
pageBox.appendChild(pageNum);
|
|
3171
|
-
}
|
|
3172
|
-
this.containerElement.appendChild(pageBox);
|
|
3173
|
-
this.positionDrawingAnchors(pageBox, contentArea, dims, pageNumber);
|
|
3174
|
-
const notesEl = pageBox.querySelector(`.${this.cssPrefix}footnotes`);
|
|
3175
|
-
if (notesEl) {
|
|
3176
|
-
const notesHeightPt = pxToPt(notesEl.getBoundingClientRect().height);
|
|
3177
|
-
if (notesHeightPt > 0) {
|
|
3178
|
-
contentArea.style.height = `${Math.max(0, contentAreaHeight - notesHeightPt)}pt`;
|
|
3179
|
-
}
|
|
3180
|
-
}
|
|
3181
|
-
return {
|
|
3182
|
-
pageNumber,
|
|
3183
|
-
sectionIndex,
|
|
3184
|
-
dimensions: dims,
|
|
3185
|
-
element: pageBox
|
|
3186
|
-
};
|
|
3187
|
-
}
|
|
3188
|
-
};
|
|
3189
|
-
function paginateHtml(html, container, options = {}) {
|
|
3190
|
-
const ownerDocument = typeof container === "string" ? globalThis.document : container.ownerDocument;
|
|
3191
|
-
const containerEl = typeof container === "string" ? ownerDocument.getElementById(container) : container;
|
|
3192
|
-
if (!containerEl) {
|
|
3193
|
-
throw new Error("Container element not found");
|
|
3194
|
-
}
|
|
3195
|
-
containerEl.innerHTML = html;
|
|
3196
|
-
const cssPrefix = options.cssPrefix ?? "page-";
|
|
3197
|
-
const staging = containerEl.querySelector("#pagination-staging") || containerEl.querySelector(`.${cssPrefix}staging`);
|
|
3198
|
-
const pageContainer = containerEl.querySelector("#pagination-container") || containerEl.querySelector(`.${cssPrefix}container`);
|
|
3199
|
-
if (!staging) {
|
|
3200
|
-
throw new Error(
|
|
3201
|
-
"Pagination staging element not found. Make sure the HTML was generated with PaginationMode.Paginated"
|
|
3202
|
-
);
|
|
3203
|
-
}
|
|
3204
|
-
if (!pageContainer) {
|
|
3205
|
-
throw new Error("Pagination container element not found");
|
|
3206
|
-
}
|
|
3207
|
-
const engine = new PaginationEngine(staging, pageContainer, {
|
|
3208
|
-
...options,
|
|
3209
|
-
fragmentParagraphs: options.fragmentParagraphs ?? true
|
|
3210
|
-
});
|
|
3211
|
-
return engine.paginate();
|
|
3212
|
-
}
|
|
3213
|
-
return __toCommonJS(pagination_exports);
|
|
3214
|
-
})();
|
|
1
|
+
"use strict";var DocxodusPagination=(()=>{var Ee=Object.defineProperty;var ze=Object.getOwnPropertyDescriptor;var Ve=Object.getOwnPropertyNames;var Ue=Object.prototype.hasOwnProperty;var Ge=(h,e)=>{for(var n in e)Ee(h,n,{get:e[n],enumerable:!0})},Xe=(h,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Ve(e))!Ue.call(h,o)&&o!==n&&Ee(h,o,{get:()=>e[o],enumerable:!(t=ze(e,o))||t.enumerable});return h};var je=h=>Xe(Ee({},"__esModule",{value:!0}),h);var dt={};Ge(dt,{DRAWING_HOST_ATTRIBUTE:()=>ue,HOSTS_PROMOTED_DRAWING_ATTRIBUTE:()=>ge,PaginationEngine:()=>me,clearPageCitationHighlight:()=>Oe,collapsedBlockMarginsPx:()=>Te,createUnavailablePageMap:()=>it,measureRenderedSource:()=>He,navigateToPageCitation:()=>rt,paginateHtml:()=>ct,renderedSourceMeasurerExpression:()=>lt});var Ke=["","i","ii","iii","iv","v","vi","vii","viii","ix"],Ze=["","x","xx","xxx","xl","l","lx","lxx","lxxx","xc"],Ye=["","c","cc","ccc","cd","d","dc","dcc","dccc","cm"],Je=["","m","mm","mmm"];function le(h){return h<=0||h>=4e3?String(h):Je[Math.floor(h/1e3)]+Ye[Math.floor(h%1e3/100)]+Ze[Math.floor(h%100/10)]+Ke[h%10]}function ce(h){if(h<=0)return String(h);let e=h%780===0?780:h%780,n=Math.floor((e-1)/26)+1;return"abcdefghijklmnopqrstuvwxyz".charAt((e-1)%26).repeat(n)}var Se={decimal:h=>String(h),lowerRoman:le,upperRoman:h=>le(h).toUpperCase(),lowerLetter:ce,upperLetter:h=>ce(h).toUpperCase(),Arabic:h=>String(h),roman:le,ROMAN:h=>le(h).toUpperCase(),alphabetic:ce,ALPHABETIC:h=>ce(h).toUpperCase()};function Pe(h,e){return((e?Se[e]:void 0)??Se.decimal)(h)}var Le="p, h1, h2, h3, h4, h5, h6";function Qe(h){let e=new Map,n=h.body??h.documentElement;return(t,o)=>{let r=[t.fontStyle,t.fontWeight,t.fontStretch,t.fontFamily,t.fontSize,t.lineHeight,o.fontStyle,o.fontWeight,o.fontStretch,o.fontFamily,o.fontSize,o.lineHeight,o.verticalAlign,o.top].join("|"),i=e.get(r);if(i===void 0){let c=h.createElement("div");c.setAttribute("aria-hidden","true"),c.style.cssText="position:absolute;left:-100000px;top:0;visibility:hidden;width:max-content;margin:0;padding:0;border:0;white-space:nowrap;";for(let s of["font-family","font-style","font-weight","font-stretch","font-size","line-height"])c.style.setProperty(s,t.getPropertyValue(s));let l=h.createElement("span");for(let s of["font-family","font-style","font-weight","font-stretch","font-size","line-height","vertical-align","position","top"])l.style.setProperty(s,o.getPropertyValue(s));let a=h.createElement("span");a.style.cssText="display:inline-block;width:0;height:0;vertical-align:baseline;margin:0;padding:0;border:0;",l.append(a,"Hg"),c.append(l),n.appendChild(c),i=a.getBoundingClientRect().bottom-c.getBoundingClientRect().top,c.remove(),e.set(r,i)}return i}}function we(h){let e=h.ownerDocument.defaultView;if(!e)return;let n=Qe(h.ownerDocument);for(let t of Array.from(h.querySelectorAll("[data-docx-exact-run]"))){let o=t.closest(Le);if(!o||!tt(t,e))continue;let r=e.getComputedStyle(o);if(!r.getPropertyValue("--docx-exact-line-height"))continue;let i=Number.parseFloat(r.lineHeight),c=e.getComputedStyle(t);if(!(i>0)||c.display!=="inline"||c.verticalAlign!=="top")continue;let l=Number.parseFloat(c.top)||0,a=n(r,c)-l,s=i*.8-a;Number.isFinite(s)&&(t.style.position="relative",t.style.top=`${(l+s).toFixed(4)}px`)}}var et="data-docx-exact-ink";function Be(h,e,n,t){let o=h.ownerDocument.defaultView;if(!o)return;let r=h.getBoundingClientRect(),i=r.width/h.offsetWidth||1,c=0,l=0;for(let a of Array.from(h.querySelectorAll(Le))){if(!o.getComputedStyle(a).getPropertyValue("--docx-exact-line-height"))continue;let s=a.getBoundingClientRect();if(s.top<r.top-.5||s.bottom>r.bottom+.5||!a.querySelector("[data-docx-exact-run]")||a.querySelector("img, svg, canvas, video, object, iframe")||Array.from(a.querySelectorAll("*")).some(p=>{let y=o.getComputedStyle(p);return y.display!=="inline"||!["static","relative"].includes(y.position)}))continue;let d=h.ownerDocument.createRange();d.selectNodeContents(a);let g=Array.from(d.getClientRects()).filter(p=>p.width>0&&p.height>0);if(g.length===0)continue;let m=Math.min(...g.map(p=>p.top)),u=Math.max(...g.map(p=>p.bottom));m<e||u>n||Math.max(0,r.top-m,u-r.bottom)===0||(a.setAttribute(et,"true"),c=Math.max(c,r.top-m),l=Math.max(l,u-r.bottom))}if(c>0||l>0){let a=Math.ceil(c/i*64)/64,s=Math.ceil(l/i*64)/64;h.style.overflowY="visible",h.style.clipPath=`inset(${-a}px ${(r.right-t.right)/i}px ${-s}px ${(t.left-r.left)/i}px)`}}function Me(h,e,n){let t=/^inset\(((?:-?\d+(?:\.\d+)?px\s*){1,4})\)$/.exec(h);if(!t)return null;let o=t[1].trim().split(/\s+/).map(Number.parseFloat),[r,i=r,c=r,l=i]=o;return{top:e.top+r*n,right:e.right-i*n,bottom:e.bottom-c*n,left:e.left+l*n}}function tt(h,e){let n=e.getComputedStyle(h).position;if(n!=="static"&&n!=="relative"||!["inline","inline-block"].includes(e.getComputedStyle(h).display)||h.querySelector("img, svg"))return!1;for(let t of Array.from(h.querySelectorAll("*")))if(["absolute","fixed"].includes(e.getComputedStyle(t).position))return!1;return!0}function I(h){return h*.75}function xe(h){return h/.75}function ee(h,e){if(h===void 0||h.trim()==="")return e;let n=Number(h);return Number.isFinite(n)?n:e}function de(h,e){let n=ee(h,e);return n>0?n:e}function Ie(h,e){let n=ee(h,e);return n>=0?n:e}function ke(h,e){return Number.isFinite(h)&&h>0?h:e}function Ne(h){let e=de(h.dataset.pageWidth,612),n=de(h.dataset.pageHeight,792),t=ee(h.dataset.marginTop,72),o=ee(h.dataset.marginRight,72),r=ee(h.dataset.marginBottom,72),i=ee(h.dataset.marginLeft,72),c=ke(e-i-o,e),l=ke(n-t-r,n),a=de(h.dataset.contentWidth,c),s=de(h.dataset.contentHeight,l),d=Ie(h.dataset.headerHeight,36),g=Ie(h.dataset.footerHeight,36);return{pageWidth:e,pageHeight:n,contentWidth:a,contentHeight:s,marginTop:t,marginRight:o,marginBottom:r,marginLeft:i,headerDistance:d,footerDistance:g,headerHeight:d,footerHeight:g}}var nt=12;function Re(h,e,n){let t=h.headerDistance,o=h.pageHeight-h.footerDistance,r=e>0?t+e:0,i=n>0?o-n:h.pageHeight,c=Math.max(h.marginTop,r),l=Math.min(h.pageHeight-h.marginBottom,i),a=nt-(l-c);if(a>0){let s=c-h.marginTop,d=h.pageHeight-h.marginBottom-l,g=s+d;g>0&&(c-=a*s/g,l+=a*d/g)}return{headerTop:t,headerHeight:Math.max(0,Math.min(e,c-t)),bodyTop:c,bodyHeight:Math.max(0,l-c),footerTop:o-Math.max(0,Math.min(n,o-l)),footerHeight:Math.max(0,Math.min(n,o-l))}}function ot(h,e){return h.pageWidth===e.pageWidth&&h.pageHeight===e.pageHeight&&h.marginTop===e.marginTop&&h.marginRight===e.marginRight&&h.marginBottom===e.marginBottom&&h.marginLeft===e.marginLeft}function it(h,e,n="continuous"){if(!e)throw new Error("rendererFingerprint must be non-empty");return{schemaVersion:1,mode:n,availability:"unavailable",documentVersion:h,rendererFingerprint:e,pages:[],fragments:[]}}var he=new WeakMap;function Oe(h){let e=he.get(h);if(e){e.highlightClass&&e.addedClass&&e.target.classList.remove(e.highlightClass);for(let n of e.inlineStyles??[])n.value?e.target.style.setProperty(n.name,n.value,n.priority):e.target.style.removeProperty(n.name);he.delete(h)}}function rt(h,e,n={}){if(Oe(h),e.availability!=="available"||e.fragments.length===0)return{navigated:!1,unavailableReason:"citation_unavailable"};let t=(i,c,l=h)=>{for(let a of Array.from(l.querySelectorAll(`[${i}]`)))if(a.getAttribute(i)===c)return a;return null},o=e.fragments[0],r=t("data-page-fragment-id",o.fragmentId);if(!r){let i=t("data-page-number",String(o.pageNumber));i&&(r=t("data-source-anchor-id",e.anchorId,i)??i)}if(!r)return{navigated:!1,pageNumber:o.pageNumber,fragmentId:o.fragmentId,unavailableReason:"fragment_not_found"};if(n.highlightClass){let i=!r.classList.contains(n.highlightClass);r.classList.add(n.highlightClass),he.set(h,{target:r,highlightClass:n.highlightClass,addedClass:i})}else if(n.highlight!==!1){let c=["outline","outline-offset","background-color"].map(l=>({name:l,value:r.style.getPropertyValue(l),priority:r.style.getPropertyPriority(l)}));r.style.setProperty("outline","3px solid #f4b400","important"),r.style.setProperty("outline-offset","2px","important"),r.style.setProperty("background-color","rgba(255, 235, 59, .18)","important"),he.set(h,{target:r,inlineStyles:c})}return r.scrollIntoView({behavior:n.behavior??"smooth",block:n.block??"center"}),{navigated:!0,target:r,pageNumber:o.pageNumber,fragmentId:o.fragmentId}}var te=.6,re=2,G="data-pagination-footnote-source-position";function Te(h,e){return{topPx:$e(h,e,"top"),bottomPx:$e(h,e,"bottom")}}function $e(h,e,n){let t=[],o=e;for(;o;){let c=h.getComputedStyle(o);t.push(parseFloat(n==="top"?c.marginTop:c.marginBottom)||0),o=at(h,o,c,n)}let r=Math.max(0,...t),i=Math.min(0,...t);return r+i}function at(h,e,n,t){if(!((n.display==="block"||n.display==="list-item")&&n.overflowX==="visible"&&n.overflowY==="visible"&&n.float==="none"&&n.position!=="absolute"&&n.position!=="fixed")||n.columnCount!=="auto"||n.columnWidth!=="auto")return null;let r=parseFloat(t==="top"?n.borderTopWidth:n.borderBottomWidth)||0,i=parseFloat(t==="top"?n.paddingTop:n.paddingBottom)||0;if(r!==0||i!==0||t==="bottom"&&e.style.height!=="")return null;let c=Array.from(e.childNodes);t==="bottom"&&c.reverse();for(let l of c){if(l.nodeType===Node.TEXT_NODE){if((l.textContent??"").trim()!=="")return null;continue}if(l.nodeType!==Node.ELEMENT_NODE)continue;let a=l,s=h.getComputedStyle(a);if(s.display==="none"||s.position==="absolute"||s.position==="fixed"||s.float!=="none")continue;return s.display==="block"||s.display==="list-item"||s.display==="table"||s.display==="flow-root"||s.display==="flex"||s.display==="grid"?a:null}return null}var ue="data-docx-anchor-host",ge="data-docx-hosts-promoted-drawing";function st(h){return(h.textContent??"").replace(/[\s\u00a0]/g,"")===""&&h.querySelector("img, svg, canvas, [data-docx-drawing-anchor]")===null}function _e(h,e,n){function t(o,r,i,c,l){let a=Math.max(l.left,c.left),s=Math.max(l.top,c.top),d=Math.min(l.right,c.right),g=Math.min(l.bottom,c.bottom),m=u=>u==="hidden"||u==="clip"||u==="scroll"||u==="auto";for(let u=r.parentElement;u&&u!==i;u=u.parentElement){let b=o.getComputedStyle(u),p=m(b.overflowX),y=m(b.overflowY);if(!p&&!y&&!b.clipPath.startsWith("inset("))continue;let f=u.getBoundingClientRect(),x=f.width/u.offsetWidth||1,M=h(b.clipPath,f,x);M&&(a=Math.max(a,M.left),s=Math.max(s,M.top),d=Math.min(d,M.right),g=Math.min(g,M.bottom)),p&&(a=Math.max(a,f.left),d=Math.min(d,f.right)),y&&(s=Math.max(s,f.top),g=Math.min(g,f.bottom))}return{left:a,top:s,right:d,bottom:g}}return function(r,i,c,l=c.getBoundingClientRect()){let a=i.getBoundingClientRect(),s=()=>({rect:a,visible:t(r,i,c,l,a)}),d=a.width>0&&a.height>0,g=i.hasAttribute(n);if(d&&!g)return s();let m=i.dataset.sourceAnchorId,u=m?Array.from(c.querySelectorAll(`[${e}]`)).filter(M=>M.getAttribute(e)===m):[];if(d&&u.length===0)return s();let b=[...d?[i]:Array.from(i.querySelectorAll("*")),...u.flatMap(M=>[M,...Array.from(M.querySelectorAll("*"))])],p=1/0,y=1/0,f=-1/0,x=-1/0;for(let M of b){let F=r.getComputedStyle(M);if(F.display==="none"||F.visibility==="hidden")continue;let H=M.getBoundingClientRect();if(H.width<=0||H.height<=0)continue;let T=t(r,M,c,l,H);T.right<=T.left||T.bottom<=T.top||(p=Math.min(p,T.left),y=Math.min(y,T.top),f=Math.max(f,T.right),x=Math.max(x,T.bottom))}return p<f&&y<x?{rect:new DOMRect(p,y,f-p,x-y),visible:{left:p,top:y,right:f,bottom:x}}:s()}}var He=_e(Me,ue,ge);function lt(){return`(${_e.toString()})(${Me.toString()},
|
|
2
|
+
${JSON.stringify(ue)}, ${JSON.stringify(ge)})`}var me=class{constructor(e,n,t={}){this.createdPageCount=0;this.footnoteSeparator=null;this.footnoteContinuationSeparator=null;this.footnoteLayoutLikeWord8=!1;this.pendingFootnoteContinuation=null;this.pageNumbering=new Map;this.lastPages=[];this.expectedPageMapAnchorIds=new Set;this.state="ready";let o=typeof e!="string"?e.ownerDocument:typeof n!="string"?n.ownerDocument:globalThis.document;if(this.stagingElement=typeof e=="string"?o.getElementById(e):e,this.containerElement=typeof n=="string"?o.getElementById(n):n,!this.stagingElement)throw new Error("Staging element not found");if(!this.containerElement)throw new Error("Container element not found");if(this.stagingElement.ownerDocument!==this.containerElement.ownerDocument)throw new Error("Staging and container elements must belong to the same document");let r=o.defaultView;if(!r)throw new Error("Pagination requires an attached document with a defaultView");this.document=o,this.view=r,this.scale=t.scale??1,this.cssPrefix=t.cssPrefix??"page-",this.showPageNumbers=t.showPageNumbers??!0,this.pageGap=t.pageGap??20,this.fragmentParagraphs=t.fragmentParagraphs??!1,this.deferFragmentIdentities=t.deferFragmentIdentities??!1,this.bodyBudgetReductionsPt=t.bodyBudgetReductionsPt??{},this.cancellationCheckpoint=t.checkCancellation,this.pageCountCheckpoint=t.checkPageCount,this.layoutToken=t.layoutToken,this.hfRegistry=new Map,this.footnoteRegistry=new Map,this.commentMarginRegistry=new Map}paginate(){if(this.checkpoint(),this.state!=="ready")throw new Error(`PaginationEngine is one-shot and is already ${this.state}`);if(!this.stagingElement.isConnected||!this.containerElement.isConnected)throw new Error("Pagination requires staging and container elements attached to their document");if(this.document.documentElement.getBoundingClientRect().width<=0)throw new Error("Pagination requires a browsing context with non-zero layout");this.state="running";try{let e=[],n=1;this.hfRegistry=this.parseHeaderFooterRegistry(),this.footnoteRegistry=this.parseFootnoteRegistry(),{normal:this.footnoteSeparator,continuation:this.footnoteContinuationSeparator}=this.parseFootnoteSeparators(),this.commentMarginRegistry=this.parseCommentMarginRegistry(),this.footnoteLayoutLikeWord8=this.stagingElement.dataset.footnoteLayoutLikeWord8==="true";let t=this.stagingElement.querySelectorAll("[data-section-index]");this.pageNumbering=this.parsePageNumbering(t);let o=t.length>0?Array.from(t):[this.stagingElement];this.expectedPageMapAnchorIds=new Set;let r=new Set,i=new Set;for(let a of o){this.checkpoint(),this.collectExpectedSourceAnchors(a,this.expectedPageMapAnchorIds,!0);for(let s of Array.from(a.querySelectorAll("[data-footnote-id]"))){if(this.checkpoint(),s.closest("#pagination-footnote-registry, #pagination-hf-registry"))continue;let d=s.dataset.footnoteId;d&&r.add(d)}for(let s of Array.from(a.querySelectorAll("[data-comment-id]"))){if(this.checkpoint(),s.closest("#pagination-comment-margin-registry, #pagination-footnote-registry, #pagination-hf-registry"))continue;let d=s.dataset.commentId;d&&i.add(d)}}for(let a of r){this.checkpoint();let s=this.footnoteRegistry.get(a);if(s){this.collectExpectedSourceAnchors(s,this.expectedPageMapAnchorIds);for(let d of Array.from(s.querySelectorAll("[data-comment-id]"))){this.checkpoint();let g=d.dataset.commentId;g&&i.add(g)}}}for(let a of i){this.checkpoint();let s=this.commentMarginRegistry.get(a);s&&this.collectExpectedSourceAnchors(s,this.expectedPageMapAnchorIds)}let c=[];for(let a of o){this.checkpoint();let s=Ne(a),d=c[c.length-1];if(d&&a.dataset.sectionType==="continuous"&&ot(d.dims,s))d.sections.push(a),d.sectionDimensions.set(parseInt(a.dataset.sectionIndex||"0",10),s);else{let g=parseInt(a.dataset.sectionIndex||"0",10);c.push({sections:[a],sectionIndex:g,sectionType:a.dataset.sectionType??"nextPage",dims:s,sectionDimensions:new Map([[g,s]])})}}for(let a of c){if(this.checkpoint(),e.length>0&&(a.sectionType==="oddPage"&&n%2===0||a.sectionType==="evenPage"&&n%2===1)){let m=e[e.length-1],u=parseInt(m.element.dataset.pageInSection??"1",10),b=parseInt(m.element.dataset.displayedPageNumber??String(m.pageNumber),10),p=this.createPage(m.dimensions,n,m.sectionIndex,b+1,[],u+1,[],0,null,void 0,!0);e.push(p),n++}this.stagingElement.style.visibility="hidden",this.stagingElement.style.position="absolute",this.stagingElement.style.left="-9999px",this.stagingElement.style.display="block";let d=[];for(let m of a.sections){this.checkpoint();let u=parseInt(m.dataset.sectionIndex||"0",10),b=a.sectionDimensions.get(u)??a.dims;m.style.width=`${b.contentWidth}pt`;let p=parseInt(m.dataset.cols||"1",10);if(p>1){let y=parseFloat(m.dataset.colGap||"");d.push(...this.buildColumnBlocks(m,b,u,p,Number.isFinite(y)?y:36,m.dataset.sectionType==="continuous"&&m===o[o.length-1]))}else d.push(...this.measureBlocks(m,b,u))}let g=this.flowToPages(d,a.dims,n,a.sectionIndex,a.sectionDimensions);this.checkpoint(),e.push(...g),n+=g.length}this.stagingElement.style.display="none",this.substitutePageNumberFields(e.length);for(let a of e){if(this.checkpoint(),a.element.dataset.sectionFiller==="true")continue;let s=parseInt(a.element.dataset.pageInSection||"1",10),d=parseInt(a.element.dataset.displayedPageNumber||String(a.pageNumber),10),g=this.selectHeader(a.sectionIndex,s,d),m=this.selectFooter(a.sectionIndex,s,d);g&&this.collectExpectedSourceAnchors(g,this.expectedPageMapAnchorIds),m&&this.collectExpectedSourceAnchors(m,this.expectedPageMapAnchorIds)}this.qualifyPageFragments(e),this.transferVisibleFragmentTargets(),this.deferFragmentIdentities||this.normalizeVisiblePageFragments(e),this.lastPages=e;let l={totalPages:e.length,pages:e,pageMap:this.layoutToken?this.materializePageMap(this.layoutToken.documentVersion,this.layoutToken.rendererFingerprint):void 0};return this.state="complete",l}catch(e){throw this.state="failed",e}}checkpoint(){this.cancellationCheckpoint?.()}normalizePageMapFragmentIdentities(){if(this.lastPages.length===0)throw new Error("paginate() must complete before fragment identities can be normalized");this.normalizeVisiblePageFragments(this.lastPages)}materializePageMap(e,n){if(!Number.isSafeInteger(e)||e<0)throw new Error("documentVersion must be a non-negative safe integer");if(!n)throw new Error("rendererFingerprint must be non-empty");if(this.lastPages.length===0)throw new Error("paginate() must complete before materializePageMap()");let t=this.lastPages.map(a=>({pageNumber:a.pageNumber,pageInSection:parseInt(a.element.dataset.pageInSection||"1",10),width:a.dimensions.pageWidth,height:a.dimensions.pageHeight,sectionIndex:a.sectionIndex,pageName:`docxodus-section-${a.sectionIndex}`})),o=[],r=new Set(this.expectedPageMapAnchorIds);if(r.size===0)throw new Error("cannot publish an available PageMap without canonical source inventory");let i=new Set,c=new Map;for(let a of this.lastPages){this.checkpoint();let s=a.element.getBoundingClientRect();if(s.width<=0||s.height<=0)throw new Error(`page ${a.pageNumber} has no measurable geometry`);let d=a.dimensions.pageWidth/s.width,g=a.dimensions.pageHeight/s.height,m=a.element.querySelectorAll("[data-source-anchor-id]");for(let u of Array.from(m)){if(this.checkpoint(),this.isDeliberatelyUnrenderedSource(u,a.element))continue;let b=u.dataset.sourceAnchorId;if(!b||!u.dataset.pageFragmentId||!Number.isInteger(parseInt(u.dataset.fragmentIndex||"",10)))throw new Error(`page ${a.pageNumber} contains an unqualified source anchor`);let p=this.view.getComputedStyle(u);if(p.display==="none"||p.visibility==="hidden")continue;let{rect:f,visible:x}=He(this.view,u,a.element,s);r.add(b);let M=x.left,F=x.top,H=x.right,T=x.bottom;if(f.width<=0||f.height<=0||H<=M||T<=F)continue;i.add(b);let S=c.get(b)??0;c.set(b,S+1);let $=`p${a.pageNumber}-f${S}-${b}`;if(u.dataset.fragmentIndex!==String(S)||u.dataset.pageFragmentId!==$||u.dataset.pageNumber!==String(a.pageNumber))throw new Error(`page ${a.pageNumber} fragment identity changed after final-tree normalization`);o.push({fragmentId:$,anchorId:b,fragmentIndex:S,pageNumber:a.pageNumber,geometry:{x:(M-s.left)*d,y:(F-s.top)*g,width:(H-M)*d,height:(T-F)*g},story:this.storyForCanonicalAnchor(b),inTableCell:u.matches("td,th")||u.closest("td,th")!==null})}}let l=Array.from(r).find(a=>!i.has(a));if(l)throw new Error(`source anchor ${l} has no measurable fragment in the paginated layout`);return{schemaVersion:1,mode:"paginated",availability:"available",documentVersion:e,rendererFingerprint:n,pages:t,fragments:o}}normalizeVisiblePageFragments(e){let n=new Map;for(let t of e){this.checkpoint();let o=t.element.getBoundingClientRect();for(let r of Array.from(t.element.querySelectorAll("[data-source-anchor-id]"))){if(this.checkpoint(),this.isDeliberatelyUnrenderedSource(r,t.element))continue;let i=r.dataset.sourceAnchorId;if(!i)continue;let c=this.view.getComputedStyle(r);if(c.display==="none"||c.visibility==="hidden")continue;let{rect:l,visible:a}=He(this.view,r,t.element,o);if(l.width<=0||l.height<=0||a.right<=a.left||a.bottom<=a.top)continue;let s=n.get(i)??0;n.set(i,s+1),r.dataset.pageNumber=String(t.pageNumber),r.dataset.fragmentIndex=String(s),r.dataset.pageFragmentId=`p${t.pageNumber}-f${s}-${i}`}}}storyForCanonicalAnchor(e){let n=e.indexOf(":"),t=n<0?-1:e.indexOf(":",n+1),o=n>=0&&t>n?e.slice(n+1,t):"body";return o.startsWith("hdr")?"header":o.startsWith("ftr")?"footer":o==="fn"?"footnote":o==="en"?"endnote":o==="cmt"?"comment":"body"}collectExpectedSourceAnchors(e,n,t=!1){let o=e.matches("[data-source-anchor-id]")?[e]:[];o.push(...Array.from(e.querySelectorAll("[data-source-anchor-id]")));for(let r of o){if(t&&r.closest("#pagination-hf-registry, #pagination-footnote-registry, #pagination-comment-margin-registry")||(this.isZeroHeightExplicitBreakCarrier(r)&&(r.dataset.pageMapExclude="true"),this.isDeliberatelyUnrenderedSource(r,e)))continue;let i=r.dataset.sourceAnchorId;i&&n.add(i)}}isZeroHeightExplicitBreakCarrier(e){let n=e.nextElementSibling,t=e.getBoundingClientRect();return e.hasAttribute("data-source-anchor-id")&&(e.textContent??"").replace(/\u00a0/g,"").trim()===""&&t.height<=.01&&!e.querySelector("[data-source-anchor-id]")&&!e.querySelector("img,svg,canvas,table,hr,input,textarea,select")&&(n?.dataset.pageBreak==="true"||n?.classList.contains(`${this.cssPrefix}break`)===!0)}isDeliberatelyUnrenderedSource(e,n){for(let t=e;t;t=t.parentElement){let o=t===e&&this.isZeroHeightExplicitBreakCarrier(t);if(t.dataset.pageMapExclude==="true"||t.dataset.pageBreak==="true"||t.classList.contains(`${this.cssPrefix}break`)||o||t.hidden||t.getAttribute("aria-hidden")==="true"||t.style.display==="none"||t.style.visibility==="hidden")return!0;if(t===n)break}return!1}qualifyPageFragments(e){let n=new Map,t=new Set,o=new Set,r=c=>{c.removeAttribute("data-anchor"),c.removeAttribute("data-committed-text"),c.hasAttribute("contenteditable")&&c.setAttribute("contenteditable","false")};for(let c of e){let l=c.element.querySelectorAll("[data-source-anchor-id]");for(let a of Array.from(l)){let s=a.dataset.sourceAnchorId;if(!s)continue;let d=n.get(s)??0;n.set(s,d+1),a.dataset.pageNumber=String(c.pageNumber),a.dataset.fragmentIndex=String(d),a.dataset.pageFragmentId=`p${c.pageNumber}-f${d}-${s}`;let g=this.storyForCanonicalAnchor(s),m=g==="body"||g==="comment"||g==="footnote"||g==="endnote";if(a.hasAttribute("data-anchor")){let u=a.dataset.anchor;m&&!t.has(s)&&!o.has(u)?(t.add(s),o.add(u)):r(a)}}}let i=new Set;for(let c of Array.from(this.stagingElement.querySelectorAll("[data-source-anchor-id][data-anchor]"))){let l=c.dataset.sourceAnchorId;if(!l)continue;let a=c.dataset.anchor;t.has(l)||i.has(l)||o.has(a)?r(c):(i.add(l),o.add(a))}}transferVisibleFragmentTargets(){let e=new Set(Array.from(this.containerElement.querySelectorAll("[id]")).map(n=>n.id).filter(Boolean));if(e.size!==0)for(let n of Array.from(this.stagingElement.querySelectorAll("[id]")))e.has(n.id)&&n.removeAttribute("id")}parsePageNumbering(e){let n=new Map;for(let t of Array.from(e)){let o=parseInt(t.dataset.sectionIndex||"0",10),r=t.dataset.pageNumStart,i=r===void 0?void 0:parseInt(r,10);n.set(o,{start:i!==void 0&&Number.isFinite(i)?i:void 0,format:t.dataset.pageNumFmt})}return n}substitutePageNumberFields(e){let n=this.containerElement.querySelectorAll(`.${this.cssPrefix}box`);for(let t of Array.from(n)){let o=t.querySelectorAll(`.${this.cssPrefix}header [data-field], .${this.cssPrefix}footer [data-field]`);if(o.length===0)continue;let r=parseInt(t.dataset.sectionIndex||"0",10),i=parseInt(t.dataset.pageNumber||"1",10),c=this.pageNumbering.get(r)??{},l=parseInt(t.dataset.displayedPageNumber||String(i),10);for(let a of Array.from(o)){let s=a.dataset.field;if(s!=="PAGE"&&s!=="NUMPAGES")continue;let d=a.dataset.fieldFormat??c.format;a.textContent=Pe(s==="PAGE"?l:e,d)}}}measureBlocks(e,n,t){let o=[],r=Array.from(e.children);for(let i of r){if(this.checkpoint(),i.dataset.sectionIndex!==void 0){let u=parseInt(i.dataset.sectionIndex||String(t),10),b=this.measureBlocks(i,n,u);o.push(...b);continue}if(i.matches("section.endnotes")){let u=this.measureSafeEndnoteBlocks(i,n,t);if(u){o.push(...u);continue}}let c=i.getBoundingClientRect(),{topPx:l,bottomPx:a}=Te(this.view,i),s=I(c.height),d=I(l),g=I(a),m=i.dataset.pageBreak==="true"||i.classList.contains(`${this.cssPrefix}break`);o.push({element:i,sectionIndex:t,heightPt:s,marginTopPt:d,marginBottomPt:g,keepWithNext:i.dataset.keepWithNext==="true",keepLines:i.dataset.keepLines==="true",pageBreakBefore:i.dataset.pageBreakBefore==="true",isPageBreak:m,isWordParagraph:this.isWordParagraphElement(i)})}return o}measureSafeEndnoteBlocks(e,n,t){let o=Array.from(e.children),r=o.find(s=>s.tagName==="OL");if(!r||o.some(s=>s.tagName!=="HR"&&s!==r)||Array.from(r.children).some(s=>s.tagName!=="LI"))return null;let i=Array.from(r.children);if(i.length===0||i.some(s=>s.children.length===0||Array.from(s.children).some(d=>d.tagName!=="P")))return null;let c=[],l=this.view.getComputedStyle(e);for(let s of o.filter(d=>d.tagName==="HR")){this.checkpoint();let d=s.cloneNode(!0),g=this.view.getComputedStyle(s);for(let m of["height","width","max-width","border","background-color","color","margin-top","margin-right","margin-bottom","margin-left"])d.style.setProperty(m,g.getPropertyValue(m));c.length===0&&(d.style.marginTop=`calc(${l.marginTop} + ${g.marginTop})`),c.push(this.measureElement(d,n,t))}let a=this.view.getComputedStyle(r);for(let s=0;s<i.length;s++){this.checkpoint();let d=i[s],g=d.dataset.sourceAnchorId,m=Array.from(d.children);for(let u=0;u<m.length;u++){this.checkpoint();let b=m[u];if(!this.hasRangeFragmentSafeLayout(b))return null;let p=b.cloneNode(!0);if(p.dataset.paginationSafeEndnote="true",p.style.fontSize||=l.fontSize,p.style.lineHeight||=l.lineHeight,p.style.paddingLeft||=a.paddingLeft,g&&!Array.from(p.querySelectorAll("[data-source-anchor-id]")).some(y=>y.dataset.sourceAnchorId===g)){let y=this.document.createElement("span");for(y.dataset.sourceAnchorId=g;p.firstChild;)y.appendChild(p.firstChild);p.appendChild(y)}if(u===0){let y=d.id;if(y){if(p.id&&p.id!==y)return null;p.id=y}if(!p.querySelector("[data-endnote-number]")){let f=parseInt(d.getAttribute("value")||String(s+1),10),x=this.formatOrderedListMarker(Number.isFinite(f)?f:s+1,a.listStyleType);p.insertBefore(this.document.createTextNode(`${x}. `),p.firstChild)}}c.push(this.measureElement(p,n,t))}}return c}formatOrderedListMarker(e,n){let t=(()=>{switch(n){case"lower-roman":return"lowerRoman";case"upper-roman":return"upperRoman";case"lower-alpha":case"lower-latin":return"lowerLetter";case"upper-alpha":case"upper-latin":return"upperLetter";default:return"decimal"}})();return Pe(e,t)}buildColumnBlocks(e,n,t,o,r,i=!1){let c=Array.from(e.children),l=[],a=this.smallestEffectiveContentHeight(n,t),s=m=>m.dataset.pageBreak==="true"||m.classList.contains(`${this.cssPrefix}break`),d=m=>{let u=this.document.createElement("div");u.style.columnCount=String(o),u.style.columnGap=`${r}pt`,i&&(u.dataset.paginationSequentialColumns="true",u.style.columnFill="auto",u.style.maxHeight=`${a}pt`);for(let b of m)u.appendChild(b.cloneNode(!0));return i&&this.liftColumnSpaceBefore(u,n),u},g=0;for(;g<c.length;){if(this.checkpoint(),s(c[g])){l.push(this.measureElement(c[g],n,t)),g++;continue}let m=g+1,u=d(c.slice(g,m));i&&this.columnContainerOverflows(u,n)&&(delete u.dataset.paginationSequentialColumns,u.style.columnFill="balance",u.style.maxHeight="");let b=this.measureElement(u,n,t);for(;m<c.length&&!s(c[m]);){this.checkpoint();let p=d(c.slice(g,m+1)),y=this.measureElement(p,n,t);if(y.heightPt>a||i&&this.columnContainerOverflows(p,n))break;u=p,b=y,m++}l.push({element:u,sectionIndex:t,heightPt:b.heightPt,marginTopPt:b.marginTopPt,marginBottomPt:b.marginBottomPt,keepWithNext:!1,keepLines:!1,pageBreakBefore:!1,isPageBreak:!1,isWordParagraph:!1}),g=m}return l}liftColumnSpaceBefore(e,n){e.style.marginTop="";let t=e.firstElementChild;if(!t||!this.isWordParagraphElement(t))return;let o=this.document.createElement("div");o.style.width=`${n.contentWidth}pt`,o.appendChild(e),this.stagingElement.appendChild(o);try{let r=t.dataset.paginationColumnSpaceBefore??this.view.getComputedStyle(t).marginTop;t.dataset.paginationColumnSpaceBefore=r,t.style.marginTop="0",e.style.marginTop=r}finally{e.remove(),o.remove()}}columnContainerOverflows(e,n){let t=this.document.createElement("div");t.style.width=`${n.contentWidth}pt`;let o=e.cloneNode(!0);t.appendChild(o),this.stagingElement.appendChild(t);try{return o.scrollWidth>o.clientWidth+1?!0:Array.from(o.querySelectorAll('[data-keep-lines="true"]')).some(r=>r.getClientRects().length>1)}finally{t.remove()}}reflowSequentialColumns(e,n,t){if(e.element.dataset.paginationSequentialColumns!=="true"||t<=0)return null;let o=this.smallestEffectiveContentHeight(n,e.sectionIndex),r=Math.min(t,o);if(Math.abs(parseFloat(e.element.style.maxHeight)-r)<.01)return null;let i=Array.from(e.element.children),c=(s,d)=>{let g=e.element.cloneNode(!1);g.style.maxHeight=`${d}pt`;for(let m of s)g.appendChild(m.cloneNode(!0));return this.liftColumnSpaceBefore(g,n),g},l=c(i,r);if(!this.columnContainerOverflows(l,n))return[this.measureElement(l,n,e.sectionIndex)];let a=0;for(let s=1;s<i.length;s++){this.checkpoint();let d=c(i.slice(0,s),r);if(this.columnContainerOverflows(d,n))break;i[s-1].dataset.keepWithNext!=="true"&&(a=s)}return a===0?null:[this.measureElement(c(i.slice(0,a),r),n,e.sectionIndex),this.measureElement(c(i.slice(a),o),n,e.sectionIndex)]}measureElement(e,n,t){let o=this.document.createElement("div");o.style.position="absolute",o.style.visibility="hidden",o.style.left="-9999px",o.style.width=`${n.contentWidth}pt`;let r=e.cloneNode(!0);o.appendChild(r),this.stagingElement.appendChild(o);let i=r.getBoundingClientRect(),c=Te(this.view,r),l={element:e,sectionIndex:t,heightPt:I(i.height),marginTopPt:I(c.topPx),marginBottomPt:I(c.bottomPx),keepWithNext:e.dataset.keepWithNext==="true",keepLines:e.dataset.keepLines==="true",pageBreakBefore:e.dataset.pageBreakBefore==="true",isPageBreak:e.dataset.pageBreak==="true"||e.classList.contains(`${this.cssPrefix}break`),isWordParagraph:this.isWordParagraphElement(e)};return this.stagingElement.removeChild(o),l}getKeepWithNextChain(e,n){let t=e[n];if(!t)return[];let o=[t],r=n;for(;e[r].keepWithNext;){let i=e[r+1];if(!i||i.isPageBreak||i.pageBreakBefore)break;o.push(i),r++}return o}measureKeepWithNextChainBodyHeight(e,n,t,o){let r=e[0];if(!r)return 0;let c=this.effectiveBlockMarginTop(r,n,t,o)+r.heightPt;for(let l=1;l<e.length;l++){let a=e[l-1],s=e[l];c+=Math.max(a.marginBottomPt,s.marginTopPt)+s.heightPt}return c}isWordParagraphElement(e){return e.tagName==="P"||/^H[1-6]$/.test(e.tagName)}shouldSuppressPageTopSpacing(e,n,t){return n&&t>1&&e.isWordParagraph===!0}effectiveBlockMarginTop(e,n,t,o){return this.shouldSuppressPageTopSpacing(e,t,o)?0:t?e.marginTopPt:Math.max(e.marginTopPt,n)-n}cloneBlockForPage(e,n,t){let o=e.element.cloneNode(!0);return this.shouldSuppressPageTopSpacing(e,n,t)&&o.style.setProperty("margin-top","0","important"),o}collectNewFootnoteIds(e,n){let t=new Set(n),o=[];for(let r of e){this.checkpoint();for(let i of this.extractFootnoteRefs(r.element))t.has(i)||(t.add(i),o.push(i))}return o}smallestEffectiveContentHeight(e,n){return Math.min(this.getPageBands(e,n,1,1).bodyHeight,this.getPageBands(e,n,2,2).bodyHeight,this.getPageBands(e,n,3,3).bodyHeight)}createSimpleTableFragment(e,n,t,o,r){let i=e.cloneNode(!1),c=n.cloneNode(!1);for(let a of Array.from(n.children))a!==t&&c.appendChild(a.cloneNode(!0));let l=t.cloneNode(!1);for(let a of o)l.appendChild(a.cloneNode(!0));return c.appendChild(l),r||(i.removeAttribute("data-anchor"),c.removeAttribute("data-anchor")),i.appendChild(c),i}trySplitSimpleOversizedTable(e,n,t){let o=e.element;if(o.tagName!=="DIV"||o.children.length!==1||e.keepWithNext||e.keepLines||e.pageBreakBefore||e.isPageBreak)return null;let r=o.firstElementChild;if(!r||r.localName!=="table")return null;let i=r,c=i.tBodies.length===1?i.tBodies[0]:null;if(!c||i.tHead||i.tFoot||c.rows.length<2||Array.from(i.children).some(u=>u!==c&&u.tagName!=="COLGROUP")||i.querySelector("table, [rowspan], [data-footnote-id]")||o.querySelector("[data-footnote-id]"))return null;let l=Array.from(c.rows),a=this.smallestEffectiveContentHeight(n,t),s=a-e.marginTopPt-e.marginBottomPt;if(s<=0)return null;let d=[],g=0;for(;g<l.length;){this.checkpoint();let u=g;for(;u<l.length;){this.checkpoint();let b=this.createSimpleTableFragment(o,i,c,l.slice(g,u+1),g===0);if(this.measureElement(b,n,e.sectionIndex).heightPt>s)break;u++}if(u===g)return null;d.push(l.slice(g,u)),g=u}if(d.length<2)return null;let m=[];for(let u=0;u<d.length;u++){let b=u===0,p=u===d.length-1,y=this.createSimpleTableFragment(o,i,c,d[u],b);b||y.style.setProperty("margin-top","0","important"),p||y.style.setProperty("margin-bottom","0","important");let f=this.measureElement(y,n,e.sectionIndex);if(f.heightPt+f.marginTopPt+f.marginBottomPt>a)return null;m.push({...f,keepWithNext:!1,keepLines:!1,pageBreakBefore:!1,isPageBreak:!1})}return m}paragraphFragmentEndpoints(e,n=!1){let t=[],o=new Map,r=new Map,i=this.document.createTreeWalker(e,this.view.NodeFilter.SHOW_TEXT),c=[],l=0,a;for(;a=i.nextNode();){this.checkpoint(),o.set(a,l),t.push(a),c.push(a.data),l+=a.data.length;let f=a.parentElement?.closest("[data-field]")??null;for(;f?.parentElement?.closest("[data-field]");)f=f.parentElement.closest("[data-field]");f&&e.contains(f)&&r.set(f,a)}let s=c.join("");if(!this.hasVisibleText(s))return[];let d=/[\s\u200B-\u200F\uFEFF]/,g=-1,m=-1;for(let f=0;f<s.length;f++)f%4096===0&&this.checkpoint(),!d.test(s[f])&&(g<0&&(g=f),m=f);let u=this.graphemeBoundaryOffsets(s),b=[],p=f=>{let{textOffset:x}=f;if(!(x<=0||x>=s.length)&&!(x<=g||x>m)&&!this.isNonBreakingTextBoundary(s,x)&&this.isLegalParagraphLineBoundary(e,s,x)){if(u){if(!u.has(x))return}else if(!this.isConservativeFallbackTextBoundary(s,x))return;b.push(f)}};for(let f of t){this.checkpoint();let x=o.get(f),M=f.parentElement?.closest("[data-field], [data-list-marker], a[data-comment-id]");if(M&&e.contains(M))continue;let F=/[\u0009-\u000D\u0020]+/g,H,T=0;for(;(H=F.exec(f.data))!==null;){++T%256===0&&this.checkpoint();let S=H.index+H[0].length;p({node:f,offset:S,textOffset:x+S,priority:0})}if(p({node:f,offset:f.data.length,textOffset:x+f.data.length,priority:0}),n)for(let S=1;S<f.data.length;S++)S%256===0&&this.checkpoint(),p({node:f,offset:S,textOffset:x+S,priority:-1})}for(let f of Array.from(e.querySelectorAll("[data-field]"))){if(this.checkpoint(),f.parentElement?.closest("[data-field]"))continue;let x=r.get(f),M=f.parentNode;if(!x||!M)continue;let F=Array.prototype.indexOf.call(M.childNodes,f);F<0||p({node:M,offset:F+1,textOffset:o.get(x)+x.data.length,priority:1})}b.sort((f,x)=>f.textOffset-x.textOffset||x.priority-f.priority);let y=[];for(let f of b)y[y.length-1]?.textOffset!==f.textOffset&&y.push(f);return y}graphemeBoundaryOffsets(e){let n=this.view.Intl.Segmenter;if(!n)return null;let t=new Set([0,e.length]),o=0;for(let r of new n("en",{granularity:"grapheme"}).segment(e))++o%256===0&&this.checkpoint(),t.add(r.index),t.add(r.index+r.segment.length);return t}isLegalParagraphLineBoundary(e,n,t){let o=n.charCodeAt(t-1),r=o>=56320&&o<=57343?Math.max(0,t-2):t-1,i=n.codePointAt(r),c=n.codePointAt(t),l=i===void 0?"":String.fromCodePoint(i),a=c===void 0?"":String.fromCodePoint(c);if(/^[\u0009-\u000D\u0020]$/.test(l)||/^[\u0009-\u000D\u0020]$/.test(a)||/[-\u00AD\u2010]$/u.test(l))return!0;let s=this.view.getComputedStyle(e);if(s.wordBreak==="break-all"||s.overflowWrap==="anywhere"||s.overflowWrap==="break-word")return!0;let d=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;return!d.test(l)&&!d.test(a)?!1:!"([{<\u2018\u201C\u3008\u300A\u300C\u300E\u3010\u3014\u3016\u3018\u301A\uFF08\uFF3B\uFF5B".includes(l)&&!")]}>\u2019\u201D\u3001\u3002\u3009\u300B\u300D\u300F\u3011\u3015\u3017\u3019\u301B\uFF01\uFF05\uFF09\uFF0C\uFF0E\uFF1A\uFF1B\uFF1F\uFF3D\uFF5D".includes(a)}isNonBreakingTextBoundary(e,n){let t=r=>{if(r<=0)return;let i=e.charCodeAt(r-1),c=i>=56320&&i<=57343?r-2:r-1;return e.codePointAt(Math.max(0,c))},o=new Set([160,8206,8207,8209,8234,8235,8236,8237,8238,8239,8288,8294,8295,8296,8297,65279]);return o.has(t(n)??-1)||o.has(e.codePointAt(n)??-1)}isConservativeFallbackTextBoundary(e,n){return e.charCodeAt(n-1)<=127&&e.charCodeAt(n)<=127}hasVisibleFragmentText(e){return this.hasVisibleText(e.textContent||"")}hasVisibleText(e){return e.replace(/[\u200B-\u200F\uFEFF]/g,"").trim().length>0}canFragmentParagraph(e){if(!this.fragmentParagraphs)return!1;let n=e.element;return e.keepWithNext||e.keepLines||e.pageBreakBefore||e.isPageBreak||n.dataset.widowControl==="true"||n.hasAttribute("contenteditable")?!1:this.canRangeFragmentParagraph(n)}canRangeFragmentParagraph(e,n=!1){if(e.tagName!=="P"||e.dataset.keepWithNext==="true"||e.dataset.keepLines==="true"||e.dataset.pageBreakBefore==="true"||e.dataset.widowControl==="true"||e.hasAttribute("contenteditable"))return!1;let t=["br","img","picture","svg","math","canvas","video","audio","iframe","object","embed","input","button","select","textarea","table","ol","ul","li","dl","div","p","section","article","aside","figure","fieldset","details","[data-footnote-id]","[contenteditable]"].join(", ");return e.querySelector(t)?!1:e.dataset.paginationSafeEndnote==="true"||this.hasRangeFragmentSafeLayout(e,n)}hasRangeFragmentSafeLayout(e,n=!1){let t=this.view.getComputedStyle(e);if(t.display!=="block"&&!(n&&t.display==="inline")||t.position!=="static"||t.float!=="none"||t.whiteSpace!=="normal"&&t.whiteSpace!=="pre-wrap"||t.breakBefore!=="auto"||t.breakAfter!=="auto"||t.breakInside==="avoid"||t.pageBreakBefore!=="auto"||t.pageBreakAfter!=="auto"||t.pageBreakInside==="avoid")return!1;for(let o of Array.from(e.querySelectorAll("*"))){let r=this.view.getComputedStyle(o);if(r.display!=="inline"||r.position!=="static"||r.float!=="none"||r.whiteSpace!=="normal"&&r.whiteSpace!=="pre-wrap")return!1}return!0}createParagraphFragment(e,n,t,o){let r=e.cloneNode(!1),i=n.cloneContents();return r.appendChild(i),t||(r.removeAttribute("id"),r.removeAttribute("data-anchor"),r.style.setProperty("margin-top","0","important"),r.style.setProperty("text-indent","0","important")),o||r.style.setProperty("margin-bottom","0","important"),r}reconcileParagraphFragmentIdentities(e,n){let t=(i,c)=>[...i.matches(c)?[i]:[],...Array.from(i.querySelectorAll(c))],o=new Set(t(e,"[id]").map(i=>i.id));for(let i of t(n,"[id]"))o.has(i.id)&&i.removeAttribute("id");let r=new Set(t(e,"[data-anchor]").map(i=>i.dataset.anchor).filter(i=>!!i));for(let i of t(n,"[data-anchor]"))!i.dataset.anchor||!r.has(i.dataset.anchor)||(i.removeAttribute("data-anchor"),i.removeAttribute("data-committed-text"))}splitParagraphAtLargestFit(e,n,t={}){let o=s=>{let d=s[s.length-1];if(d){let b=this.document.createRange();b.setStart(e,0),b.setEnd(d.node,d.offset);let p=b.cloneContents();if(this.hasVisibleFragmentText(p)){let y=this.createParagraphFragment(e,b,!0,!1);if(n(y))return{endpoint:d,head:y}}}let g=0,m=s.length-2,u=null;for(;g<=m;){this.checkpoint();let b=Math.floor((g+m)/2),p=s[b],y=this.document.createRange();y.setStart(e,0),y.setEnd(p.node,p.offset);let f=y.cloneContents();if(!this.hasVisibleFragmentText(f)){g=b+1;continue}let x=this.createParagraphFragment(e,y,!0,!1);n(x)?(u={endpoint:p,head:x},g=b+1):m=b-1}return u},r=this.paragraphFragmentEndpoints(e),i=o(r);if(!i&&t.emergencyGraphemeBreaks&&(r=this.paragraphFragmentEndpoints(e,!0),i=o(r)),!i&&t.forceFirstOnNoFit&&r.length>0){let s=r[0],d=this.document.createRange();d.setStart(e,0),d.setEnd(s.node,s.offset),i={endpoint:s,head:this.createParagraphFragment(e,d,!0,!1)}}if(!i)return r.length>0?{kind:"no-fit"}:{kind:"indivisible"};let c=this.document.createRange();c.setStart(i.endpoint.node,i.endpoint.offset),c.setEnd(e,e.childNodes.length);let l=c.cloneContents();if(!this.hasVisibleFragmentText(l))return{kind:"indivisible"};let a=this.createParagraphFragment(e,c,!1,!0);return this.reconcileParagraphFragmentIdentities(i.head,a),{kind:"split",head:i.head,tail:a}}tryFragmentParagraph(e,n,t,o){if(!this.canFragmentParagraph(e)||t<=o)return null;let r=this.splitParagraphAtLargestFit(e.element,l=>{let a=this.measureElement(l,n,e.sectionIndex);return o+a.heightPt<=t});if(r.kind!=="split")return null;let i=this.measureElement(r.head,n,e.sectionIndex),c=this.measureElement(r.tail,n,e.sectionIndex);return[{...i,element:r.head,keepWithNext:!1,keepLines:!1,pageBreakBefore:!1,isPageBreak:!1},{...c,element:r.tail,keepWithNext:!1,keepLines:!1,pageBreakBefore:!1,isPageBreak:!1}]}parseHeaderFooterRegistry(){let e=new Map,n=this.stagingElement.querySelector("#pagination-hf-registry");if(!n)return e;let t=new Map,o=Array.from(this.stagingElement.querySelectorAll("[data-section-index]"));for(let c of o){let l=parseInt(c.dataset.sectionIndex||"0",10),a=parseFloat(c.dataset.contentWidth||"")||468;t.set(l,a)}let r=t.get(0)||468,i=Array.from(n.querySelectorAll("[data-section][data-hf-type]"));for(let c of i){let l=parseInt(c.dataset.section||"0",10),a=c.dataset.hfType;e.has(l)||e.set(l,{});let s=e.get(l),d=c.cloneNode(!0),g=t.get(l)||r,m=this.measureHeaderFooterHeight(d,g);switch(a){case"header-default":s.headerDefault=d,s.headerDefaultHeight=m;break;case"header-first":s.headerFirst=d,s.headerFirstHeight=m;break;case"header-even":s.headerEven=d,s.headerEvenHeight=m;break;case"footer-default":s.footerDefault=d,s.footerDefaultHeight=m;break;case"footer-first":s.footerFirst=d,s.footerFirstHeight=m;break;case"footer-even":s.footerEven=d,s.footerEvenHeight=m;break}}return e}parseFootnoteRegistry(){let e=new Map,n=this.stagingElement.querySelector("#pagination-footnote-registry");if(!n)return e;let t=Array.from(n.children).filter(o=>o instanceof this.view.HTMLElement&&o.hasAttribute("data-footnote-id"));for(let o of t){let r=o.dataset.footnoteId;r&&e.set(r,o.cloneNode(!0))}return e}parseFootnoteSeparators(){let e=this.stagingElement.querySelector("#pagination-footnote-registry"),n=t=>Array.from(e?.children??[]).find(r=>r instanceof this.view.HTMLElement&&r.getAttribute("data-footnote-separator")===t)?.cloneNode(!0);return{normal:n("normal")??null,continuation:n("continuation")??null}}appendFootnoteSeparator(e,n){let t=n?"continuation":"normal",o=n?this.footnoteContinuationSeparator:this.footnoteSeparator;if(!o){let i=this.document.createElement("hr");i.dataset.footnoteSeparator=t,e.appendChild(i);return}let r=o.cloneNode(!0);for(let i of[r,...Array.from(r.querySelectorAll("*"))])i.removeAttribute("id"),i.removeAttribute("data-anchor"),i.removeAttribute("data-committed-text"),i.removeAttribute("data-source-anchor-id"),i.removeAttribute("data-page-fragment-id"),i.removeAttribute("data-fragment-index"),i.removeAttribute("data-footnote-id"),i.removeAttribute("data-comment-id"),i.removeAttribute("name"),i instanceof this.view.HTMLAnchorElement&&i.getAttribute("href")?.startsWith("#")&&i.removeAttribute("href"),i.setAttribute("contenteditable","false");e.appendChild(r)}parseCommentMarginRegistry(){let e=new Map,n=this.stagingElement.querySelector("#pagination-comment-margin-registry");if(!n)return e;for(let t of Array.from(n.querySelectorAll("[data-comment-id]"))){let o=t.dataset.commentId;if(!o)continue;let r=t;for(let i=r.parentElement?.closest("[data-comment-id]");i&&n.contains(i);i=r.parentElement?.closest("[data-comment-id]"))r=i;e.set(o,r.cloneNode(!0))}return e}extractFootnoteRefs(e){let n=e.querySelectorAll("[data-footnote-id]"),t=[];for(let o of Array.from(n)){let r=o.dataset.footnoteId;r&&!t.includes(r)&&t.push(r)}return t}cloneFootnoteElementForContinuation(e,n){let t=e.cloneNode(!0);return t.setAttribute(G,n===0?"first":"later"),t}makeContinuationShellInert(e){e.removeAttribute("id"),e.removeAttribute("data-anchor"),e.removeAttribute("data-committed-text")}createFootnoteContinuationWrapper(e,n=e.remainingElements){let t=this.footnoteRegistry.get(e.footnoteId),o=t?t.cloneNode(!1):this.document.createElement("div");this.makeContinuationShellInert(o),o.classList.add("footnote-continuation"),o.dataset.footnoteId=e.footnoteId,e.sourceAnchorId&&(o.dataset.sourceAnchorId=e.sourceAnchorId);let r=t?.querySelector(".footnote-content"),i=r?r.cloneNode(!1):this.document.createElement("span");this.makeContinuationShellInert(i),r||(i.className="footnote-content");let c=n[0];if(c?.tagName==="P"&&c.getAttribute(G)==="later"){let l=this.document.createElement("p");l.className="footnote-continuation-position-sentinel",l.setAttribute("aria-hidden","true"),l.style.setProperty("display","none","important"),i.appendChild(l)}for(let l=0;l<n.length;l++){this.checkpoint();let s=n[l].cloneNode(!0);s.removeAttribute(G),l===0&&s.tagName==="P"&&s.style.setProperty("display","block","important"),i.appendChild(s)}return o.appendChild(i),o}createPartialFootnoteItem(e,n){let t=e.cloneNode(!1),o=e.querySelector(".footnote-number");o&&t.appendChild(o.cloneNode(!0));let r=e.querySelector(".footnote-content"),i=r?r.cloneNode(!1):this.document.createElement("span");r||(i.className="footnote-content");for(let c of n)this.checkpoint(),i.appendChild(c.cloneNode(!0));return t.appendChild(i),t}noteMeasurementHost(){return this.containerElement.isConnected?this.containerElement:this.stagingElement}measureFootnotesHeight(e,n,t,o){let r=t&&t.remainingElements.length>0;if(e.length===0&&!r||this.footnoteRegistry.size===0&&!r)return 0;let i=this.document.createElement("div");i.style.position="absolute",i.style.visibility="hidden",i.style.width=`${n}pt`,i.style.left="-9999px",i.className=this.cssPrefix+"footnotes",this.appendFootnoteSeparator(i,!!r),r&&i.appendChild(this.createFootnoteContinuationWrapper(t));let c=new Map(o?.map(l=>[l.footnoteId,l])??[]);for(let l of e){this.checkpoint();let a=this.footnoteRegistry.get(l);if(a){let s=c.get(l);i.appendChild(s?this.createPartialFootnoteItem(a,s.fittingElements):a.cloneNode(!0))}}this.noteMeasurementHost().appendChild(i);try{return I(i.getBoundingClientRect().height)}finally{i.remove()}}splitContinuationForPage(e,n,t){let o=[],r=[],i=this.document.createElement("div");i.style.position="absolute",i.style.visibility="hidden",i.style.width=`${t}pt`,i.style.left="-9999px",i.className=this.cssPrefix+"footnotes",this.appendFootnoteSeparator(i,!0);let c=this.createFootnoteContinuationWrapper(e,[]),l=c.querySelector(":scope > .footnote-content");if(!l)throw new Error("Footnote continuation is missing its content shell");i.appendChild(c),this.noteMeasurementHost().appendChild(i);try{for(let a=0;a<e.remainingElements.length;a++){this.checkpoint();let s=e.remainingElements[a],d=s.getAttribute(G);if(o.length===0&&s.tagName==="P"&&s.getAttribute(G)==="later"){let p=this.document.createElement("p");p.className="footnote-continuation-position-sentinel",p.setAttribute("aria-hidden","true"),p.style.setProperty("display","none","important"),l.appendChild(p)}let g=s.cloneNode(!0);if(g.removeAttribute(G),o.length===0&&g.tagName==="P"&&g.style.setProperty("display","block","important"),l.appendChild(g),I(i.getBoundingClientRect().height)<=n){o.push(s);continue}let u=g.tagName==="P"&&this.canRangeFragmentParagraph(g);g.remove();let b=null;u&&(b=this.splitParagraphAtLargestFit(g,p=>{l.appendChild(p);try{return I(i.getBoundingClientRect().height)<=n}finally{p.remove()}},{emergencyGraphemeBreaks:o.length===0,forceFirstOnNoFit:o.length===0})),b?.kind==="split"?(d&&(b.head.setAttribute(G,d),b.tail.setAttribute(G,d)),o.push(b.head),r=[b.tail,...e.remainingElements.slice(a+1)]):o.length===0?(o.push(s),r=e.remainingElements.slice(a+1)):r=e.remainingElements.slice(a);break}}finally{i.remove()}return{current:{footnoteId:e.footnoteId,sourceAnchorId:e.sourceAnchorId,remainingElements:o},overflow:r.length>0?{footnoteId:e.footnoteId,sourceAnchorId:e.sourceAnchorId,remainingElements:r}:null}}splitFootnoteToFit(e,n,t,o=!1,r){let i=this.document.createElement("div");i.style.position="absolute",i.style.visibility="hidden",i.style.width=`${t}pt`,i.style.left="-9999px",i.className=this.cssPrefix+"footnotes",this.appendFootnoteSeparator(i,!1);let c=e.cloneNode(!0);i.appendChild(c),this.noteMeasurementHost().appendChild(i);let l=this.document.createElement("div");l.style.position="absolute",l.style.visibility="hidden",l.style.width=`${t}pt`,l.style.left="-9999px",l.className=this.cssPrefix+"footnotes";let a=!!r?.continuation?.remainingElements.length;this.appendFootnoteSeparator(l,a),a&&l.appendChild(this.createFootnoteContinuationWrapper(r.continuation));let s=new Map(r?.partialFootnotes?.map(m=>[m.footnoteId,m])??[]);for(let m of r?.footnoteIds??[]){this.checkpoint();let u=this.footnoteRegistry.get(m);if(!u)continue;let b=s.get(m);l.appendChild(b?this.createPartialFootnoteItem(u,b.fittingElements):u.cloneNode(!0))}let d=this.createPartialFootnoteItem(c,[]),g=d.querySelector(":scope > .footnote-content");if(!g)throw i.remove(),new Error("Footnote is missing its content shell");l.appendChild(d),this.noteMeasurementHost().appendChild(l);try{let m=c.querySelector(".footnote-content");if(!m)return{fits:Array.from(c.children).map(y=>y.cloneNode(!0)),overflow:[]};let u=Array.from(m.children),b=[],p=[];for(let y=0;y<u.length;y++){this.checkpoint();let f=u[y],x=f.cloneNode(!0);if(g.appendChild(x),I(l.getBoundingClientRect().height)<=n){b.push(f.cloneNode(!0));continue}x.remove();let F=this.canRangeFragmentParagraph(f,!0)?this.splitParagraphAtLargestFit(f,H=>{g.appendChild(H);try{return I(l.getBoundingClientRect().height)<=n}finally{H.remove()}},{emergencyGraphemeBreaks:b.length===0&&o,forceFirstOnNoFit:b.length===0&&o}):null;F?.kind==="split"?(b.push(F.head),F.tail.setAttribute(G,y===0?"first":"later"),p=[F.tail,...u.slice(y+1).map((H,T)=>this.cloneFootnoteElementForContinuation(H,y+1+T))]):F?.kind==="no-fit"?p=u.slice(y).map((H,T)=>this.cloneFootnoteElementForContinuation(H,y+T)):b.length===0&&o?(b.push(f.cloneNode(!0)),p=u.slice(y+1).map((H,T)=>this.cloneFootnoteElementForContinuation(H,y+1+T))):p=u.slice(y).map((H,T)=>this.cloneFootnoteElementForContinuation(H,y+T));break}return{fits:b,overflow:p}}finally{l.remove(),i.remove()}}addPageFootnotes(e,n,t,o,r,i,c){let l=i&&i.remainingElements.length>0;if(n.length===0&&!l||this.footnoteRegistry.size===0&&!l)return;let a=Math.min(r,o.bodyHeight*te),s=this.document.createElement("div");s.className=`${this.cssPrefix}footnotes`,s.style.position="absolute",s.style.bottom=`${t.pageHeight-(o.bodyTop+o.bodyHeight)}pt`,s.style.left=`${t.marginLeft}pt`,s.style.width=`${t.contentWidth}pt`,s.style.boxSizing="border-box",s.style.maxHeight=`${a}pt`,s.style.overflow="hidden",this.appendFootnoteSeparator(s,!!l),l&&s.appendChild(this.createFootnoteContinuationWrapper(i));for(let d of n){let g=c?.find(m=>m.footnoteId===d);if(g){let m=this.footnoteRegistry.get(d);m&&s.appendChild(this.createPartialFootnoteItem(m,g.fittingElements))}else{let m=this.footnoteRegistry.get(d);m&&s.appendChild(m.cloneNode(!0))}}e.appendChild(s)}selectHeader(e,n,t){let o=this.hfRegistry.get(e);if(o)return n===1&&o.headerFirst?o.headerFirst:t%2===0&&o.headerEven?o.headerEven:o.headerDefault}storyKindFor(e,n,t,o){let r=this.hfRegistry.get(n),i=e==="header"?r?.headerFirst:r?.footerFirst,c=e==="header"?r?.headerEven:r?.footerEven;return t===1&&i?"first":o%2===0&&c?"even":"default"}selectFooter(e,n,t){let o=this.hfRegistry.get(e);if(o)return n===1&&o.footerFirst?o.footerFirst:t%2===0&&o.footerEven?o.footerEven:o.footerDefault}bodyBudgetPt(e,n){return Math.max(0,e-(this.bodyBudgetReductionsPt[n]??0))}getPageBands(e,n,t,o){let r=this.hfRegistry.get(n);return Re(e,this.selectStoryHeight(r?.headerFirstHeight,r?.headerEvenHeight,r?.headerDefaultHeight,t,o),this.selectStoryHeight(r?.footerFirstHeight,r?.footerEvenHeight,r?.footerDefaultHeight,t,o))}selectStoryHeight(e,n,t,o,r){return o===1&&e!=null?e:r%2===0&&n!=null?n:t??0}measureHeaderFooterHeight(e,n){let t=this.document.createElement("div");t.style.position="absolute",t.style.visibility="hidden",t.style.width=`${n}pt`,t.style.left="-9999px";for(let i of Array.from(e.childNodes))t.appendChild(i.cloneNode(!0));this.stagingElement.appendChild(t);let o=t.getBoundingClientRect(),r=I(o.height);return this.stagingElement.removeChild(t),r}flowToPages(e,n,t,o,r){let i=[],c=[],l=t,a=o,s=new Map([[o,t]]),d=(v=a,E=l)=>E-(s.get(v)??E)+1,g=(v=a)=>r.get(v)??n,m=v=>{s.has(v)||s.set(v,l)},u=this.containerElement.querySelector(`.${this.cssPrefix}box:last-of-type`),b=parseInt(u?.dataset.displayedPageNumber??"0",10),p=(v=a,E=d(v),R=l)=>{let O=this.pageNumbering.get(v)??{};if(O.start!==void 0)return O.start+E-1;let w=this.pageNumbering.get(a)??{};return(w.start!==void 0?w.start+d(a)-1:b+1)+R-l},y=this.bodyBudgetPt(this.getPageBands(g(),a,d(),p()).bodyHeight,l),f=y,x=0,M=[],F=!1,H=0,T=this.pendingFootnoteContinuation,S=null,$=!1,P=!1,C=[],A=[],X=v=>{a=v;let E=this.getPageBands(g(v),v,d(v),p(v));y=this.bodyBudgetPt(E.bodyHeight,l),f=y},ne=()=>{P||(this.admitPageAllocation(),P=!0)},oe=()=>{if($||(T?.remainingElements.length??0)===0)return;ne();let v=g(),E=this.getPageBands(v,a,d(),p()),R=this.splitContinuationForPage(T,E.bodyHeight*te,v.contentWidth);T=R.current,R.overflow&&(S=R.overflow),$=!0,H=this.measureFootnotesHeight(M,v.contentWidth,T,A)},W=(v=a,E=!1)=>{let R=(T?.remainingElements.length??0)>0;if(!E&&c.length===0&&M.length===0&&!R){X(v);return}oe(),ne();let O=T,w=d(),_=g(),Q=p(a,w),ie=this.getPageBands(_,a,w,Q).bodyHeight*te;if(O=T,H=this.measureFootnotesHeight(M,_.contentWidth,O,A),c.length===0&&M.length>0&&A.length===0){let q=[];for(let z=0;z<M.length;z++){let k=M[z],se=[...q,k],j=this.measureFootnotesHeight(se,_.contentWidth,O)+re;if(j<=ie){q.push(k),H=j;continue}let pe=(O?.remainingElements.length??0)>0;if(q.length===0&&!pe){let Y=this.footnoteRegistry.get(k),L=Y?this.splitFootnoteToFit(Y,ie-re,_.contentWidth,!0):null;q.push(k),Y&&L&&L.fits.length>0&&L.overflow.length>0?(A.push({footnoteId:k,fittingElements:L.fits}),S={footnoteId:k,sourceAnchorId:Y.dataset.sourceAnchorId,remainingElements:L.overflow},H=ie):H=j,C.push(...M.slice(z+1))}else C.push(...M.slice(z));break}M=q}let Ce=this.createPage(_,l,a,Q,c,w,M,H,O,A.length>0?A:void 0,!1,P);i.push(Ce),b=Q,l++,c=[],a=v,s.has(a)||s.set(a,l);let B=this.getPageBands(g(),a,d(),p());y=this.bodyBudgetPt(B.bodyHeight,l),f=y,x=0,M=[],F=!1,A=[],T=S,S=null,$=!1,P=!1,C.length>0&&(M=[...C],C=[]);let Z=g();H=T?0:this.measureFootnotesHeight(M,Z.contentWidth)},ve=()=>c.length>0||M.length>0||(T?.remainingElements.length??0)>0;for(let v=0;v<e.length;v++){this.checkpoint();let E=e[v],R=this.extractFootnoteRefs(E.element);if(E.sectionIndex!==a&&F&&(!this.footnoteLayoutLikeWord8||R.length>0)&&W(E.sectionIndex),c.length===0&&M.length===0&&(T?.remainingElements.length??0)===0&&E.sectionIndex!==a&&X(E.sectionIndex),oe(),H>0){let L=g(),J=this.getPageBands(L,a,d(),p()).bodyHeight*te;if(H>J){let U=[],D=0;if((T?.remainingElements.length??0)===0&&A.length===0)for(let N of M){this.checkpoint();let K=this.measureFootnotesHeight([...U,N],L.contentWidth);if(K+re>J)break;U.push(N),D=K}if(U.length>0)C=[...M.slice(U.length),...C],M=U,H=D;else{W(E.sectionIndex),v--;continue}}}let w=g(E.sectionIndex),_=d(E.sectionIndex,l+1),Q=this.getPageBands(w,E.sectionIndex,_,p(E.sectionIndex,_,l+1)).bodyHeight;if(E.isPageBreak){W(E.sectionIndex);continue}E.pageBreakBefore&&c.length>0&&W(E.sectionIndex);let ae=e[v-1],ie=!ae||!ae.keepWithNext||ae.isPageBreak||E.pageBreakBefore;if((c.length>0||H>0)&&E.keepWithNext&&ie){let L=this.getKeepWithNextChain(e,v);if(L.length>1){let V=this.collectNewFootnoteIds(L,M),J=0;if(V.length>0&&this.footnoteRegistry.size>0){let N=this.measureFootnotesHeight([...M,...V],w.contentWidth,T);J=Math.max(0,N-H)}let U=this.measureKeepWithNextChainBodyHeight(L,x,c.length===0,d(E.sectionIndex))+J,D=f-H;if(U>D){let N=this.getPageBands(g(E.sectionIndex),E.sectionIndex,d(E.sectionIndex,l+1),p(E.sectionIndex,d(E.sectionIndex,l+1),l+1)),K=this.measureKeepWithNextChainBodyHeight(L,0,!0,d(E.sectionIndex,l+1)),be=this.measureFootnotesHeight(V,w.contentWidth,S);K+be<=N.bodyHeight&&W(E.sectionIndex)}}}let B=this.collectNewFootnoteIds([E],M),Z=H,q=0;if(B.length>0&&this.footnoteRegistry.size>0){let L=[...M,...B];Z=this.measureFootnotesHeight(L,w.contentWidth,T,A),q=Math.max(0,Z-H)}let z=c.length===0,k=this.effectiveBlockMarginTop(E,x,z,d(E.sectionIndex)),se=this.reflowSequentialColumns(E,w,f-H-q-k);if(se){e.splice(v,1,...se),v--;continue}let fe=k+E.heightPt+q,j=f-H,pe=y-f,Y=y*te;if(fe>j){let L=this.tryFragmentParagraph(E,w,j,k);if(L){e.splice(v,1,...L),v--;continue}}if(fe<=j&&(Z===0||Z+re<=Y))m(E.sectionIndex),c.push(this.cloneBlockForPage(E,z,d(E.sectionIndex))),f-=k+E.heightPt+E.marginBottomPt,x=E.marginBottomPt,B.length>0&&(M.push(...B),H=Z),F||=R.length>0;else if(E.heightPt+this.effectiveBlockMarginTop(E,0,!0,d(E.sectionIndex,l+1))<=Q){let L=k+E.heightPt;if(B.length>0&&L<=j){m(E.sectionIndex),c.push(this.cloneBlockForPage(E,z,d(E.sectionIndex))),f-=k+E.heightPt+E.marginBottomPt,x=E.marginBottomPt;let V=Math.min(Y,y-pe-L),J=Q*te,U=V>=J;for(let D=0;D<B.length;D++){this.checkpoint();let N=B[D],K=this.footnoteRegistry.get(N);if(!K)continue;if(S){C.push(...B.slice(D));break}let be=[...M,N],Ae=this.measureFootnotesHeight(be,w.contentWidth,T,A),qe=V-H;if(Ae+re<=V)M.push(N),H=Ae;else if(qe>20){let{fits:Fe,overflow:ye}=this.splitFootnoteToFit(K,V,w.contentWidth,U,{footnoteIds:M,continuation:T,partialFootnotes:A});if(Fe.length>0){if(M.push(N),A.push({footnoteId:N,fittingElements:Fe}),ye.length>0&&(S={footnoteId:N,sourceAnchorId:K.dataset.sourceAnchorId,remainingElements:ye}),H=this.measureFootnotesHeight(M,w.contentWidth,T,A),ye.length>0){C.push(...B.slice(D+1));break}}else{C.push(...B.slice(D));break}}else{C.push(...B.slice(D));break}}F||=R.length>0}else{W(E.sectionIndex,!ve()),v--;continue}}else{let L=this.trySplitSimpleOversizedTable(E,g(E.sectionIndex),E.sectionIndex);if(L){(c.length>0||T)&&W(E.sectionIndex),e.splice(v,1,...L),v--;continue}if(ve()){W(E.sectionIndex),v--;continue}m(E.sectionIndex),c.push(this.cloneBlockForPage(E,!0,d(E.sectionIndex))),C.push(...B),F||=R.length>0,W(E.sectionIndex)}}for(W();M.length>0||C.length>0||(T?.remainingElements.length??0)>0;)W();return this.pendingFootnoteContinuation=S,i}makeClonedStoryInert(e){let n=[e,...Array.from(e.querySelectorAll("*"))];for(let t of n)t.removeAttribute("data-anchor"),t.removeAttribute("data-committed-text"),t.getAttribute("contenteditable")!==null&&t.setAttribute("contenteditable","false")}makeClonedMarginCommentInert(e){this.makeClonedStoryInert(e);let n=[e,...Array.from(e.querySelectorAll("*"))];for(let t of n)t.removeAttribute("id"),t.localName==="a"&&t.getAttribute("href")?.startsWith("#")&&(t.removeAttribute("href"),t.setAttribute("aria-disabled","true"),t.tabIndex=-1)}positionDrawingAnchors(e,n,t,o){let r=Array.from(n.querySelectorAll('[data-docx-drawing-anchor="true"]'));if(r.length===0)return;let i=e.getBoundingClientRect(),c=i.width/t.pageWidth;if(c>0)for(let l of r){let a=l.getBoundingClientRect(),s=l.closest("p, h1, h2, h3, h4, h5, h6"),d=s?.getBoundingClientRect()??a,g=A=>(A-i.left)/c,m=A=>(A-i.top)/c,u={staticLeft:g(a.left),staticTop:m(a.top),lineHeight:s?(parseFloat(this.view.getComputedStyle(s).lineHeight)||a.height)/c:a.height/c,paragraphLeft:g(d.left),paragraphTop:m(d.top),paragraphWidth:d.width/c,paragraphHeight:d.height/c},b=this.horizontalAnchorReference(l.getAttribute("data-docx-anchor-width-relative")??"margin",t,o,u),p=this.anchorNumber(l,"width-percent");p!==void 0&&p>0&&(l.style.width=`${b.size*p/100}pt`);let y=this.verticalAnchorReference(l.getAttribute("data-docx-anchor-height-relative")??"margin",t,o,u),f=this.anchorNumber(l,"height-percent");f!==void 0&&f>0&&l.dataset.docxAnchorAutofit!=="true"&&(l.style.height=`${y.size*f/100}pt`);let x=l.getBoundingClientRect(),M=x.width/c,F=x.height/c,H=this.horizontalAnchorReference(l.getAttribute("data-docx-anchor-h-relative")??"column",t,o,u),T=this.verticalAnchorReference(l.getAttribute("data-docx-anchor-v-relative")??"paragraph",t,o,u),S=this.resolveAnchorAxis(H,M,l.getAttribute("data-docx-anchor-h-align"),this.anchorNumber(l,"h-offset"),o),$=this.resolveAnchorAxis(T,F,l.getAttribute("data-docx-anchor-v-align"),this.anchorNumber(l,"v-offset"),o),P=l.parentElement?.closest("[data-source-anchor-id]"),C=P?.dataset.sourceAnchorId;C&&l.setAttribute(ue,C),e.appendChild(l),P&&st(P)&&P.setAttribute(ge,"true"),l.style.position="absolute",l.style.left=`${S}pt`,l.style.top=`${$}pt`,l.style.margin="0"}}anchorNumber(e,n){let t=e.getAttribute(`data-docx-anchor-${n}`);if(t===null)return;let o=Number(t);return Number.isFinite(o)?o:void 0}horizontalAnchorReference(e,n,t,o){let r={start:0,size:n.marginLeft},i={start:n.marginLeft+n.contentWidth,size:n.marginRight};switch(e){case"page":return{start:0,size:n.pageWidth};case"character":return{start:o.staticLeft,size:0};case"leftMargin":return r;case"rightMargin":return i;case"insideMargin":return t%2===1?r:i;case"outsideMargin":return t%2===1?i:r;case"paragraph":return{start:o.paragraphLeft,size:o.paragraphWidth};default:return{start:n.marginLeft,size:n.contentWidth}}}verticalAnchorReference(e,n,t,o){let r={start:0,size:n.marginTop},i={start:n.marginTop+n.contentHeight,size:n.marginBottom};switch(e){case"page":return{start:0,size:n.pageHeight};case"paragraph":return{start:o.paragraphTop,size:o.paragraphHeight};case"line":return{start:o.staticTop,size:o.lineHeight};case"topMargin":return r;case"bottomMargin":return i;case"insideMargin":return t%2===1?r:i;case"outsideMargin":return t%2===1?i:r;default:return{start:n.marginTop,size:n.contentHeight}}}resolveAnchorAxis(e,n,t,o,r){if(o!==void 0)return e.start+o;let i=0;return t==="center"?i=.5:t==="right"||t==="bottom"?i=1:t==="inside"?i=r%2===1?0:1:t==="outside"&&(i=r%2===1?1:0),e.start+(e.size-n)*i}admitPageAllocation(){let e=this.createdPageCount+1;this.pageCountCheckpoint?.(e),this.createdPageCount=e}createPage(e,n,t,o,r,i,c=[],l=0,a,s,d=!1,g=!1){g||this.admitPageAllocation();let m=this.document.createElement("div");if(m.className=`${this.cssPrefix}box`,m.style.width=`${e.pageWidth}pt`,m.style.height=`${e.pageHeight}pt`,m.style.overflow="hidden",m.style.position="relative",this.scale!==1)if(this.view.CSS?.supports("zoom","1"))m.style.zoom=String(this.scale);else{m.style.transform=`scale(${this.scale})`,m.style.transformOrigin="top left";let P=e.pageHeight*(1-this.scale),C=e.pageWidth*(1-this.scale),A=xe(P),X=xe(C);m.style.marginRight=`-${X}px`,m.style.marginBottom=`${this.pageGap-A}px`}m.style.willChange="transform",m.style.contain="layout paint",m.dataset.pageNumber=String(n),m.dataset.sectionIndex=String(t),m.dataset.displayedPageNumber=String(o),d&&(m.dataset.sectionFiller="true"),m.dataset.pageInSection=String(i);let u=this.getPageBands(e,t,i,o),b=d?void 0:this.selectHeader(t,i,o);if(b){let P=this.document.createElement("div");P.className=`${this.cssPrefix}header`,P.dataset.hfType=this.storyKindFor("header",t,i,o),P.style.position="absolute",P.style.top=`${u.headerTop}pt`,P.style.bottom="auto",P.style.left=`${e.marginLeft}pt`,P.style.width=`${e.contentWidth}pt`,P.style.height=`${u.headerHeight}pt`,P.style.overflow="hidden",P.style.boxSizing="border-box",P.style.display="flex",P.style.flexDirection="column",P.style.justifyContent="flex-start";for(let C of Array.from(b.childNodes)){let A=C.cloneNode(!0);A.nodeType===1&&this.makeClonedStoryInert(A),P.appendChild(A)}m.appendChild(P)}let p=u.bodyTop,y=u.bodyHeight,f=this.document.createElement("div");f.className=`${this.cssPrefix}content`,f.style.position="absolute",f.style.top=`${p}pt`,f.style.left=`${e.marginLeft}pt`,f.style.width=`${e.contentWidth}pt`,f.style.height=`${y}pt`,f.style.overflowX="visible",f.style.overflowY="clip";for(let P of r)f.appendChild(P);m.appendChild(f);let x=a&&a.remainingElements.length>0;!d&&(c.length>0||x)&&this.addPageFootnotes(m,c,e,u,l,a,s);let M=[];for(let P of Array.from(m.querySelectorAll("[data-comment-id]"))){if(P.closest(`.${this.cssPrefix}comment-margin`))continue;let C=P.dataset.commentId;C&&this.commentMarginRegistry.has(C)&&!M.includes(C)&&M.push(C)}if(M.length>0){let P=this.document.createElement("aside");P.className=`${this.cssPrefix}comment-margin`,P.style.position="absolute",P.style.top=`${p}pt`,P.style.left=`${e.marginLeft+e.contentWidth+3}pt`,P.style.width=`${Math.max(12,e.marginRight-6)}pt`,P.style.maxHeight=`${y}pt`,P.style.overflow="hidden",P.style.boxSizing="border-box";let C=new Set;for(let A of M){let X=this.commentMarginRegistry.get(A);if(X){let ne=X.dataset.commentId??A;if(C.has(ne))continue;C.add(ne);let oe=X.cloneNode(!0);this.makeClonedMarginCommentInert(oe),P.appendChild(oe)}}m.appendChild(P)}let F=d?void 0:this.selectFooter(t,i,o);if(F){let P=this.document.createElement("div");P.className=`${this.cssPrefix}footer`,P.dataset.hfType=this.storyKindFor("footer",t,i,o),P.style.position="absolute",P.style.top="auto",P.style.bottom=`${e.footerDistance}pt`,P.style.left=`${e.marginLeft}pt`,P.style.width=`${e.contentWidth}pt`,P.style.height=`${u.footerHeight}pt`,P.style.overflow="hidden",P.style.boxSizing="border-box",P.style.display="flex",P.style.flexDirection="column",P.style.justifyContent="flex-end";for(let C of Array.from(F.childNodes)){let A=C.cloneNode(!0);A.nodeType===1&&this.makeClonedStoryInert(A),P.appendChild(A)}m.appendChild(P)}if(this.showPageNumbers&&!d){let P=this.document.createElement("div");P.className=`${this.cssPrefix}number`,P.textContent=String(n),m.appendChild(P)}this.containerElement.appendChild(m),this.positionDrawingAnchors(m,f,e,n);let H=m.querySelector(`.${this.cssPrefix}footnotes`);if(H){let P=I(H.getBoundingClientRect().height);P>0&&(f.style.height=`${Math.max(0,y-P)}pt`)}we(f);let T=m.getBoundingClientRect(),S=m.querySelector(`.${this.cssPrefix}header`),$=m.querySelector(`.${this.cssPrefix}footer`);return Be(f,Math.max(T.top,S?.getBoundingClientRect().bottom??T.top),Math.min(T.bottom,$?.getBoundingClientRect().top??T.bottom,H?.getBoundingClientRect().top??T.bottom),T),{pageNumber:n,sectionIndex:t,dimensions:e,element:m}}};function ct(h,e,n={}){let t=typeof e=="string"?globalThis.document:e.ownerDocument,o=typeof e=="string"?t.getElementById(e):e;if(!o)throw new Error("Container element not found");o.innerHTML=h;let r=n.cssPrefix??"page-",i=o.querySelector("#pagination-staging")||o.querySelector(`.${r}staging`),c=o.querySelector("#pagination-container")||o.querySelector(`.${r}container`);if(!i)throw new Error("Pagination staging element not found. Make sure the HTML was generated with PaginationMode.Paginated");if(!c)throw new Error("Pagination container element not found");return new me(i,c,{...n,fragmentParagraphs:n.fragmentParagraphs??!0}).paginate()}return je(dt);})();
|
|
3
|
+
//# sourceMappingURL=pagination.bundle.js.map
|