docxodus 7.1.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +65 -24
  2. package/dist/docxodus.worker.js +2 -2
  3. package/dist/docxodus.worker.js.map +1 -1
  4. package/dist/editor-headerfooter.d.ts +166 -0
  5. package/dist/editor-headerfooter.d.ts.map +1 -0
  6. package/dist/editor-headerfooter.js +530 -0
  7. package/dist/editor-headerfooter.js.map +1 -0
  8. package/dist/editor-reconcile.d.ts +75 -0
  9. package/dist/editor-reconcile.d.ts.map +1 -0
  10. package/dist/editor-reconcile.js +125 -0
  11. package/dist/editor-reconcile.js.map +1 -0
  12. package/dist/editor.bundle.js +1861 -110
  13. package/dist/editor.d.ts +200 -6
  14. package/dist/editor.d.ts.map +1 -1
  15. package/dist/editor.js +868 -86
  16. package/dist/editor.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -4
  20. package/dist/index.js.map +1 -1
  21. package/dist/page-number-format.d.ts +22 -0
  22. package/dist/page-number-format.d.ts.map +1 -0
  23. package/dist/page-number-format.js +60 -0
  24. package/dist/page-number-format.js.map +1 -0
  25. package/dist/pagination.bundle.js +635 -37
  26. package/dist/pagination.d.ts +119 -0
  27. package/dist/pagination.d.ts.map +1 -1
  28. package/dist/pagination.js +706 -52
  29. package/dist/pagination.js.map +1 -1
  30. package/dist/react.d.ts +3 -1
  31. package/dist/react.d.ts.map +1 -1
  32. package/dist/react.js +6 -4
  33. package/dist/react.js.map +1 -1
  34. package/dist/session.bundle.js +241 -4
  35. package/dist/session.d.ts +160 -6
  36. package/dist/session.d.ts.map +1 -1
  37. package/dist/session.js +202 -4
  38. package/dist/session.js.map +1 -1
  39. package/dist/types.d.ts +238 -14
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/types.js +9 -11
  42. package/dist/types.js.map +1 -1
  43. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  44. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  45. package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
  46. package/dist/wasm/_framework/System.Collections.Immutable.wasm +0 -0
  47. package/dist/wasm/_framework/System.Collections.NonGeneric.wasm +0 -0
  48. package/dist/wasm/_framework/System.Collections.Specialized.wasm +0 -0
  49. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  50. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm +0 -0
  51. package/dist/wasm/_framework/System.ComponentModel.TypeConverter.wasm +0 -0
  52. package/dist/wasm/_framework/System.ComponentModel.wasm +0 -0
  53. package/dist/wasm/_framework/System.Console.wasm +0 -0
  54. package/dist/wasm/_framework/System.Diagnostics.Process.wasm +0 -0
  55. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  56. package/dist/wasm/_framework/System.IO.Pipelines.wasm +0 -0
  57. package/dist/wasm/_framework/System.Linq.Expressions.wasm +0 -0
  58. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  59. package/dist/wasm/_framework/System.Memory.wasm +0 -0
  60. package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
  61. package/dist/wasm/_framework/System.Net.Primitives.wasm +0 -0
  62. package/dist/wasm/_framework/System.ObjectModel.wasm +0 -0
  63. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  64. package/dist/wasm/_framework/System.Private.Uri.wasm +0 -0
  65. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  66. package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
  67. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
  68. package/dist/wasm/_framework/System.Runtime.wasm +0 -0
  69. package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
  70. package/dist/wasm/_framework/System.Text.Encoding.Extensions.wasm +0 -0
  71. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm +0 -0
  72. package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
  73. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  74. package/dist/wasm/_framework/System.Threading.Thread.wasm +0 -0
  75. package/dist/wasm/_framework/System.Threading.wasm +0 -0
  76. package/dist/wasm/_framework/System.Xml.Linq.wasm +0 -0
  77. package/dist/wasm/_framework/System.Xml.ReaderWriter.wasm +0 -0
  78. package/dist/wasm/_framework/System.Xml.XDocument.wasm +0 -0
  79. package/dist/wasm/_framework/System.Xml.XPath.XDocument.wasm +0 -0
  80. package/dist/wasm/_framework/System.Xml.XPath.wasm +0 -0
  81. package/dist/wasm/_framework/System.wasm +0 -0
  82. package/dist/wasm/_framework/dotnet.boot.js +41 -41
  83. package/dist/wasm/_framework/dotnet.js +1 -1
  84. package/dist/wasm/_framework/dotnet.js.map +1 -1
  85. package/dist/wasm/_framework/dotnet.native.js +3 -3
  86. package/dist/wasm/_framework/dotnet.native.js.symbols +2102 -2101
  87. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  88. package/dist/wasm/_framework/dotnet.runtime.js +1 -1
  89. package/dist/wasm/_framework/dotnet.runtime.js.map +1 -1
  90. package/dist/wasm/index.html +2 -1
  91. package/package.json +17 -4
@@ -24,6 +24,42 @@ var DocxodusPagination = (() => {
24
24
  PaginationEngine: () => PaginationEngine,
25
25
  paginateHtml: () => paginateHtml
26
26
  });
27
+
28
+ // src/page-number-format.ts
29
+ var ROMAN_ONES = ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"];
30
+ var ROMAN_TENS = ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"];
31
+ var ROMAN_HUNDREDS = ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"];
32
+ var ROMAN_THOUSANDS = ["", "m", "mm", "mmm"];
33
+ function toRoman(value) {
34
+ if (value <= 0 || value >= 4e3) return String(value);
35
+ 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];
36
+ }
37
+ function toLetter(value) {
38
+ if (value <= 0) return String(value);
39
+ const wrapped = value % 780 === 0 ? 780 : value % 780;
40
+ const repeats = Math.floor((wrapped - 1) / 26) + 1;
41
+ return "abcdefghijklmnopqrstuvwxyz".charAt((wrapped - 1) % 26).repeat(repeats);
42
+ }
43
+ var RENDERERS = {
44
+ // ST_NumberFormat tokens (w:pgNumType/@w:fmt, w:numFmt).
45
+ decimal: (v) => String(v),
46
+ lowerRoman: toRoman,
47
+ upperRoman: (v) => toRoman(v).toUpperCase(),
48
+ lowerLetter: toLetter,
49
+ upperLetter: (v) => toLetter(v).toUpperCase(),
50
+ // Field `\*` general-formatting switch arguments.
51
+ Arabic: (v) => String(v),
52
+ roman: toRoman,
53
+ ROMAN: (v) => toRoman(v).toUpperCase(),
54
+ alphabetic: toLetter,
55
+ ALPHABETIC: (v) => toLetter(v).toUpperCase()
56
+ };
57
+ function formatPageNumber(value, format) {
58
+ const renderer = format ? RENDERERS[format] : void 0;
59
+ return (renderer ?? RENDERERS.decimal)(value);
60
+ }
61
+
62
+ // src/pagination.ts
27
63
  var DEFAULT_PAGE_WIDTH = 612;
28
64
  var DEFAULT_PAGE_HEIGHT = 792;
29
65
  var DEFAULT_MARGIN = 72;
@@ -70,6 +106,8 @@ var DocxodusPagination = (() => {
70
106
  */
71
107
  constructor(staging, container, options = {}) {
72
108
  this.pendingFootnoteContinuation = null;
109
+ /** Per-section `w:pgNumType` (start / format), read off the section wrappers. */
110
+ this.pageNumbering = /* @__PURE__ */ new Map();
73
111
  this.stagingElement = typeof staging === "string" ? document.getElementById(staging) : staging;
74
112
  this.containerElement = typeof container === "string" ? document.getElementById(container) : container;
75
113
  if (!this.stagingElement) {
@@ -82,6 +120,7 @@ var DocxodusPagination = (() => {
82
120
  this.cssPrefix = options.cssPrefix ?? "page-";
83
121
  this.showPageNumbers = options.showPageNumbers ?? true;
84
122
  this.pageGap = options.pageGap ?? 20;
123
+ this.fragmentParagraphs = options.fragmentParagraphs ?? false;
85
124
  this.hfRegistry = /* @__PURE__ */ new Map();
86
125
  this.footnoteRegistry = /* @__PURE__ */ new Map();
87
126
  }
@@ -98,6 +137,7 @@ var DocxodusPagination = (() => {
98
137
  const sections = this.stagingElement.querySelectorAll(
99
138
  "[data-section-index]"
100
139
  );
140
+ this.pageNumbering = this.parsePageNumbering(sections);
101
141
  const sectionsToProcess = sections.length > 0 ? Array.from(sections) : [this.stagingElement];
102
142
  for (const section of sectionsToProcess) {
103
143
  const sectionIndex = parseInt(section.dataset.sectionIndex || "0", 10);
@@ -113,8 +153,62 @@ var DocxodusPagination = (() => {
113
153
  pageNumber += sectionPages.length;
114
154
  }
115
155
  this.stagingElement.style.display = "none";
156
+ this.substitutePageNumberFields(pages.length);
116
157
  return { totalPages: pages.length, pages };
117
158
  }
159
+ /** Read each section's `w:pgNumType` off its wrapper (see {@link SectionPageNumbering}). */
160
+ parsePageNumbering(sections) {
161
+ const map = /* @__PURE__ */ new Map();
162
+ for (const section of Array.from(sections)) {
163
+ const index = parseInt(section.dataset.sectionIndex || "0", 10);
164
+ const rawStart = section.dataset.pageNumStart;
165
+ const start = rawStart === void 0 ? void 0 : parseInt(rawStart, 10);
166
+ map.set(index, {
167
+ start: start !== void 0 && Number.isFinite(start) ? start : void 0,
168
+ format: section.dataset.pageNumFmt
169
+ });
170
+ }
171
+ return map;
172
+ }
173
+ /**
174
+ * Fill in the page-number fields inside every page's cloned header/footer.
175
+ *
176
+ * A header/footer is authored once and cloned onto each page, so a PAGE field's single cached
177
+ * result would otherwise show the same number on every page — the whole reason the converter
178
+ * marks these. `data-field-format` (the field's own `\*` switch) wins over the section's format
179
+ * when present, which is exactly how Word resolves the two.
180
+ *
181
+ * Runs after layout because NUMPAGES cannot be known before the last page exists. The
182
+ * substituted text can therefore be marginally wider than the cached result the header was
183
+ * measured with; the header band clips, so the failure mode is a hair of overflow rather than
184
+ * a layout that disagrees with itself.
185
+ *
186
+ * Scoped to the CLONED header/footer regions on purpose. A page-number field in body text is
187
+ * ordinary run content that the editor may make editable, and committing an edited block writes
188
+ * back whatever text the DOM holds — rewriting it here would mean a body field commits a number
189
+ * the document never contained. Body content is also not cloned, so it does not have the problem
190
+ * this method exists to solve.
191
+ */
192
+ substitutePageNumberFields(totalPages) {
193
+ const boxes = this.containerElement.querySelectorAll(`.${this.cssPrefix}box`);
194
+ for (const box of Array.from(boxes)) {
195
+ const markers = box.querySelectorAll(
196
+ `.${this.cssPrefix}header [data-field], .${this.cssPrefix}footer [data-field]`
197
+ );
198
+ if (markers.length === 0) continue;
199
+ const sectionIndex = parseInt(box.dataset.sectionIndex || "0", 10);
200
+ const pageNumber = parseInt(box.dataset.pageNumber || "1", 10);
201
+ const pageInSection = parseInt(box.dataset.pageInSection || "1", 10);
202
+ const numbering = this.pageNumbering.get(sectionIndex) ?? {};
203
+ const displayed = numbering.start !== void 0 ? numbering.start + pageInSection - 1 : pageNumber;
204
+ for (const marker of Array.from(markers)) {
205
+ const kind = marker.dataset.field;
206
+ if (kind !== "PAGE" && kind !== "NUMPAGES") continue;
207
+ const format = marker.dataset.fieldFormat ?? numbering.format;
208
+ marker.textContent = formatPageNumber(kind === "PAGE" ? displayed : totalPages, format);
209
+ }
210
+ }
211
+ }
118
212
  /**
119
213
  * Measures all content blocks in a section.
120
214
  */
@@ -148,6 +242,393 @@ var DocxodusPagination = (() => {
148
242
  }
149
243
  return blocks;
150
244
  }
245
+ /**
246
+ * Measures one element in the same hidden staging context used for the source blocks.
247
+ * This is intentionally DOM-based: table row heights cannot be inferred from individual
248
+ * rows because wrapping and collapsed borders change the height of a fragment.
249
+ */
250
+ measureElement(element, dims) {
251
+ const measurementHost = document.createElement("div");
252
+ measurementHost.style.position = "absolute";
253
+ measurementHost.style.visibility = "hidden";
254
+ measurementHost.style.left = "-9999px";
255
+ measurementHost.style.width = `${dims.contentWidth}pt`;
256
+ const measuredElement = element.cloneNode(true);
257
+ measurementHost.appendChild(measuredElement);
258
+ this.stagingElement.appendChild(measurementHost);
259
+ const rect = measuredElement.getBoundingClientRect();
260
+ const style = window.getComputedStyle(measuredElement);
261
+ const measured = {
262
+ element,
263
+ heightPt: pxToPt(rect.height),
264
+ marginTopPt: pxToPt(parseFloat(style.marginTop) || 0),
265
+ marginBottomPt: pxToPt(parseFloat(style.marginBottom) || 0),
266
+ keepWithNext: element.dataset.keepWithNext === "true",
267
+ keepLines: element.dataset.keepLines === "true",
268
+ pageBreakBefore: element.dataset.pageBreakBefore === "true",
269
+ isPageBreak: element.dataset.pageBreak === "true" || element.classList.contains(`${this.cssPrefix}break`)
270
+ };
271
+ this.stagingElement.removeChild(measurementHost);
272
+ return measured;
273
+ }
274
+ /**
275
+ * Returns the contiguous keep-with-next chain beginning at a block.
276
+ *
277
+ * A hard page break or a page-break-before directive is stronger than a
278
+ * keep-with-next directive, so it terminates the chain. The caller only
279
+ * keeps a chain together when the whole chain can fit on a fresh page.
280
+ */
281
+ getKeepWithNextChain(blocks, startIndex) {
282
+ const firstBlock = blocks[startIndex];
283
+ if (!firstBlock) return [];
284
+ const chain = [firstBlock];
285
+ let lastIndex = startIndex;
286
+ while (blocks[lastIndex].keepWithNext) {
287
+ const nextBlock = blocks[lastIndex + 1];
288
+ if (!nextBlock || nextBlock.isPageBreak || nextBlock.pageBreakBefore) {
289
+ break;
290
+ }
291
+ chain.push(nextBlock);
292
+ lastIndex++;
293
+ }
294
+ return chain;
295
+ }
296
+ /**
297
+ * Measures the visible body height of a keep-with-next chain using the same
298
+ * collapsed-margin rules as normal block placement. The trailing margin is
299
+ * intentionally excluded, matching the individual block fit check.
300
+ */
301
+ measureKeepWithNextChainBodyHeight(chain, previousMarginBottomPt, isFirstOnPage) {
302
+ const firstBlock = chain[0];
303
+ if (!firstBlock) return 0;
304
+ const firstMarginTop = isFirstOnPage ? firstBlock.marginTopPt : Math.max(firstBlock.marginTopPt, previousMarginBottomPt) - previousMarginBottomPt;
305
+ let bodyHeight = firstMarginTop + firstBlock.heightPt;
306
+ for (let index = 1; index < chain.length; index++) {
307
+ const previousBlock = chain[index - 1];
308
+ const block = chain[index];
309
+ bodyHeight += Math.max(previousBlock.marginBottomPt, block.marginTopPt) + block.heightPt;
310
+ }
311
+ return bodyHeight;
312
+ }
313
+ /**
314
+ * Finds footnote references introduced by a sequence of blocks, preserving
315
+ * document order and excluding references already assigned to the page.
316
+ */
317
+ collectNewFootnoteIds(blocks, existingFootnoteIds) {
318
+ const knownIds = new Set(existingFootnoteIds);
319
+ const newIds = [];
320
+ for (const block of blocks) {
321
+ for (const id of this.extractFootnoteRefs(block.element)) {
322
+ if (!knownIds.has(id)) {
323
+ knownIds.add(id);
324
+ newIds.push(id);
325
+ }
326
+ }
327
+ }
328
+ return newIds;
329
+ }
330
+ /**
331
+ * The shortest body available to a section's first, default, or even page.
332
+ * A row fragment must fit every variant, otherwise a later header/footer could
333
+ * send it through the oversized-block fallback again.
334
+ */
335
+ smallestEffectiveContentHeight(dims, sectionIndex) {
336
+ return Math.min(
337
+ this.getEffectiveHeights(dims, sectionIndex, 1, 1).contentHeight,
338
+ this.getEffectiveHeights(dims, sectionIndex, 2, 1).contentHeight,
339
+ this.getEffectiveHeights(dims, sectionIndex, 2, 2).contentHeight
340
+ );
341
+ }
342
+ /**
343
+ * Builds a clone of a simple table wrapper containing a contiguous run of rows.
344
+ * Complex table features are deliberately rejected by the caller: a split across
345
+ * merged cells, nested tables, or footnotes cannot be made correct by cloning rows.
346
+ */
347
+ createSimpleTableFragment(wrapper, table, body, rows, retainAnchor) {
348
+ const wrapperClone = wrapper.cloneNode(false);
349
+ const tableClone = table.cloneNode(false);
350
+ for (const child of Array.from(table.children)) {
351
+ if (child !== body) {
352
+ tableClone.appendChild(child.cloneNode(true));
353
+ }
354
+ }
355
+ const bodyClone = body.cloneNode(false);
356
+ for (const row of rows) {
357
+ bodyClone.appendChild(row.cloneNode(true));
358
+ }
359
+ tableClone.appendChild(bodyClone);
360
+ if (!retainAnchor) {
361
+ wrapperClone.removeAttribute("data-anchor");
362
+ tableClone.removeAttribute("data-anchor");
363
+ }
364
+ wrapperClone.appendChild(tableClone);
365
+ return wrapperClone;
366
+ }
367
+ /**
368
+ * Splits an oversized, ordinary table at row boundaries. This only participates
369
+ * in the existing oversized-block fallback; unsupported tables keep the previous
370
+ * overflow behavior rather than risking broken table semantics.
371
+ */
372
+ trySplitSimpleOversizedTable(block, dims, sectionIndex) {
373
+ const wrapper = block.element;
374
+ if (wrapper.tagName !== "DIV" || wrapper.children.length !== 1 || block.keepWithNext || block.keepLines || block.pageBreakBefore || block.isPageBreak) {
375
+ return null;
376
+ }
377
+ const table = wrapper.firstElementChild;
378
+ if (!(table instanceof HTMLTableElement)) {
379
+ return null;
380
+ }
381
+ const body = table.tBodies.length === 1 ? table.tBodies[0] : null;
382
+ 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], [colspan], [data-footnote-id]") || wrapper.querySelector("[data-footnote-id]")) {
383
+ return null;
384
+ }
385
+ const rows = Array.from(body.rows);
386
+ const minimumContentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);
387
+ const maximumFragmentHeight = minimumContentHeight - block.marginTopPt - block.marginBottomPt;
388
+ if (maximumFragmentHeight <= 0) {
389
+ return null;
390
+ }
391
+ const groups = [];
392
+ let start = 0;
393
+ while (start < rows.length) {
394
+ let end = start;
395
+ while (end < rows.length) {
396
+ const candidate = this.createSimpleTableFragment(
397
+ wrapper,
398
+ table,
399
+ body,
400
+ rows.slice(start, end + 1),
401
+ start === 0
402
+ );
403
+ const measured = this.measureElement(candidate, dims);
404
+ if (measured.heightPt > maximumFragmentHeight) {
405
+ break;
406
+ }
407
+ end++;
408
+ }
409
+ if (end === start) {
410
+ return null;
411
+ }
412
+ groups.push(rows.slice(start, end));
413
+ start = end;
414
+ }
415
+ if (groups.length < 2) {
416
+ return null;
417
+ }
418
+ const fragments = [];
419
+ for (let index = 0; index < groups.length; index++) {
420
+ const isFirst = index === 0;
421
+ const isLast = index === groups.length - 1;
422
+ const fragment = this.createSimpleTableFragment(
423
+ wrapper,
424
+ table,
425
+ body,
426
+ groups[index],
427
+ isFirst
428
+ );
429
+ if (!isFirst) {
430
+ fragment.style.setProperty("margin-top", "0", "important");
431
+ }
432
+ if (!isLast) {
433
+ fragment.style.setProperty("margin-bottom", "0", "important");
434
+ }
435
+ const measured = this.measureElement(fragment, dims);
436
+ if (measured.heightPt + measured.marginTopPt + measured.marginBottomPt > minimumContentHeight) {
437
+ return null;
438
+ }
439
+ fragments.push({
440
+ ...measured,
441
+ keepWithNext: false,
442
+ keepLines: false,
443
+ pageBreakBefore: false,
444
+ isPageBreak: false
445
+ });
446
+ }
447
+ return fragments;
448
+ }
449
+ /**
450
+ * A DOM endpoint that can finish a paragraph fragment. Endpoints are chosen
451
+ * after whitespace or at a run boundary so the paginator never deliberately
452
+ * cuts through an ordinary word merely to fill a little more of a page.
453
+ */
454
+ paragraphFragmentEndpoints(paragraph) {
455
+ const endpoints = [];
456
+ const walker = document.createTreeWalker(paragraph, NodeFilter.SHOW_TEXT);
457
+ let textNode;
458
+ while (textNode = walker.nextNode()) {
459
+ const text = textNode.data;
460
+ if (text.length === 0) continue;
461
+ const whitespace = /\s+/g;
462
+ let match;
463
+ while ((match = whitespace.exec(text)) !== null) {
464
+ endpoints.push({ node: textNode, offset: match.index + match[0].length });
465
+ }
466
+ if (endpoints.length === 0 || endpoints[endpoints.length - 1].node !== textNode || endpoints[endpoints.length - 1].offset !== text.length) {
467
+ endpoints.push({ node: textNode, offset: text.length });
468
+ }
469
+ }
470
+ return endpoints;
471
+ }
472
+ /**
473
+ * Whether a range contains visible text after ignoring bidi/zero-width marks.
474
+ * Paragraph fragmentation deliberately excludes non-textual descendants, so
475
+ * this is enough to reject empty head or tail fragments.
476
+ */
477
+ hasVisibleFragmentText(fragment) {
478
+ return (fragment.textContent || "").replace(/[\u200B-\u200F\uFEFF]/g, "").trim().length > 0;
479
+ }
480
+ /**
481
+ * Phase one only handles text-like paragraph descendants. Objects, explicit
482
+ * line breaks, list markers, notes, and out-of-flow/inline-block content all
483
+ * require their own line-layout rules and retain the established whole-block
484
+ * fallback instead of risking broken content or duplicate anchors.
485
+ */
486
+ canFragmentParagraph(block) {
487
+ if (!this.fragmentParagraphs || block.element.tagName !== "P") {
488
+ return false;
489
+ }
490
+ const paragraph = block.element;
491
+ if (block.keepWithNext || block.keepLines || block.pageBreakBefore || block.isPageBreak || paragraph.dataset.widowControl === "true" || paragraph.hasAttribute("contenteditable")) {
492
+ return false;
493
+ }
494
+ const unsupportedDescendants = [
495
+ "br",
496
+ "img",
497
+ "picture",
498
+ "svg",
499
+ "math",
500
+ "canvas",
501
+ "video",
502
+ "audio",
503
+ "iframe",
504
+ "object",
505
+ "embed",
506
+ "input",
507
+ "button",
508
+ "select",
509
+ "textarea",
510
+ "table",
511
+ "ol",
512
+ "ul",
513
+ "li",
514
+ "dl",
515
+ "div",
516
+ "p",
517
+ "section",
518
+ "article",
519
+ "aside",
520
+ "figure",
521
+ "fieldset",
522
+ "details",
523
+ "[data-footnote-id]",
524
+ "[data-list-marker]",
525
+ "[data-anchor]",
526
+ "[id]",
527
+ "[contenteditable]"
528
+ ].join(", ");
529
+ if (paragraph.querySelector(unsupportedDescendants)) {
530
+ return false;
531
+ }
532
+ const paragraphStyle = window.getComputedStyle(paragraph);
533
+ if (paragraphStyle.display !== "block" || paragraphStyle.position !== "static" || paragraphStyle.float !== "none" || paragraphStyle.whiteSpace !== "normal" || paragraphStyle.breakBefore !== "auto" || paragraphStyle.breakAfter !== "auto" || paragraphStyle.breakInside === "avoid" || paragraphStyle.pageBreakBefore !== "auto" || paragraphStyle.pageBreakAfter !== "auto" || paragraphStyle.pageBreakInside === "avoid") {
534
+ return false;
535
+ }
536
+ for (const descendant of Array.from(paragraph.querySelectorAll("*"))) {
537
+ const style = window.getComputedStyle(descendant);
538
+ if (style.display !== "inline" || style.position !== "static" || style.float !== "none" || style.whiteSpace !== "normal") {
539
+ return false;
540
+ }
541
+ }
542
+ return true;
543
+ }
544
+ /**
545
+ * Builds one range-cloned paragraph fragment. Only the leading fragment keeps
546
+ * the source paragraph's addressability; continuations must not duplicate an
547
+ * id/data-anchor in the rendered document.
548
+ */
549
+ createParagraphFragment(paragraph, range, retainSourceIdentity, isFinalFragment) {
550
+ const fragment = paragraph.cloneNode(false);
551
+ const contents = range.cloneContents();
552
+ fragment.appendChild(contents);
553
+ if (!retainSourceIdentity) {
554
+ fragment.removeAttribute("id");
555
+ fragment.removeAttribute("data-anchor");
556
+ fragment.style.setProperty("margin-top", "0", "important");
557
+ fragment.style.setProperty("text-indent", "0", "important");
558
+ }
559
+ if (!isFinalFragment) {
560
+ fragment.style.setProperty("margin-bottom", "0", "important");
561
+ }
562
+ return fragment;
563
+ }
564
+ /**
565
+ * Splits a simple paragraph at the largest DOM Range endpoint that fits the
566
+ * currently available body space. The caller then processes the tail normally,
567
+ * allowing it to fragment again on later pages when necessary.
568
+ */
569
+ tryFragmentParagraph(block, dims, availableHeightPt, effectiveMarginTopPt) {
570
+ if (!this.canFragmentParagraph(block) || availableHeightPt <= effectiveMarginTopPt) {
571
+ return null;
572
+ }
573
+ const paragraph = block.element;
574
+ const endpoints = this.paragraphFragmentEndpoints(paragraph);
575
+ if (endpoints.length < 2) {
576
+ return null;
577
+ }
578
+ let low = 0;
579
+ let high = endpoints.length - 2;
580
+ let best = null;
581
+ while (low <= high) {
582
+ const middle = Math.floor((low + high) / 2);
583
+ const endpoint = endpoints[middle];
584
+ const headRange = document.createRange();
585
+ headRange.setStart(paragraph, 0);
586
+ headRange.setEnd(endpoint.node, endpoint.offset);
587
+ const headContents = headRange.cloneContents();
588
+ if (!this.hasVisibleFragmentText(headContents)) {
589
+ low = middle + 1;
590
+ continue;
591
+ }
592
+ const head = this.createParagraphFragment(paragraph, headRange, true, false);
593
+ const measured = this.measureElement(head, dims);
594
+ if (effectiveMarginTopPt + measured.heightPt <= availableHeightPt) {
595
+ best = { endpoint, element: head, measured };
596
+ low = middle + 1;
597
+ } else {
598
+ high = middle - 1;
599
+ }
600
+ }
601
+ if (!best) {
602
+ return null;
603
+ }
604
+ const tailRange = document.createRange();
605
+ tailRange.setStart(best.endpoint.node, best.endpoint.offset);
606
+ tailRange.setEnd(paragraph, paragraph.childNodes.length);
607
+ const tailContents = tailRange.cloneContents();
608
+ if (!this.hasVisibleFragmentText(tailContents)) {
609
+ return null;
610
+ }
611
+ const tail = this.createParagraphFragment(paragraph, tailRange, false, true);
612
+ const tailMeasured = this.measureElement(tail, dims);
613
+ return [
614
+ {
615
+ ...best.measured,
616
+ element: best.element,
617
+ keepWithNext: false,
618
+ keepLines: false,
619
+ pageBreakBefore: false,
620
+ isPageBreak: false
621
+ },
622
+ {
623
+ ...tailMeasured,
624
+ element: tail,
625
+ keepWithNext: false,
626
+ keepLines: false,
627
+ pageBreakBefore: false,
628
+ isPageBreak: false
629
+ }
630
+ ];
631
+ }
151
632
  /**
152
633
  * Parses the header/footer registry from the staging element.
153
634
  * Also measures heights during parsing for lazy-loading compatibility.
@@ -251,6 +732,7 @@ var DocxodusPagination = (() => {
251
732
  measureContainer.style.visibility = "hidden";
252
733
  measureContainer.style.width = `${contentWidth}pt`;
253
734
  measureContainer.style.left = "-9999px";
735
+ measureContainer.className = this.cssPrefix + "footnotes";
254
736
  const hr = document.createElement("hr");
255
737
  measureContainer.appendChild(hr);
256
738
  if (hasContinuation) {
@@ -285,6 +767,7 @@ var DocxodusPagination = (() => {
285
767
  measureContainer.style.visibility = "hidden";
286
768
  measureContainer.style.width = `${contentWidth}pt`;
287
769
  measureContainer.style.left = "-9999px";
770
+ measureContainer.className = this.cssPrefix + "footnotes";
288
771
  const hr = document.createElement("hr");
289
772
  measureContainer.appendChild(hr);
290
773
  for (const el of continuation.remainingElements) {
@@ -303,11 +786,17 @@ var DocxodusPagination = (() => {
303
786
  splitFootnoteToFit(footnoteElement, availableHeightPt, contentWidth) {
304
787
  const footnoteContent = footnoteElement.querySelector(".footnote-content");
305
788
  if (!footnoteContent) {
306
- return { fits: [footnoteElement.cloneNode(true)], overflow: [] };
789
+ return {
790
+ fits: Array.from(footnoteElement.children).map((el) => el.cloneNode(true)),
791
+ overflow: []
792
+ };
307
793
  }
308
794
  const children = Array.from(footnoteContent.children);
309
795
  if (children.length <= 1) {
310
- return { fits: [footnoteElement.cloneNode(true)], overflow: [] };
796
+ return {
797
+ fits: children.map((el) => el.cloneNode(true)),
798
+ overflow: []
799
+ };
311
800
  }
312
801
  const fits = [];
313
802
  const overflow = [];
@@ -317,6 +806,7 @@ var DocxodusPagination = (() => {
317
806
  hrMeasure.style.visibility = "hidden";
318
807
  hrMeasure.style.width = `${contentWidth}pt`;
319
808
  hrMeasure.style.left = "-9999px";
809
+ hrMeasure.className = this.cssPrefix + "footnotes";
320
810
  const hr = document.createElement("hr");
321
811
  hrMeasure.appendChild(hr);
322
812
  this.stagingElement.appendChild(hrMeasure);
@@ -331,6 +821,7 @@ var DocxodusPagination = (() => {
331
821
  measureContainer.style.visibility = "hidden";
332
822
  measureContainer.style.width = `${contentWidth}pt`;
333
823
  measureContainer.style.left = "-9999px";
824
+ measureContainer.className = this.cssPrefix + "footnotes";
334
825
  measureContainer.appendChild(child.cloneNode(true));
335
826
  this.stagingElement.appendChild(measureContainer);
336
827
  const childHeight = pxToPt(measureContainer.getBoundingClientRect().height);
@@ -358,6 +849,7 @@ var DocxodusPagination = (() => {
358
849
  measureContainer.style.visibility = "hidden";
359
850
  measureContainer.style.width = `${contentWidth}pt`;
360
851
  measureContainer.style.left = "-9999px";
852
+ measureContainer.className = this.cssPrefix + "footnotes";
361
853
  measureContainer.appendChild(footnote.cloneNode(true));
362
854
  this.stagingElement.appendChild(measureContainer);
363
855
  const rect = measureContainer.getBoundingClientRect();
@@ -541,12 +1033,14 @@ var DocxodusPagination = (() => {
541
1033
  let currentFootnoteHeight = 0;
542
1034
  let currentContinuation = this.pendingFootnoteContinuation;
543
1035
  let nextPageContinuation = null;
1036
+ let deferredFootnoteIds = [];
544
1037
  let currentPartialFootnotes = [];
545
1038
  if (currentContinuation && currentContinuation.remainingElements.length > 0) {
546
1039
  currentFootnoteHeight = this.measureContinuationHeight(currentContinuation, dims.contentWidth);
547
1040
  }
548
1041
  const finishPage = () => {
549
- if (currentContent.length === 0 && !currentContinuation) return;
1042
+ const hasCurrentContinuation = (currentContinuation?.remainingElements.length ?? 0) > 0;
1043
+ if (currentContent.length === 0 && !hasCurrentContinuation) return;
550
1044
  const page = this.createPage(
551
1045
  dims,
552
1046
  pageNumber,
@@ -570,15 +1064,25 @@ var DocxodusPagination = (() => {
570
1064
  currentPartialFootnotes = [];
571
1065
  currentContinuation = nextPageContinuation;
572
1066
  nextPageContinuation = null;
1067
+ if (deferredFootnoteIds.length > 0) {
1068
+ currentFootnoteIds = [...deferredFootnoteIds];
1069
+ deferredFootnoteIds = [];
1070
+ }
573
1071
  if (currentContinuation && currentContinuation.remainingElements.length > 0) {
574
1072
  currentFootnoteHeight = this.measureContinuationHeight(currentContinuation, dims.contentWidth);
575
1073
  } else {
576
1074
  currentFootnoteHeight = 0;
577
1075
  }
1076
+ if (currentFootnoteIds.length > 0) {
1077
+ currentFootnoteHeight += this.measureFootnotesHeight(
1078
+ currentFootnoteIds,
1079
+ dims.contentWidth,
1080
+ null
1081
+ );
1082
+ }
578
1083
  };
579
1084
  for (let i = 0; i < blocks.length; i++) {
580
1085
  const block = blocks[i];
581
- const nextBlock = blocks[i + 1];
582
1086
  if (block.isPageBreak) {
583
1087
  finishPage();
584
1088
  continue;
@@ -586,8 +1090,59 @@ var DocxodusPagination = (() => {
586
1090
  if (block.pageBreakBefore && currentContent.length > 0) {
587
1091
  finishPage();
588
1092
  }
589
- const blockFootnoteIds = this.extractFootnoteRefs(block.element);
590
- const newFootnoteIds = blockFootnoteIds.filter((id) => !currentFootnoteIds.includes(id));
1093
+ const previousBlock = blocks[i - 1];
1094
+ const startsKeepChain = !previousBlock || !previousBlock.keepWithNext || previousBlock.isPageBreak || block.pageBreakBefore;
1095
+ const pageHasOccupiedSpace = currentContent.length > 0 || currentFootnoteHeight > 0;
1096
+ if (pageHasOccupiedSpace && block.keepWithNext && startsKeepChain) {
1097
+ const keepChain = this.getKeepWithNextChain(blocks, i);
1098
+ if (keepChain.length > 1) {
1099
+ const newChainFootnoteIds = this.collectNewFootnoteIds(
1100
+ keepChain,
1101
+ currentFootnoteIds
1102
+ );
1103
+ let additionalChainFootnoteHeight = 0;
1104
+ if (newChainFootnoteIds.length > 0 && this.footnoteRegistry.size > 0) {
1105
+ const totalChainFootnoteHeight = this.measureFootnotesHeight(
1106
+ [...currentFootnoteIds, ...newChainFootnoteIds],
1107
+ dims.contentWidth,
1108
+ currentContinuation
1109
+ );
1110
+ additionalChainFootnoteHeight = Math.max(
1111
+ 0,
1112
+ totalChainFootnoteHeight - currentFootnoteHeight
1113
+ );
1114
+ }
1115
+ const currentChainHeight = this.measureKeepWithNextChainBodyHeight(
1116
+ keepChain,
1117
+ prevMarginBottomPt,
1118
+ currentContent.length === 0
1119
+ ) + additionalChainFootnoteHeight;
1120
+ const currentAvailableHeight = remainingHeight - currentFootnoteHeight;
1121
+ if (currentChainHeight > currentAvailableHeight) {
1122
+ const nextPageHeights = this.getEffectiveHeights(
1123
+ dims,
1124
+ sectionIndex,
1125
+ pageInSection + 1,
1126
+ pageNumber + 1
1127
+ );
1128
+ const freshChainBodyHeight = this.measureKeepWithNextChainBodyHeight(
1129
+ keepChain,
1130
+ 0,
1131
+ true
1132
+ );
1133
+ const freshChainFootnoteHeight = this.measureFootnotesHeight(
1134
+ newChainFootnoteIds,
1135
+ dims.contentWidth,
1136
+ nextPageContinuation
1137
+ );
1138
+ if (freshChainBodyHeight + freshChainFootnoteHeight <= nextPageHeights.contentHeight) {
1139
+ finishPage();
1140
+ }
1141
+ }
1142
+ }
1143
+ }
1144
+ const allBlockFootnoteIds = this.extractFootnoteRefs(block.element);
1145
+ const newFootnoteIds = this.collectNewFootnoteIds([block], currentFootnoteIds);
591
1146
  let additionalFootnoteHeight = 0;
592
1147
  if (newFootnoteIds.length > 0 && this.footnoteRegistry.size > 0) {
593
1148
  const combinedFootnoteIds = [...currentFootnoteIds, ...newFootnoteIds];
@@ -604,15 +1159,23 @@ var DocxodusPagination = (() => {
604
1159
  effectiveMarginTop = Math.max(block.marginTopPt, prevMarginBottomPt) - prevMarginBottomPt;
605
1160
  }
606
1161
  const blockSpace = effectiveMarginTop + block.heightPt + additionalFootnoteHeight;
607
- let neededHeight = blockSpace;
608
- if (block.keepWithNext && nextBlock && !nextBlock.isPageBreak) {
609
- const collapsedMargin = Math.max(block.marginBottomPt, nextBlock.marginTopPt);
610
- neededHeight = effectiveMarginTop + block.heightPt + collapsedMargin + nextBlock.heightPt + additionalFootnoteHeight;
611
- }
612
1162
  const effectiveRemainingHeight = remainingHeight - currentFootnoteHeight;
613
1163
  const bodyContentUsed = effectiveContentHeight - remainingHeight;
614
1164
  const maxFootnoteArea = effectiveContentHeight * MAX_FOOTNOTE_AREA_RATIO;
615
1165
  const maxFootnoteExpansion = Math.max(0, maxFootnoteArea - currentFootnoteHeight);
1166
+ if (blockSpace > effectiveRemainingHeight) {
1167
+ const paragraphFragments = this.tryFragmentParagraph(
1168
+ block,
1169
+ dims,
1170
+ effectiveRemainingHeight,
1171
+ effectiveMarginTop
1172
+ );
1173
+ if (paragraphFragments) {
1174
+ blocks.splice(i, 1, ...paragraphFragments);
1175
+ i--;
1176
+ continue;
1177
+ }
1178
+ }
616
1179
  if (blockSpace <= effectiveRemainingHeight) {
617
1180
  currentContent.push(block.element.cloneNode(true));
618
1181
  remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
@@ -654,28 +1217,18 @@ var DocxodusPagination = (() => {
654
1217
  footnoteId,
655
1218
  fittingElements: fits
656
1219
  });
657
- nextPageContinuation = {
658
- footnoteId,
659
- remainingElements: overflow
660
- };
1220
+ if (overflow.length > 0) {
1221
+ nextPageContinuation = {
1222
+ footnoteId,
1223
+ remainingElements: overflow
1224
+ };
1225
+ }
661
1226
  currentFootnoteHeight = availableForFootnotes;
662
1227
  } else {
663
- nextPageContinuation = {
664
- footnoteId,
665
- remainingElements: Array.from(footnote.querySelectorAll(".footnote-content > *")).map((el) => el.cloneNode(true))
666
- };
667
- if (nextPageContinuation.remainingElements.length === 0) {
668
- nextPageContinuation.remainingElements = [footnote.cloneNode(true)];
669
- }
1228
+ deferredFootnoteIds.push(footnoteId);
670
1229
  }
671
1230
  } else {
672
- nextPageContinuation = {
673
- footnoteId,
674
- remainingElements: Array.from(footnote.querySelectorAll(".footnote-content > *")).map((el) => el.cloneNode(true))
675
- };
676
- if (nextPageContinuation.remainingElements.length === 0) {
677
- nextPageContinuation.remainingElements = [footnote.cloneNode(true)];
678
- }
1231
+ deferredFootnoteIds.push(footnoteId);
679
1232
  }
680
1233
  }
681
1234
  }
@@ -690,30 +1243,39 @@ var DocxodusPagination = (() => {
690
1243
  currentFootnoteHeight = expandedFootnoteSpace;
691
1244
  } else {
692
1245
  finishPage();
693
- const newPageFootnoteHeight = blockFootnoteIds.length > 0 ? this.measureFootnotesHeight(blockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
1246
+ const newPageFootnoteHeight = allBlockFootnoteIds.length > 0 ? this.measureFootnotesHeight(allBlockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
694
1247
  const newPageSpace = block.marginTopPt + block.heightPt + block.marginBottomPt;
695
1248
  currentContent.push(block.element.cloneNode(true));
696
1249
  remainingHeight = effectiveContentHeight - newPageSpace;
697
1250
  prevMarginBottomPt = block.marginBottomPt;
698
- currentFootnoteIds = [...blockFootnoteIds];
1251
+ currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
699
1252
  currentFootnoteHeight = newPageFootnoteHeight;
700
1253
  }
701
1254
  } else {
702
1255
  finishPage();
703
- const newPageFootnoteHeight = blockFootnoteIds.length > 0 ? this.measureFootnotesHeight(blockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
1256
+ const newPageFootnoteHeight = allBlockFootnoteIds.length > 0 ? this.measureFootnotesHeight(allBlockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
704
1257
  const newPageSpace = block.marginTopPt + block.heightPt + block.marginBottomPt;
705
1258
  currentContent.push(block.element.cloneNode(true));
706
1259
  remainingHeight = effectiveContentHeight - newPageSpace;
707
1260
  prevMarginBottomPt = block.marginBottomPt;
708
- currentFootnoteIds = [...blockFootnoteIds];
1261
+ currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
709
1262
  currentFootnoteHeight = newPageFootnoteHeight;
710
1263
  }
711
1264
  } else {
1265
+ const tableFragments = this.trySplitSimpleOversizedTable(block, dims, sectionIndex);
1266
+ if (tableFragments) {
1267
+ if (currentContent.length > 0 || currentContinuation) {
1268
+ finishPage();
1269
+ }
1270
+ blocks.splice(i, 1, ...tableFragments);
1271
+ i--;
1272
+ continue;
1273
+ }
712
1274
  if (currentContent.length > 0) {
713
1275
  finishPage();
714
1276
  }
715
1277
  currentContent.push(block.element.cloneNode(true));
716
- currentFootnoteIds = [...blockFootnoteIds];
1278
+ currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
717
1279
  finishPage();
718
1280
  }
719
1281
  }
@@ -721,6 +1283,27 @@ var DocxodusPagination = (() => {
721
1283
  this.pendingFootnoteContinuation = nextPageContinuation;
722
1284
  return pages;
723
1285
  }
1286
+ /**
1287
+ * Strip block addressing from a header/footer node cloned into a page box.
1288
+ *
1289
+ * A running story is authored ONCE and cloned onto every page, so the clones all carry the same
1290
+ * `data-anchor` — on this document, 42 page boxes claiming one footer paragraph. Left editable,
1291
+ * committing any one of them writes back through that single shared anchor, and the per-page
1292
+ * page-number substitution makes it worse: each clone shows a DIFFERENT number, so a commit
1293
+ * writes that page's number into the story as literal text and destroys the PAGE field.
1294
+ *
1295
+ * Page-box header/footer content is presentation. The docked editing bands
1296
+ * (`editor-headerfooter.ts`) are the addressable affordance, and they exist precisely because a
1297
+ * cloned node cannot be uniquely addressed.
1298
+ */
1299
+ makeClonedStoryInert(root) {
1300
+ const nodes = [root, ...Array.from(root.querySelectorAll("*"))];
1301
+ for (const el of nodes) {
1302
+ el.removeAttribute("data-anchor");
1303
+ el.removeAttribute("data-committed-text");
1304
+ if (el.getAttribute("contenteditable") !== null) el.setAttribute("contenteditable", "false");
1305
+ }
1306
+ }
724
1307
  /**
725
1308
  * Creates a page container element.
726
1309
  */
@@ -746,6 +1329,7 @@ var DocxodusPagination = (() => {
746
1329
  pageBox.style.contain = "layout paint";
747
1330
  pageBox.dataset.pageNumber = String(pageNumber);
748
1331
  pageBox.dataset.sectionIndex = String(sectionIndex);
1332
+ pageBox.dataset.pageInSection = String(pageInSection);
749
1333
  const effectiveHeights = this.getEffectiveHeights(dims, sectionIndex, pageInSection, pageNumber);
750
1334
  const headerSource = this.selectHeader(sectionIndex, pageInSection, pageNumber);
751
1335
  if (headerSource) {
@@ -763,7 +1347,9 @@ var DocxodusPagination = (() => {
763
1347
  headerDiv.style.justifyContent = "flex-end";
764
1348
  headerDiv.style.paddingBottom = "4pt";
765
1349
  for (const child of Array.from(headerSource.childNodes)) {
766
- headerDiv.appendChild(child.cloneNode(true));
1350
+ const clonedheaderDiv = child.cloneNode(true);
1351
+ if (clonedheaderDiv.nodeType === 1) this.makeClonedStoryInert(clonedheaderDiv);
1352
+ headerDiv.appendChild(clonedheaderDiv);
767
1353
  }
768
1354
  pageBox.appendChild(headerDiv);
769
1355
  }
@@ -801,7 +1387,9 @@ var DocxodusPagination = (() => {
801
1387
  footerDiv.style.justifyContent = "flex-start";
802
1388
  footerDiv.style.paddingTop = "4pt";
803
1389
  for (const child of Array.from(footerSource.childNodes)) {
804
- footerDiv.appendChild(child.cloneNode(true));
1390
+ const clonedfooterDiv = child.cloneNode(true);
1391
+ if (clonedfooterDiv.nodeType === 1) this.makeClonedStoryInert(clonedfooterDiv);
1392
+ footerDiv.appendChild(clonedfooterDiv);
805
1393
  }
806
1394
  pageBox.appendChild(footerDiv);
807
1395
  }
@@ -812,6 +1400,13 @@ var DocxodusPagination = (() => {
812
1400
  pageBox.appendChild(pageNum);
813
1401
  }
814
1402
  this.containerElement.appendChild(pageBox);
1403
+ const notesEl = pageBox.querySelector(`.${this.cssPrefix}footnotes`);
1404
+ if (notesEl) {
1405
+ const notesHeightPt = pxToPt(notesEl.getBoundingClientRect().height);
1406
+ if (notesHeightPt > 0) {
1407
+ contentArea.style.height = `${Math.max(0, contentAreaHeight - notesHeightPt)}pt`;
1408
+ }
1409
+ }
815
1410
  return {
816
1411
  pageNumber,
817
1412
  sectionIndex,
@@ -837,7 +1432,10 @@ var DocxodusPagination = (() => {
837
1432
  if (!pageContainer) {
838
1433
  throw new Error("Pagination container element not found");
839
1434
  }
840
- const engine = new PaginationEngine(staging, pageContainer, options);
1435
+ const engine = new PaginationEngine(staging, pageContainer, {
1436
+ ...options,
1437
+ fragmentParagraphs: options.fragmentParagraphs ?? true
1438
+ });
841
1439
  return engine.paginate();
842
1440
  }
843
1441
  return __toCommonJS(pagination_exports);