docxodus 9.7.0 → 9.9.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 (101) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +0 -1
  3. package/dist/editor.bundle.js +454 -99
  4. package/dist/editor.d.ts +4 -4
  5. package/dist/editor.d.ts.map +1 -1
  6. package/dist/editor.js +55 -8
  7. package/dist/editor.js.map +1 -1
  8. package/dist/embed.bundle.js +454 -99
  9. package/dist/embed.iife.js +454 -99
  10. package/dist/page-geometry.d.ts +50 -3
  11. package/dist/page-geometry.d.ts.map +1 -1
  12. package/dist/page-geometry.js +63 -6
  13. package/dist/page-geometry.js.map +1 -1
  14. package/dist/pagination.bundle.js +416 -91
  15. package/dist/pagination.d.ts +61 -8
  16. package/dist/pagination.d.ts.map +1 -1
  17. package/dist/pagination.js +353 -105
  18. package/dist/pagination.js.map +1 -1
  19. package/dist/ribbon-chrome.d.ts +1 -1
  20. package/dist/ribbon-chrome.d.ts.map +1 -1
  21. package/dist/ribbon-chrome.js +1 -0
  22. package/dist/ribbon-chrome.js.map +1 -1
  23. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  24. package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
  25. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  26. package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
  27. package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
  28. package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
  29. package/dist/wasm/_framework/System.Collections.Immutable.wasm +0 -0
  30. package/dist/wasm/_framework/System.Collections.Immutable.wasm.br +0 -0
  31. package/dist/wasm/_framework/System.Collections.NonGeneric.wasm +0 -0
  32. package/dist/wasm/_framework/System.Collections.NonGeneric.wasm.br +0 -0
  33. package/dist/wasm/_framework/System.Collections.Specialized.wasm +0 -0
  34. package/dist/wasm/_framework/System.Collections.Specialized.wasm.br +0 -0
  35. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  36. package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
  37. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm +0 -0
  38. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm.br +0 -0
  39. package/dist/wasm/_framework/System.ComponentModel.TypeConverter.wasm +0 -0
  40. package/dist/wasm/_framework/System.ComponentModel.TypeConverter.wasm.br +0 -0
  41. package/dist/wasm/_framework/System.ComponentModel.wasm +0 -0
  42. package/dist/wasm/_framework/System.ComponentModel.wasm.br +0 -0
  43. package/dist/wasm/_framework/System.Console.wasm +0 -0
  44. package/dist/wasm/_framework/System.Console.wasm.br +0 -0
  45. package/dist/wasm/_framework/System.Diagnostics.Process.wasm +0 -0
  46. package/dist/wasm/_framework/System.Diagnostics.Process.wasm.br +0 -0
  47. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  48. package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
  49. package/dist/wasm/_framework/System.IO.Pipelines.wasm +0 -0
  50. package/dist/wasm/_framework/System.IO.Pipelines.wasm.br +0 -0
  51. package/dist/wasm/_framework/System.Linq.Expressions.wasm +0 -0
  52. package/dist/wasm/_framework/System.Linq.Expressions.wasm.br +0 -0
  53. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  54. package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
  55. package/dist/wasm/_framework/System.Memory.wasm +0 -0
  56. package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
  57. package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
  58. package/dist/wasm/_framework/System.Net.Http.wasm.br +0 -0
  59. package/dist/wasm/_framework/System.Net.Primitives.wasm +0 -0
  60. package/dist/wasm/_framework/System.Net.Primitives.wasm.br +0 -0
  61. package/dist/wasm/_framework/System.ObjectModel.wasm +0 -0
  62. package/dist/wasm/_framework/System.ObjectModel.wasm.br +0 -0
  63. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  64. package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
  65. package/dist/wasm/_framework/System.Private.Uri.wasm +0 -0
  66. package/dist/wasm/_framework/System.Private.Uri.wasm.br +0 -0
  67. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  68. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
  69. package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
  70. package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
  71. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
  72. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
  73. package/dist/wasm/_framework/System.Runtime.wasm +0 -0
  74. package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
  75. package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
  76. package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
  77. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm +0 -0
  78. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm.br +0 -0
  79. package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
  80. package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
  81. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  82. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
  83. package/dist/wasm/_framework/System.Xml.Linq.wasm +0 -0
  84. package/dist/wasm/_framework/System.Xml.Linq.wasm.br +0 -0
  85. package/dist/wasm/_framework/System.Xml.XDocument.wasm +0 -0
  86. package/dist/wasm/_framework/System.Xml.XDocument.wasm.br +0 -0
  87. package/dist/wasm/_framework/System.wasm +0 -0
  88. package/dist/wasm/_framework/System.wasm.br +0 -0
  89. package/dist/wasm/_framework/dotnet.boot.js +35 -35
  90. package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
  91. package/dist/wasm/_framework/dotnet.js +1 -1
  92. package/dist/wasm/_framework/dotnet.js.br +0 -0
  93. package/dist/wasm/_framework/dotnet.native.js +3 -3
  94. package/dist/wasm/_framework/dotnet.native.js.br +0 -0
  95. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  96. package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
  97. package/dist/wasm/_framework/dotnet.runtime.js +1 -1
  98. package/dist/wasm/_framework/dotnet.runtime.js.br +0 -0
  99. package/package.json +14 -5
  100. package/dist/wasm/index.html +0 -238
  101. package/dist/wasm/main.js +0 -26
@@ -63,7 +63,7 @@ var DocxodusPagination = (() => {
63
63
  var DEFAULT_PAGE_WIDTH = 612;
64
64
  var DEFAULT_PAGE_HEIGHT = 792;
65
65
  var DEFAULT_MARGIN = 72;
66
- var DEFAULT_HEADER_FOOTER_HEIGHT = 36;
66
+ var DEFAULT_HEADER_FOOTER_DISTANCE = 36;
67
67
  function pxToPt(px) {
68
68
  return px * 0.75;
69
69
  }
@@ -79,8 +79,8 @@ var DocxodusPagination = (() => {
79
79
  const marginRight = parseFloat(section.dataset.marginRight || "") || DEFAULT_MARGIN;
80
80
  const marginBottom = parseFloat(section.dataset.marginBottom || "") || DEFAULT_MARGIN;
81
81
  const marginLeft = parseFloat(section.dataset.marginLeft || "") || DEFAULT_MARGIN;
82
- const headerHeight = parseFloat(section.dataset.headerHeight || "") || DEFAULT_HEADER_FOOTER_HEIGHT;
83
- const footerHeight = parseFloat(section.dataset.footerHeight || "") || DEFAULT_HEADER_FOOTER_HEIGHT;
82
+ const headerDistance = parseFloat(section.dataset.headerHeight || "") || DEFAULT_HEADER_FOOTER_DISTANCE;
83
+ const footerDistance = parseFloat(section.dataset.footerHeight || "") || DEFAULT_HEADER_FOOTER_DISTANCE;
84
84
  return {
85
85
  pageWidth,
86
86
  pageHeight,
@@ -90,12 +90,44 @@ var DocxodusPagination = (() => {
90
90
  marginRight,
91
91
  marginBottom,
92
92
  marginLeft,
93
- headerHeight,
94
- footerHeight
93
+ headerDistance,
94
+ footerDistance,
95
+ headerHeight: headerDistance,
96
+ footerHeight: footerDistance
97
+ };
98
+ }
99
+ var MIN_BODY_BAND_PT = 12;
100
+ function resolvePageBands(dims, headerContentHeight, footerContentHeight) {
101
+ const headerTop = dims.headerDistance;
102
+ const footerBottom = dims.pageHeight - dims.footerDistance;
103
+ const headerReach = headerContentHeight > 0 ? headerTop + headerContentHeight : 0;
104
+ const footerReach = footerContentHeight > 0 ? footerBottom - footerContentHeight : dims.pageHeight;
105
+ let bodyTop = Math.max(dims.marginTop, headerReach);
106
+ let bodyBottom = Math.min(dims.pageHeight - dims.marginBottom, footerReach);
107
+ const shortfall = MIN_BODY_BAND_PT - (bodyBottom - bodyTop);
108
+ if (shortfall > 0) {
109
+ const headerGrowth = bodyTop - dims.marginTop;
110
+ const footerGrowth = dims.pageHeight - dims.marginBottom - bodyBottom;
111
+ const growth = headerGrowth + footerGrowth;
112
+ if (growth > 0) {
113
+ bodyTop -= shortfall * headerGrowth / growth;
114
+ bodyBottom += shortfall * footerGrowth / growth;
115
+ }
116
+ }
117
+ return {
118
+ headerTop,
119
+ headerHeight: Math.max(0, Math.min(headerContentHeight, bodyTop - headerTop)),
120
+ bodyTop,
121
+ bodyHeight: Math.max(0, bodyBottom - bodyTop),
122
+ footerTop: footerBottom - Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom)),
123
+ footerHeight: Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom))
95
124
  };
96
125
  }
97
126
 
98
127
  // src/pagination.ts
128
+ function samePageBox(a, b) {
129
+ 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;
130
+ }
99
131
  var MAX_FOOTNOTE_AREA_RATIO = 0.6;
100
132
  var MIN_BODY_CONTENT_HEIGHT = 72;
101
133
  var PaginationEngine = class {
@@ -141,16 +173,43 @@ var DocxodusPagination = (() => {
141
173
  );
142
174
  this.pageNumbering = this.parsePageNumbering(sections);
143
175
  const sectionsToProcess = sections.length > 0 ? Array.from(sections) : [this.stagingElement];
176
+ const runs = [];
144
177
  for (const section of sectionsToProcess) {
145
- const sectionIndex = parseInt(section.dataset.sectionIndex || "0", 10);
146
178
  const dims = parseSectionDimensions(section);
179
+ const previous = runs[runs.length - 1];
180
+ if (previous && section.dataset.sectionType === "continuous" && samePageBox(previous.dims, dims)) {
181
+ previous.sections.push(section);
182
+ } else {
183
+ runs.push({
184
+ sections: [section],
185
+ sectionIndex: parseInt(section.dataset.sectionIndex || "0", 10),
186
+ dims
187
+ });
188
+ }
189
+ }
190
+ for (const run of runs) {
147
191
  this.stagingElement.style.visibility = "hidden";
148
192
  this.stagingElement.style.position = "absolute";
149
193
  this.stagingElement.style.left = "-9999px";
150
194
  this.stagingElement.style.display = "block";
151
- section.style.width = `${dims.contentWidth}pt`;
152
- const blocks = this.measureBlocks(section, dims);
153
- const sectionPages = this.flowToPages(blocks, dims, pageNumber, sectionIndex);
195
+ const blocks = [];
196
+ for (const section of run.sections) {
197
+ section.style.width = `${run.dims.contentWidth}pt`;
198
+ const columnCount = parseInt(section.dataset.cols || "1", 10);
199
+ if (columnCount > 1) {
200
+ const gap = parseFloat(section.dataset.colGap || "");
201
+ blocks.push(...this.buildColumnBlocks(
202
+ section,
203
+ run.dims,
204
+ run.sectionIndex,
205
+ columnCount,
206
+ Number.isFinite(gap) ? gap : 36
207
+ ));
208
+ } else {
209
+ blocks.push(...this.measureBlocks(section, run.dims));
210
+ }
211
+ }
212
+ const sectionPages = this.flowToPages(blocks, run.dims, pageNumber, run.sectionIndex);
154
213
  pages.push(...sectionPages);
155
214
  pageNumber += sectionPages.length;
156
215
  }
@@ -239,8 +298,67 @@ var DocxodusPagination = (() => {
239
298
  keepWithNext: child.dataset.keepWithNext === "true",
240
299
  keepLines: child.dataset.keepLines === "true",
241
300
  pageBreakBefore: child.dataset.pageBreakBefore === "true",
242
- isPageBreak
301
+ isPageBreak,
302
+ isWordParagraph: this.isWordParagraphElement(child)
303
+ });
304
+ }
305
+ return blocks;
306
+ }
307
+ /**
308
+ * Flows a multi-column (`w:cols`) section's children into CSS-multicol container
309
+ * blocks. Word lays such a section out as N columns inside the same body extent;
310
+ * a balanced `column-count` container reproduces that geometry, and the paginator
311
+ * then places each container as one ordinary measured block. A container grows
312
+ * greedily until its balanced height would exceed the smallest page body available
313
+ * to the section, so a long columned section still splits across pages at block
314
+ * boundaries. Each child lands in exactly one container, so anchors never
315
+ * duplicate. An explicit page break child passes through as its own block, which
316
+ * ends the current container and lets the normal flow logic turn the page.
317
+ */
318
+ buildColumnBlocks(section, dims, sectionIndex, columnCount, columnGapPt) {
319
+ const children = Array.from(section.children);
320
+ const blocks = [];
321
+ const maxFragmentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);
322
+ const isBreak = (child) => child.dataset.pageBreak === "true" || child.classList.contains(`${this.cssPrefix}break`);
323
+ const makeContainer = (slice) => {
324
+ const container = document.createElement("div");
325
+ container.style.columnCount = String(columnCount);
326
+ container.style.columnGap = `${columnGapPt}pt`;
327
+ for (const child of slice) {
328
+ container.appendChild(child.cloneNode(true));
329
+ }
330
+ return container;
331
+ };
332
+ let start = 0;
333
+ while (start < children.length) {
334
+ if (isBreak(children[start])) {
335
+ blocks.push(this.measureElement(children[start], dims));
336
+ start++;
337
+ continue;
338
+ }
339
+ let end = start + 1;
340
+ let container = makeContainer(children.slice(start, end));
341
+ let measured = this.measureElement(container, dims);
342
+ while (end < children.length && !isBreak(children[end])) {
343
+ const candidate = makeContainer(children.slice(start, end + 1));
344
+ const candidateMeasured = this.measureElement(candidate, dims);
345
+ if (candidateMeasured.heightPt > maxFragmentHeight) break;
346
+ container = candidate;
347
+ measured = candidateMeasured;
348
+ end++;
349
+ }
350
+ blocks.push({
351
+ element: container,
352
+ heightPt: measured.heightPt,
353
+ marginTopPt: measured.marginTopPt,
354
+ marginBottomPt: measured.marginBottomPt,
355
+ keepWithNext: false,
356
+ keepLines: false,
357
+ pageBreakBefore: false,
358
+ isPageBreak: false,
359
+ isWordParagraph: false
243
360
  });
361
+ start = end;
244
362
  }
245
363
  return blocks;
246
364
  }
@@ -268,7 +386,8 @@ var DocxodusPagination = (() => {
268
386
  keepWithNext: element.dataset.keepWithNext === "true",
269
387
  keepLines: element.dataset.keepLines === "true",
270
388
  pageBreakBefore: element.dataset.pageBreakBefore === "true",
271
- isPageBreak: element.dataset.pageBreak === "true" || element.classList.contains(`${this.cssPrefix}break`)
389
+ isPageBreak: element.dataset.pageBreak === "true" || element.classList.contains(`${this.cssPrefix}break`),
390
+ isWordParagraph: this.isWordParagraphElement(element)
272
391
  };
273
392
  this.stagingElement.removeChild(measurementHost);
274
393
  return measured;
@@ -300,10 +419,15 @@ var DocxodusPagination = (() => {
300
419
  * collapsed-margin rules as normal block placement. The trailing margin is
301
420
  * intentionally excluded, matching the individual block fit check.
302
421
  */
303
- measureKeepWithNextChainBodyHeight(chain, previousMarginBottomPt, isFirstOnPage) {
422
+ measureKeepWithNextChainBodyHeight(chain, previousMarginBottomPt, isFirstOnPage, pageInSection) {
304
423
  const firstBlock = chain[0];
305
424
  if (!firstBlock) return 0;
306
- const firstMarginTop = isFirstOnPage ? firstBlock.marginTopPt : Math.max(firstBlock.marginTopPt, previousMarginBottomPt) - previousMarginBottomPt;
425
+ const firstMarginTop = this.effectiveBlockMarginTop(
426
+ firstBlock,
427
+ previousMarginBottomPt,
428
+ isFirstOnPage,
429
+ pageInSection
430
+ );
307
431
  let bodyHeight = firstMarginTop + firstBlock.heightPt;
308
432
  for (let index = 1; index < chain.length; index++) {
309
433
  const previousBlock = chain[index - 1];
@@ -312,6 +436,35 @@ var DocxodusPagination = (() => {
312
436
  }
313
437
  return bodyHeight;
314
438
  }
439
+ /** Word paragraphs render as `p`, or as `h1`–`h6` when their style has an outline level. */
440
+ isWordParagraphElement(element) {
441
+ return element.tagName === "P" || /^H[1-6]$/.test(element.tagName);
442
+ }
443
+ shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection) {
444
+ return isFirstOnPage && pageInSection > 1 && block.isWordParagraph === true;
445
+ }
446
+ /**
447
+ * Resolves the part of a block's top margin that consumes the current page.
448
+ *
449
+ * In Word's native DOCX layout, paragraph space-before is suppressed when a paragraph is the
450
+ * first body block on a later page of the SAME section. The first page of a document/section is
451
+ * the exception and keeps its spacing. Tables and other block margins are not paragraph spacing,
452
+ * so they continue to use the ordinary CSS collapsing rule.
453
+ */
454
+ effectiveBlockMarginTop(block, previousMarginBottomPt, isFirstOnPage, pageInSection) {
455
+ if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {
456
+ return 0;
457
+ }
458
+ return isFirstOnPage ? block.marginTopPt : Math.max(block.marginTopPt, previousMarginBottomPt) - previousMarginBottomPt;
459
+ }
460
+ /** Clone a source block with the same page-top spacing decision used by the height budget. */
461
+ cloneBlockForPage(block, isFirstOnPage, pageInSection) {
462
+ const clone = block.element.cloneNode(true);
463
+ if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {
464
+ clone.style.setProperty("margin-top", "0", "important");
465
+ }
466
+ return clone;
467
+ }
315
468
  /**
316
469
  * Finds footnote references introduced by a sequence of blocks, preserving
317
470
  * document order and excluding references already assigned to the page.
@@ -336,9 +489,9 @@ var DocxodusPagination = (() => {
336
489
  */
337
490
  smallestEffectiveContentHeight(dims, sectionIndex) {
338
491
  return Math.min(
339
- this.getEffectiveHeights(dims, sectionIndex, 1, 1).contentHeight,
340
- this.getEffectiveHeights(dims, sectionIndex, 2, 1).contentHeight,
341
- this.getEffectiveHeights(dims, sectionIndex, 2, 2).contentHeight
492
+ this.getPageBands(dims, sectionIndex, 1, 1).bodyHeight,
493
+ this.getPageBands(dims, sectionIndex, 2, 1).bodyHeight,
494
+ this.getPageBands(dims, sectionIndex, 2, 2).bodyHeight
342
495
  );
343
496
  }
344
497
  /**
@@ -862,7 +1015,7 @@ var DocxodusPagination = (() => {
862
1015
  /**
863
1016
  * Adds footnotes to a page container, including continuation content.
864
1017
  */
865
- addPageFootnotes(pageBox, footnoteIds, dims, footnoteHeight, continuation, partialFootnotes) {
1018
+ addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes) {
866
1019
  const hasContinuation = continuation && continuation.remainingElements.length > 0;
867
1020
  if (footnoteIds.length === 0 && !hasContinuation) {
868
1021
  return;
@@ -873,12 +1026,12 @@ var DocxodusPagination = (() => {
873
1026
  const partialFootnoteIds = new Set(partialFootnotes?.map((p) => p.footnoteId) || []);
874
1027
  const maxFootnoteHeight = Math.min(
875
1028
  footnoteHeight,
876
- dims.contentHeight * MAX_FOOTNOTE_AREA_RATIO
1029
+ bands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO
877
1030
  );
878
1031
  const footnotesDiv = document.createElement("div");
879
1032
  footnotesDiv.className = `${this.cssPrefix}footnotes`;
880
1033
  footnotesDiv.style.position = "absolute";
881
- footnotesDiv.style.bottom = `${dims.marginBottom}pt`;
1034
+ footnotesDiv.style.bottom = `${dims.pageHeight - (bands.bodyTop + bands.bodyHeight)}pt`;
882
1035
  footnotesDiv.style.left = `${dims.marginLeft}pt`;
883
1036
  footnotesDiv.style.width = `${dims.contentWidth}pt`;
884
1037
  footnotesDiv.style.boxSizing = "border-box";
@@ -952,50 +1105,51 @@ var DocxodusPagination = (() => {
952
1105
  return sectionHf.footerDefault;
953
1106
  }
954
1107
  /**
955
- * Computes effective header, footer, and content heights for a specific page position.
956
- * Uses pre-measured header/footer heights from the registry.
957
- * This method is deterministic - same inputs always produce same outputs.
958
- * This enables lazy loading compatibility since available height can be computed
959
- * for any page position without knowing the page's content.
1108
+ * The header, body, and footer bands for one page position.
1109
+ *
1110
+ * The single owner of "where does anything sit vertically on this page" — placement in
1111
+ * {@link createPage}, the body budget the flow loop spends, and the note area's anchor all
1112
+ * read it, so those three cannot disagree about where the body ends.
1113
+ *
1114
+ * Deterministic: it depends only on the section's page setup and the registry's pre-measured
1115
+ * story heights, never on the page's content, which is what keeps it lazy-loading compatible.
960
1116
  */
961
- getEffectiveHeights(dims, sectionIndex, pageInSection, globalPageNumber) {
1117
+ getPageBands(dims, sectionIndex, pageInSection, globalPageNumber) {
962
1118
  const sectionHf = this.hfRegistry.get(sectionIndex);
963
- let headerHeight = dims.marginTop;
964
- if (sectionHf) {
965
- let measuredHeaderHeight;
966
- if (pageInSection === 1 && sectionHf.headerFirstHeight != null) {
967
- measuredHeaderHeight = sectionHf.headerFirstHeight;
968
- } else if (globalPageNumber % 2 === 0 && sectionHf.headerEvenHeight != null) {
969
- measuredHeaderHeight = sectionHf.headerEvenHeight;
970
- } else if (sectionHf.headerDefaultHeight != null) {
971
- measuredHeaderHeight = sectionHf.headerDefaultHeight;
972
- }
973
- if (measuredHeaderHeight != null) {
974
- headerHeight = Math.max(dims.marginTop, measuredHeaderHeight);
975
- }
976
- }
977
- let footerHeight = dims.marginBottom;
978
- if (sectionHf) {
979
- let measuredFooterHeight;
980
- if (pageInSection === 1 && sectionHf.footerFirstHeight != null) {
981
- measuredFooterHeight = sectionHf.footerFirstHeight;
982
- } else if (globalPageNumber % 2 === 0 && sectionHf.footerEvenHeight != null) {
983
- measuredFooterHeight = sectionHf.footerEvenHeight;
984
- } else if (sectionHf.footerDefaultHeight != null) {
985
- measuredFooterHeight = sectionHf.footerDefaultHeight;
986
- }
987
- if (measuredFooterHeight != null) {
988
- footerHeight = Math.max(dims.marginBottom, measuredFooterHeight);
989
- }
990
- }
991
- const headerExpansion = headerHeight - dims.marginTop;
992
- const footerExpansion = footerHeight - dims.marginBottom;
993
- const contentHeight = dims.contentHeight - headerExpansion - footerExpansion;
994
- return { headerHeight, footerHeight, contentHeight };
1119
+ return resolvePageBands(
1120
+ dims,
1121
+ this.selectStoryHeight(
1122
+ sectionHf?.headerFirstHeight,
1123
+ sectionHf?.headerEvenHeight,
1124
+ sectionHf?.headerDefaultHeight,
1125
+ pageInSection,
1126
+ globalPageNumber
1127
+ ),
1128
+ this.selectStoryHeight(
1129
+ sectionHf?.footerFirstHeight,
1130
+ sectionHf?.footerEvenHeight,
1131
+ sectionHf?.footerDefaultHeight,
1132
+ pageInSection,
1133
+ globalPageNumber
1134
+ )
1135
+ );
1136
+ }
1137
+ /**
1138
+ * The measured height of the running story this page position selects, mirroring
1139
+ * {@link selectHeader}/{@link selectFooter}. Zero when the page has no such story.
1140
+ */
1141
+ selectStoryHeight(first, even, fallback, pageInSection, globalPageNumber) {
1142
+ if (pageInSection === 1 && first != null) return first;
1143
+ if (globalPageNumber % 2 === 0 && even != null) return even;
1144
+ return fallback ?? 0;
995
1145
  }
996
1146
  /**
997
1147
  * Measures the content height of a header or footer element.
998
- * This is needed because headers/footers can contain more content than fits in the margin area.
1148
+ *
1149
+ * This is what tells {@link resolvePageBands} whether the story stays inside its margin or
1150
+ * pushes the body, so it must measure the story ALONE — any padding added here would have to
1151
+ * be added to the rendered band too, and the two drifting apart is exactly how a header
1152
+ * silently starts overlapping body text.
999
1153
  */
1000
1154
  measureHeaderFooterHeight(source, contentWidth) {
1001
1155
  const measureContainer = document.createElement("div");
@@ -1003,7 +1157,6 @@ var DocxodusPagination = (() => {
1003
1157
  measureContainer.style.visibility = "hidden";
1004
1158
  measureContainer.style.width = `${contentWidth}pt`;
1005
1159
  measureContainer.style.left = "-9999px";
1006
- measureContainer.style.paddingBottom = "4pt";
1007
1160
  for (const child of Array.from(source.childNodes)) {
1008
1161
  measureContainer.appendChild(child.cloneNode(true));
1009
1162
  }
@@ -1023,7 +1176,7 @@ var DocxodusPagination = (() => {
1023
1176
  let currentContent = [];
1024
1177
  let pageNumber = startPageNumber;
1025
1178
  let pageInSection = 1;
1026
- let { contentHeight: effectiveContentHeight } = this.getEffectiveHeights(
1179
+ let { bodyHeight: effectiveContentHeight } = this.getPageBands(
1027
1180
  dims,
1028
1181
  sectionIndex,
1029
1182
  pageInSection,
@@ -1058,8 +1211,8 @@ var DocxodusPagination = (() => {
1058
1211
  pageNumber++;
1059
1212
  pageInSection++;
1060
1213
  currentContent = [];
1061
- const newHeights = this.getEffectiveHeights(dims, sectionIndex, pageInSection, pageNumber);
1062
- effectiveContentHeight = newHeights.contentHeight;
1214
+ const newBands = this.getPageBands(dims, sectionIndex, pageInSection, pageNumber);
1215
+ effectiveContentHeight = newBands.bodyHeight;
1063
1216
  remainingHeight = effectiveContentHeight;
1064
1217
  prevMarginBottomPt = 0;
1065
1218
  currentFootnoteIds = [];
@@ -1117,11 +1270,12 @@ var DocxodusPagination = (() => {
1117
1270
  const currentChainHeight = this.measureKeepWithNextChainBodyHeight(
1118
1271
  keepChain,
1119
1272
  prevMarginBottomPt,
1120
- currentContent.length === 0
1273
+ currentContent.length === 0,
1274
+ pageInSection
1121
1275
  ) + additionalChainFootnoteHeight;
1122
1276
  const currentAvailableHeight = remainingHeight - currentFootnoteHeight;
1123
1277
  if (currentChainHeight > currentAvailableHeight) {
1124
- const nextPageHeights = this.getEffectiveHeights(
1278
+ const nextPageBands = this.getPageBands(
1125
1279
  dims,
1126
1280
  sectionIndex,
1127
1281
  pageInSection + 1,
@@ -1130,14 +1284,15 @@ var DocxodusPagination = (() => {
1130
1284
  const freshChainBodyHeight = this.measureKeepWithNextChainBodyHeight(
1131
1285
  keepChain,
1132
1286
  0,
1133
- true
1287
+ true,
1288
+ pageInSection + 1
1134
1289
  );
1135
1290
  const freshChainFootnoteHeight = this.measureFootnotesHeight(
1136
1291
  newChainFootnoteIds,
1137
1292
  dims.contentWidth,
1138
1293
  nextPageContinuation
1139
1294
  );
1140
- if (freshChainBodyHeight + freshChainFootnoteHeight <= nextPageHeights.contentHeight) {
1295
+ if (freshChainBodyHeight + freshChainFootnoteHeight <= nextPageBands.bodyHeight) {
1141
1296
  finishPage();
1142
1297
  }
1143
1298
  }
@@ -1156,10 +1311,12 @@ var DocxodusPagination = (() => {
1156
1311
  additionalFootnoteHeight = totalFootnoteHeight - currentFootnoteHeight;
1157
1312
  }
1158
1313
  const isFirstOnPage = currentContent.length === 0;
1159
- let effectiveMarginTop = block.marginTopPt;
1160
- if (!isFirstOnPage) {
1161
- effectiveMarginTop = Math.max(block.marginTopPt, prevMarginBottomPt) - prevMarginBottomPt;
1162
- }
1314
+ const effectiveMarginTop = this.effectiveBlockMarginTop(
1315
+ block,
1316
+ prevMarginBottomPt,
1317
+ isFirstOnPage,
1318
+ pageInSection
1319
+ );
1163
1320
  const blockSpace = effectiveMarginTop + block.heightPt + additionalFootnoteHeight;
1164
1321
  const effectiveRemainingHeight = remainingHeight - currentFootnoteHeight;
1165
1322
  const bodyContentUsed = effectiveContentHeight - remainingHeight;
@@ -1179,19 +1336,19 @@ var DocxodusPagination = (() => {
1179
1336
  }
1180
1337
  }
1181
1338
  if (blockSpace <= effectiveRemainingHeight) {
1182
- currentContent.push(block.element.cloneNode(true));
1339
+ currentContent.push(this.cloneBlockForPage(block, isFirstOnPage, pageInSection));
1183
1340
  remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
1184
1341
  prevMarginBottomPt = block.marginBottomPt;
1185
1342
  if (newFootnoteIds.length > 0) {
1186
1343
  currentFootnoteIds.push(...newFootnoteIds);
1187
1344
  currentFootnoteHeight += additionalFootnoteHeight;
1188
1345
  }
1189
- } else if (block.heightPt + block.marginTopPt <= effectiveContentHeight) {
1346
+ } else if (block.heightPt + this.effectiveBlockMarginTop(block, 0, true, pageInSection + 1) <= effectiveContentHeight) {
1190
1347
  const blockSpaceWithoutFootnotes = effectiveMarginTop + block.heightPt;
1191
1348
  const minBodySpaceNeeded = bodyContentUsed + blockSpaceWithoutFootnotes + MIN_BODY_CONTENT_HEIGHT;
1192
1349
  const canExpandFootnotes = minBodySpaceNeeded <= effectiveContentHeight;
1193
1350
  if (newFootnoteIds.length > 0 && blockSpaceWithoutFootnotes <= effectiveRemainingHeight) {
1194
- currentContent.push(block.element.cloneNode(true));
1351
+ currentContent.push(this.cloneBlockForPage(block, isFirstOnPage, pageInSection));
1195
1352
  remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
1196
1353
  prevMarginBottomPt = block.marginBottomPt;
1197
1354
  const availableForFootnotes = Math.min(
@@ -1238,7 +1395,7 @@ var DocxodusPagination = (() => {
1238
1395
  const expandedFootnoteSpace = Math.min(maxFootnoteArea, additionalFootnoteHeight + currentFootnoteHeight);
1239
1396
  const bodySpaceAfterExpansion = effectiveContentHeight - expandedFootnoteSpace;
1240
1397
  if (blockSpaceWithoutFootnotes <= bodySpaceAfterExpansion - bodyContentUsed) {
1241
- currentContent.push(block.element.cloneNode(true));
1398
+ currentContent.push(this.cloneBlockForPage(block, isFirstOnPage, pageInSection));
1242
1399
  remainingHeight = bodySpaceAfterExpansion - bodyContentUsed - blockSpaceWithoutFootnotes;
1243
1400
  prevMarginBottomPt = block.marginBottomPt;
1244
1401
  currentFootnoteIds.push(...newFootnoteIds);
@@ -1246,8 +1403,14 @@ var DocxodusPagination = (() => {
1246
1403
  } else {
1247
1404
  finishPage();
1248
1405
  const newPageFootnoteHeight = allBlockFootnoteIds.length > 0 ? this.measureFootnotesHeight(allBlockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
1249
- const newPageSpace = block.marginTopPt + block.heightPt + block.marginBottomPt;
1250
- currentContent.push(block.element.cloneNode(true));
1406
+ const newPageMarginTop = this.effectiveBlockMarginTop(
1407
+ block,
1408
+ 0,
1409
+ true,
1410
+ pageInSection
1411
+ );
1412
+ const newPageSpace = newPageMarginTop + block.heightPt + block.marginBottomPt;
1413
+ currentContent.push(this.cloneBlockForPage(block, true, pageInSection));
1251
1414
  remainingHeight = effectiveContentHeight - newPageSpace;
1252
1415
  prevMarginBottomPt = block.marginBottomPt;
1253
1416
  currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
@@ -1256,8 +1419,14 @@ var DocxodusPagination = (() => {
1256
1419
  } else {
1257
1420
  finishPage();
1258
1421
  const newPageFootnoteHeight = allBlockFootnoteIds.length > 0 ? this.measureFootnotesHeight(allBlockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
1259
- const newPageSpace = block.marginTopPt + block.heightPt + block.marginBottomPt;
1260
- currentContent.push(block.element.cloneNode(true));
1422
+ const newPageMarginTop = this.effectiveBlockMarginTop(
1423
+ block,
1424
+ 0,
1425
+ true,
1426
+ pageInSection
1427
+ );
1428
+ const newPageSpace = newPageMarginTop + block.heightPt + block.marginBottomPt;
1429
+ currentContent.push(this.cloneBlockForPage(block, true, pageInSection));
1261
1430
  remainingHeight = effectiveContentHeight - newPageSpace;
1262
1431
  prevMarginBottomPt = block.marginBottomPt;
1263
1432
  currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
@@ -1276,7 +1445,7 @@ var DocxodusPagination = (() => {
1276
1445
  if (currentContent.length > 0) {
1277
1446
  finishPage();
1278
1447
  }
1279
- currentContent.push(block.element.cloneNode(true));
1448
+ currentContent.push(this.cloneBlockForPage(block, true, pageInSection));
1280
1449
  currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
1281
1450
  finishPage();
1282
1451
  }
@@ -1306,6 +1475,161 @@ var DocxodusPagination = (() => {
1306
1475
  if (el.getAttribute("contenteditable") !== null) el.setAttribute("contenteditable", "false");
1307
1476
  }
1308
1477
  }
1478
+ /**
1479
+ * Resolves floating DrawingML objects after their anchor paragraphs have landed on a page.
1480
+ *
1481
+ * The converter deliberately carries the OOXML bases and offsets as data instead of flattening
1482
+ * them into CSS: page/margin/column bases belong to the page, while paragraph/line/character
1483
+ * bases belong to the laid-out anchor. Once both coordinate systems exist, promote the object
1484
+ * from the clipped text column into the page box and position it in one shared point space.
1485
+ */
1486
+ positionDrawingAnchors(pageBox, contentArea, dims, pageNumber) {
1487
+ const anchors = Array.from(
1488
+ contentArea.querySelectorAll('[data-docx-drawing-anchor="true"]')
1489
+ );
1490
+ if (anchors.length === 0) return;
1491
+ const pageRect = pageBox.getBoundingClientRect();
1492
+ const pixelsPerPoint = pageRect.width / dims.pageWidth;
1493
+ if (!(pixelsPerPoint > 0)) return;
1494
+ for (const anchor of anchors) {
1495
+ const staticRect = anchor.getBoundingClientRect();
1496
+ const paragraph = anchor.closest("p, h1, h2, h3, h4, h5, h6");
1497
+ const paragraphRect = paragraph?.getBoundingClientRect() ?? staticRect;
1498
+ const toPageX = (x) => (x - pageRect.left) / pixelsPerPoint;
1499
+ const toPageY = (y) => (y - pageRect.top) / pixelsPerPoint;
1500
+ const context = {
1501
+ staticLeft: toPageX(staticRect.left),
1502
+ staticTop: toPageY(staticRect.top),
1503
+ lineHeight: paragraph ? (parseFloat(getComputedStyle(paragraph).lineHeight) || staticRect.height) / pixelsPerPoint : staticRect.height / pixelsPerPoint,
1504
+ paragraphLeft: toPageX(paragraphRect.left),
1505
+ paragraphTop: toPageY(paragraphRect.top),
1506
+ paragraphWidth: paragraphRect.width / pixelsPerPoint,
1507
+ paragraphHeight: paragraphRect.height / pixelsPerPoint
1508
+ };
1509
+ const widthReference = this.horizontalAnchorReference(
1510
+ anchor.getAttribute("data-docx-anchor-width-relative") ?? "margin",
1511
+ dims,
1512
+ pageNumber,
1513
+ context
1514
+ );
1515
+ const widthPercent = this.anchorNumber(anchor, "width-percent");
1516
+ if (widthPercent !== void 0) {
1517
+ anchor.style.width = `${widthReference.size * widthPercent / 100}pt`;
1518
+ }
1519
+ const heightReference = this.verticalAnchorReference(
1520
+ anchor.getAttribute("data-docx-anchor-height-relative") ?? "margin",
1521
+ dims,
1522
+ pageNumber,
1523
+ context
1524
+ );
1525
+ const heightPercent = this.anchorNumber(anchor, "height-percent");
1526
+ if (heightPercent !== void 0 && anchor.dataset.docxAnchorAutofit !== "true") {
1527
+ anchor.style.height = `${heightReference.size * heightPercent / 100}pt`;
1528
+ }
1529
+ const sizedRect = anchor.getBoundingClientRect();
1530
+ const width = sizedRect.width / pixelsPerPoint;
1531
+ const height = sizedRect.height / pixelsPerPoint;
1532
+ const horizontal = this.horizontalAnchorReference(
1533
+ anchor.getAttribute("data-docx-anchor-h-relative") ?? "column",
1534
+ dims,
1535
+ pageNumber,
1536
+ context
1537
+ );
1538
+ const vertical = this.verticalAnchorReference(
1539
+ anchor.getAttribute("data-docx-anchor-v-relative") ?? "paragraph",
1540
+ dims,
1541
+ pageNumber,
1542
+ context
1543
+ );
1544
+ const left = this.resolveAnchorAxis(
1545
+ horizontal,
1546
+ width,
1547
+ anchor.getAttribute("data-docx-anchor-h-align"),
1548
+ this.anchorNumber(anchor, "h-offset"),
1549
+ pageNumber
1550
+ );
1551
+ const top = this.resolveAnchorAxis(
1552
+ vertical,
1553
+ height,
1554
+ anchor.getAttribute("data-docx-anchor-v-align"),
1555
+ this.anchorNumber(anchor, "v-offset"),
1556
+ pageNumber
1557
+ );
1558
+ pageBox.appendChild(anchor);
1559
+ anchor.style.position = "absolute";
1560
+ anchor.style.left = `${left}pt`;
1561
+ anchor.style.top = `${top}pt`;
1562
+ anchor.style.margin = "0";
1563
+ }
1564
+ }
1565
+ anchorNumber(anchor, suffix) {
1566
+ const raw = anchor.getAttribute(`data-docx-anchor-${suffix}`);
1567
+ if (raw === null) return void 0;
1568
+ const value = Number(raw);
1569
+ return Number.isFinite(value) ? value : void 0;
1570
+ }
1571
+ horizontalAnchorReference(relativeFrom, dims, pageNumber, context) {
1572
+ const leftMargin = { start: 0, size: dims.marginLeft };
1573
+ const rightMargin = {
1574
+ start: dims.marginLeft + dims.contentWidth,
1575
+ size: dims.marginRight
1576
+ };
1577
+ switch (relativeFrom) {
1578
+ case "page":
1579
+ return { start: 0, size: dims.pageWidth };
1580
+ case "character":
1581
+ return { start: context.staticLeft, size: 0 };
1582
+ case "leftMargin":
1583
+ return leftMargin;
1584
+ case "rightMargin":
1585
+ return rightMargin;
1586
+ case "insideMargin":
1587
+ return pageNumber % 2 === 1 ? leftMargin : rightMargin;
1588
+ case "outsideMargin":
1589
+ return pageNumber % 2 === 1 ? rightMargin : leftMargin;
1590
+ case "paragraph":
1591
+ return { start: context.paragraphLeft, size: context.paragraphWidth };
1592
+ case "column":
1593
+ case "margin":
1594
+ default:
1595
+ return { start: dims.marginLeft, size: dims.contentWidth };
1596
+ }
1597
+ }
1598
+ verticalAnchorReference(relativeFrom, dims, pageNumber, context) {
1599
+ const topMargin = { start: 0, size: dims.marginTop };
1600
+ const bottomMargin = {
1601
+ start: dims.marginTop + dims.contentHeight,
1602
+ size: dims.marginBottom
1603
+ };
1604
+ switch (relativeFrom) {
1605
+ case "page":
1606
+ return { start: 0, size: dims.pageHeight };
1607
+ case "paragraph":
1608
+ return { start: context.paragraphTop, size: context.paragraphHeight };
1609
+ case "line":
1610
+ return { start: context.staticTop, size: context.lineHeight };
1611
+ case "topMargin":
1612
+ return topMargin;
1613
+ case "bottomMargin":
1614
+ return bottomMargin;
1615
+ case "insideMargin":
1616
+ return pageNumber % 2 === 1 ? topMargin : bottomMargin;
1617
+ case "outsideMargin":
1618
+ return pageNumber % 2 === 1 ? bottomMargin : topMargin;
1619
+ case "margin":
1620
+ default:
1621
+ return { start: dims.marginTop, size: dims.contentHeight };
1622
+ }
1623
+ }
1624
+ resolveAnchorAxis(reference, objectSize, alignment, offset, pageNumber) {
1625
+ if (offset !== void 0) return reference.start + offset;
1626
+ let fraction = 0;
1627
+ if (alignment === "center") fraction = 0.5;
1628
+ else if (alignment === "right" || alignment === "bottom") fraction = 1;
1629
+ else if (alignment === "inside") fraction = pageNumber % 2 === 1 ? 0 : 1;
1630
+ else if (alignment === "outside") fraction = pageNumber % 2 === 1 ? 1 : 0;
1631
+ return reference.start + (reference.size - objectSize) * fraction;
1632
+ }
1309
1633
  /**
1310
1634
  * Creates a page container element.
1311
1635
  */
@@ -1332,22 +1656,22 @@ var DocxodusPagination = (() => {
1332
1656
  pageBox.dataset.pageNumber = String(pageNumber);
1333
1657
  pageBox.dataset.sectionIndex = String(sectionIndex);
1334
1658
  pageBox.dataset.pageInSection = String(pageInSection);
1335
- const effectiveHeights = this.getEffectiveHeights(dims, sectionIndex, pageInSection, pageNumber);
1659
+ const bands = this.getPageBands(dims, sectionIndex, pageInSection, pageNumber);
1336
1660
  const headerSource = this.selectHeader(sectionIndex, pageInSection, pageNumber);
1337
1661
  if (headerSource) {
1338
1662
  const headerDiv = document.createElement("div");
1339
1663
  headerDiv.className = `${this.cssPrefix}header`;
1340
1664
  headerDiv.style.position = "absolute";
1341
- headerDiv.style.top = "0";
1665
+ headerDiv.style.top = `${bands.headerTop}pt`;
1666
+ headerDiv.style.bottom = "auto";
1342
1667
  headerDiv.style.left = `${dims.marginLeft}pt`;
1343
1668
  headerDiv.style.width = `${dims.contentWidth}pt`;
1344
- headerDiv.style.height = `${effectiveHeights.headerHeight}pt`;
1669
+ headerDiv.style.height = `${bands.headerHeight}pt`;
1345
1670
  headerDiv.style.overflow = "hidden";
1346
1671
  headerDiv.style.boxSizing = "border-box";
1347
1672
  headerDiv.style.display = "flex";
1348
1673
  headerDiv.style.flexDirection = "column";
1349
- headerDiv.style.justifyContent = "flex-end";
1350
- headerDiv.style.paddingBottom = "4pt";
1674
+ headerDiv.style.justifyContent = "flex-start";
1351
1675
  for (const child of Array.from(headerSource.childNodes)) {
1352
1676
  const clonedheaderDiv = child.cloneNode(true);
1353
1677
  if (clonedheaderDiv.nodeType === 1) this.makeClonedStoryInert(clonedheaderDiv);
@@ -1355,8 +1679,8 @@ var DocxodusPagination = (() => {
1355
1679
  }
1356
1680
  pageBox.appendChild(headerDiv);
1357
1681
  }
1358
- const contentAreaTop = effectiveHeights.headerHeight;
1359
- const contentAreaHeight = effectiveHeights.contentHeight;
1682
+ const contentAreaTop = bands.bodyTop;
1683
+ const contentAreaHeight = bands.bodyHeight;
1360
1684
  const contentArea = document.createElement("div");
1361
1685
  contentArea.className = `${this.cssPrefix}content`;
1362
1686
  contentArea.style.position = "absolute";
@@ -1371,23 +1695,23 @@ var DocxodusPagination = (() => {
1371
1695
  pageBox.appendChild(contentArea);
1372
1696
  const hasContinuation = continuation && continuation.remainingElements.length > 0;
1373
1697
  if (footnoteIds.length > 0 || hasContinuation) {
1374
- this.addPageFootnotes(pageBox, footnoteIds, dims, footnoteHeight, continuation, partialFootnotes);
1698
+ this.addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes);
1375
1699
  }
1376
1700
  const footerSource = this.selectFooter(sectionIndex, pageInSection, pageNumber);
1377
1701
  if (footerSource) {
1378
1702
  const footerDiv = document.createElement("div");
1379
1703
  footerDiv.className = `${this.cssPrefix}footer`;
1380
1704
  footerDiv.style.position = "absolute";
1381
- footerDiv.style.bottom = "0";
1705
+ footerDiv.style.top = "auto";
1706
+ footerDiv.style.bottom = `${dims.footerDistance}pt`;
1382
1707
  footerDiv.style.left = `${dims.marginLeft}pt`;
1383
1708
  footerDiv.style.width = `${dims.contentWidth}pt`;
1384
- footerDiv.style.height = `${effectiveHeights.footerHeight}pt`;
1709
+ footerDiv.style.height = `${bands.footerHeight}pt`;
1385
1710
  footerDiv.style.overflow = "hidden";
1386
1711
  footerDiv.style.boxSizing = "border-box";
1387
1712
  footerDiv.style.display = "flex";
1388
1713
  footerDiv.style.flexDirection = "column";
1389
- footerDiv.style.justifyContent = "flex-start";
1390
- footerDiv.style.paddingTop = "4pt";
1714
+ footerDiv.style.justifyContent = "flex-end";
1391
1715
  for (const child of Array.from(footerSource.childNodes)) {
1392
1716
  const clonedfooterDiv = child.cloneNode(true);
1393
1717
  if (clonedfooterDiv.nodeType === 1) this.makeClonedStoryInert(clonedfooterDiv);
@@ -1402,6 +1726,7 @@ var DocxodusPagination = (() => {
1402
1726
  pageBox.appendChild(pageNum);
1403
1727
  }
1404
1728
  this.containerElement.appendChild(pageBox);
1729
+ this.positionDrawingAnchors(pageBox, contentArea, dims, pageNumber);
1405
1730
  const notesEl = pageBox.querySelector(`.${this.cssPrefix}footnotes`);
1406
1731
  if (notesEl) {
1407
1732
  const notesHeightPt = pxToPt(notesEl.getBoundingClientRect().height);