ooxml-core 1.8.3 → 1.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 (308) hide show
  1. package/dist/PptxHandler-AT7FVG5Q.mjs +2 -0
  2. package/dist/{PptxHandler-BL4B7CZO.mjs → PptxHandler-EX4WBIQQ.mjs} +2 -2
  3. package/dist/PptxHandler-VFSCECPL.cjs +11 -0
  4. package/dist/automation/index.cjs +7380 -4682
  5. package/dist/automation/index.mjs +7380 -4682
  6. package/dist/chunk-AT66SA5M.cjs +44423 -0
  7. package/dist/{chunk-TTPZITQN.mjs → chunk-FIELY5PI.mjs} +1 -1
  8. package/dist/{chunk-ZAP5WLEN.mjs → chunk-IGOA57LE.mjs} +1 -1
  9. package/dist/chunk-LCS3J3QI.mjs +87604 -0
  10. package/dist/{chunk-NIHPA63A.mjs → chunk-M76PHYRR.mjs} +54 -37
  11. package/dist/chunk-OB277ZGE.mjs +44107 -0
  12. package/dist/{chunk-GAGSCAR3.mjs → chunk-OXHMZS7B.mjs} +552 -178
  13. package/dist/chunk-SWF7KVL5.cjs +87610 -0
  14. package/dist/{chunk-Z7IQR4S4.mjs → chunk-WSO4T2LJ.mjs} +246 -21
  15. package/dist/docx/block-parser.d.ts.map +1 -1
  16. package/dist/docx/cell-blocks.d.ts +4 -0
  17. package/dist/docx/cell-blocks.d.ts.map +1 -0
  18. package/dist/docx/document-paragraphs.d.ts.map +1 -1
  19. package/dist/docx/document-parts.d.ts +2 -0
  20. package/dist/docx/document-parts.d.ts.map +1 -1
  21. package/dist/docx/embedded.cjs +189 -20
  22. package/dist/docx/embedded.mjs +189 -20
  23. package/dist/docx/index.cjs +219 -24
  24. package/dist/docx/index.mjs +219 -24
  25. package/dist/docx/layout/adapt-table.d.ts.map +1 -1
  26. package/dist/docx/layout/adapter.d.ts.map +1 -1
  27. package/dist/docx/layout/flow-paragraph.d.ts +5 -0
  28. package/dist/docx/layout/flow-paragraph.d.ts.map +1 -1
  29. package/dist/docx/layout/flow-table.d.ts +2 -1
  30. package/dist/docx/layout/flow-table.d.ts.map +1 -1
  31. package/dist/docx/layout/footnote-columns.d.ts +22 -0
  32. package/dist/docx/layout/footnote-columns.d.ts.map +1 -0
  33. package/dist/docx/layout/footnote-flow.d.ts +27 -0
  34. package/dist/docx/layout/footnote-flow.d.ts.map +1 -0
  35. package/dist/docx/layout/footnote-split.d.ts +7 -0
  36. package/dist/docx/layout/footnote-split.d.ts.map +1 -0
  37. package/dist/docx/layout/index.cjs +1099 -92
  38. package/dist/docx/layout/index.mjs +1099 -92
  39. package/dist/docx/layout/input.d.ts +17 -1
  40. package/dist/docx/layout/input.d.ts.map +1 -1
  41. package/dist/docx/layout/legacy-footnote-flow.d.ts +18 -0
  42. package/dist/docx/layout/legacy-footnote-flow.d.ts.map +1 -0
  43. package/dist/docx/layout/line-metrics.d.ts.map +1 -1
  44. package/dist/docx/layout/page-cursor.d.ts +13 -8
  45. package/dist/docx/layout/page-cursor.d.ts.map +1 -1
  46. package/dist/docx/layout/page-flow.d.ts.map +1 -1
  47. package/dist/docx/layout/paragraph-layout.d.ts.map +1 -1
  48. package/dist/docx/layout/paragraph-tokens.d.ts +2 -0
  49. package/dist/docx/layout/paragraph-tokens.d.ts.map +1 -1
  50. package/dist/docx/layout/reference-footnotes.d.ts +7 -0
  51. package/dist/docx/layout/reference-footnotes.d.ts.map +1 -0
  52. package/dist/docx/layout/result.d.ts +31 -0
  53. package/dist/docx/layout/result.d.ts.map +1 -1
  54. package/dist/docx/layout/section-flow.d.ts +2 -0
  55. package/dist/docx/layout/section-flow.d.ts.map +1 -1
  56. package/dist/docx/layout/table-cell-layout.d.ts +11 -0
  57. package/dist/docx/layout/table-cell-layout.d.ts.map +1 -0
  58. package/dist/docx/layout/table-footnotes.d.ts +27 -0
  59. package/dist/docx/layout/table-footnotes.d.ts.map +1 -0
  60. package/dist/docx/layout/table-layout.d.ts +10 -4
  61. package/dist/docx/layout/table-layout.d.ts.map +1 -1
  62. package/dist/docx/load/index.cjs +194 -25
  63. package/dist/docx/load/index.mjs +194 -25
  64. package/dist/docx/model-run.d.ts +2 -0
  65. package/dist/docx/model-run.d.ts.map +1 -1
  66. package/dist/docx/model.d.ts +7 -0
  67. package/dist/docx/model.d.ts.map +1 -1
  68. package/dist/docx/notes.d.ts +5 -0
  69. package/dist/docx/notes.d.ts.map +1 -1
  70. package/dist/docx/parse-table.d.ts.map +1 -1
  71. package/dist/docx/parse.d.ts.map +1 -1
  72. package/dist/docx/save.d.ts.map +1 -1
  73. package/dist/docx/section-model.d.ts +2 -0
  74. package/dist/docx/section-model.d.ts.map +1 -1
  75. package/dist/docx/sections.d.ts.map +1 -1
  76. package/dist/docx/settings.d.ts +2 -0
  77. package/dist/docx/settings.d.ts.map +1 -1
  78. package/dist/docx/table-model.d.ts +3 -1
  79. package/dist/docx/table-model.d.ts.map +1 -1
  80. package/dist/docx/ui/index.cjs +55 -0
  81. package/dist/docx/ui/index.d.ts +1 -0
  82. package/dist/docx/ui/index.d.ts.map +1 -1
  83. package/dist/docx/ui/index.mjs +55 -1
  84. package/dist/docx/ui/table-structure-guard.d.ts +4 -0
  85. package/dist/docx/ui/table-structure-guard.d.ts.map +1 -0
  86. package/dist/docx/write-footnote-columns.d.ts +3 -0
  87. package/dist/docx/write-footnote-columns.d.ts.map +1 -0
  88. package/dist/docx/write-notes.d.ts.map +1 -1
  89. package/dist/docx/write-run.d.ts.map +1 -1
  90. package/dist/docx/write-sections.d.ts.map +1 -1
  91. package/dist/index.cjs +8 -5
  92. package/dist/index.mjs +8 -5
  93. package/dist/opc/clone-part-graph.d.ts +4 -0
  94. package/dist/opc/clone-part-graph.d.ts.map +1 -1
  95. package/dist/opc/index.cjs +8 -5
  96. package/dist/opc/index.mjs +8 -5
  97. package/dist/pptx/automation/index.cjs +621 -207
  98. package/dist/pptx/automation/index.d.ts +3 -3
  99. package/dist/pptx/automation/index.mjs +5 -5
  100. package/dist/pptx/chunks/{index-Cn-GzYGp.d.ts → index-B4cnSPNz.d.ts} +33 -4
  101. package/dist/pptx/chunks/index-B4cnSPNz.d.ts.map +1 -0
  102. package/dist/pptx/chunks/{index-CKIUurnd.d.ts → index-BZh-IMkN.d.ts} +14 -9
  103. package/dist/pptx/chunks/{index-CKIUurnd.d.ts.map → index-BZh-IMkN.d.ts.map} +1 -1
  104. package/dist/pptx/chunks/{text-operations-BHfAm5K9.d.ts → text-operations-BLodkuXE.d.ts} +29 -12
  105. package/dist/pptx/chunks/text-operations-BLodkuXE.d.ts.map +1 -0
  106. package/dist/pptx/cli/index.cjs +622 -213
  107. package/dist/pptx/cli/index.d.ts +1 -1
  108. package/dist/pptx/cli/index.mjs +4 -4
  109. package/dist/pptx/converter/index.cjs +53 -36
  110. package/dist/pptx/converter/index.d.ts +1 -1
  111. package/dist/pptx/converter/index.mjs +2 -2
  112. package/dist/pptx/editor/render/find-replace.cjs +416 -45
  113. package/dist/pptx/editor/render/find-replace.d.ts +35 -4
  114. package/dist/pptx/editor/render/find-replace.d.ts.map +1 -1
  115. package/dist/pptx/editor/render/find-replace.mjs +416 -46
  116. package/dist/pptx/editor/render/slide-operations.cjs +87 -0
  117. package/dist/pptx/editor/render/slide-operations.d.ts +17 -1
  118. package/dist/pptx/editor/render/slide-operations.d.ts.map +1 -1
  119. package/dist/pptx/editor/render/slide-operations.mjs +83 -1
  120. package/dist/pptx/editor/render/slide-search.cjs +6 -10
  121. package/dist/pptx/editor/render/slide-search.d.ts +1 -1
  122. package/dist/pptx/editor/render/slide-search.mjs +6 -10
  123. package/dist/pptx/editor/render/template-editing.d.ts +1 -1
  124. package/dist/pptx/editor/render/template-editing.d.ts.map +1 -1
  125. package/dist/pptx/index.cjs +889 -237
  126. package/dist/pptx/index.d.ts +4 -4
  127. package/dist/pptx/index.mjs +5 -5
  128. package/dist/teams/index.cjs +672 -37
  129. package/dist/teams/index.mjs +672 -37
  130. package/dist/visio/accessibility.d.ts +10 -0
  131. package/dist/visio/accessibility.d.ts.map +1 -0
  132. package/dist/visio/cell-formula.d.ts +2 -0
  133. package/dist/visio/cell-formula.d.ts.map +1 -1
  134. package/dist/visio/connector-curve-aligned.d.ts +5 -0
  135. package/dist/visio/connector-curve-aligned.d.ts.map +1 -0
  136. package/dist/visio/connector-curve-spans.d.ts +14 -0
  137. package/dist/visio/connector-curve-spans.d.ts.map +1 -0
  138. package/dist/visio/connector-jumps.d.ts +5 -0
  139. package/dist/visio/connector-jumps.d.ts.map +1 -0
  140. package/dist/visio/connector-route-walk.d.ts +2 -1
  141. package/dist/visio/connector-route-walk.d.ts.map +1 -1
  142. package/dist/visio/edit-alt-text-scope.d.ts +5 -0
  143. package/dist/visio/edit-alt-text-scope.d.ts.map +1 -0
  144. package/dist/visio/edit-alt-text.d.ts +3 -0
  145. package/dist/visio/edit-alt-text.d.ts.map +1 -0
  146. package/dist/visio/edit-commands.d.ts +7 -2
  147. package/dist/visio/edit-commands.d.ts.map +1 -1
  148. package/dist/visio/edit-formatting-commands.d.ts +5 -0
  149. package/dist/visio/edit-formatting-commands.d.ts.map +1 -1
  150. package/dist/visio/edit-formatting-paint.d.ts.map +1 -1
  151. package/dist/visio/edit-formatting-scope.d.ts.map +1 -1
  152. package/dist/visio/edit-line-jump-cache.d.ts +4 -0
  153. package/dist/visio/edit-line-jump-cache.d.ts.map +1 -0
  154. package/dist/visio/edit-master-index.d.ts.map +1 -1
  155. package/dist/visio/edit-metadata-commands.d.ts +10 -2
  156. package/dist/visio/edit-metadata-commands.d.ts.map +1 -1
  157. package/dist/visio/edit-metadata.d.ts +3 -1
  158. package/dist/visio/edit-metadata.d.ts.map +1 -1
  159. package/dist/visio/edit-page-alt-text.d.ts +4 -0
  160. package/dist/visio/edit-page-alt-text.d.ts.map +1 -0
  161. package/dist/visio/edit-page-layout.d.ts.map +1 -1
  162. package/dist/visio/edit-page-rename.d.ts.map +1 -1
  163. package/dist/visio/edit-page-setup-commands.d.ts +7 -1
  164. package/dist/visio/edit-page-setup-commands.d.ts.map +1 -1
  165. package/dist/visio/edit-page-size.d.ts.map +1 -1
  166. package/dist/visio/edit-pages.d.ts.map +1 -1
  167. package/dist/visio/edit-recalculate-index.d.ts.map +1 -1
  168. package/dist/visio/edit-scope.d.ts.map +1 -1
  169. package/dist/visio/edit-shape-create.d.ts.map +1 -1
  170. package/dist/visio/edit-stencil-connector-write.d.ts.map +1 -1
  171. package/dist/visio/edit-style-admission.d.ts.map +1 -1
  172. package/dist/visio/edit-text.d.ts.map +1 -1
  173. package/dist/visio/edit.d.ts +1 -1
  174. package/dist/visio/edit.d.ts.map +1 -1
  175. package/dist/visio/index.cjs +1091 -233
  176. package/dist/visio/index.d.ts +2 -1
  177. package/dist/visio/index.d.ts.map +1 -1
  178. package/dist/visio/index.mjs +1091 -234
  179. package/dist/visio/line-jumps.d.ts +1 -5
  180. package/dist/visio/line-jumps.d.ts.map +1 -1
  181. package/dist/visio/model.d.ts +13 -0
  182. package/dist/visio/model.d.ts.map +1 -1
  183. package/dist/visio/page-layout.d.ts +11 -2
  184. package/dist/visio/page-layout.d.ts.map +1 -1
  185. package/dist/visio/parser.d.ts.map +1 -1
  186. package/dist/visio/shape-metadata.d.ts +6 -0
  187. package/dist/visio/shape-metadata.d.ts.map +1 -1
  188. package/dist/visio/shapes.d.ts.map +1 -1
  189. package/dist/visio/text-field-case.d.ts +2 -0
  190. package/dist/visio/text-field-case.d.ts.map +1 -0
  191. package/dist/visio/text-field-display.d.ts.map +1 -1
  192. package/dist/visio/text-field-numeric.d.ts +4 -0
  193. package/dist/visio/text-field-numeric.d.ts.map +1 -0
  194. package/dist/visio/ui/connector-preview.d.ts +1 -1
  195. package/dist/visio/ui/connector-preview.d.ts.map +1 -1
  196. package/dist/visio/ui/data-graphic-rules.d.ts +2 -0
  197. package/dist/visio/ui/data-graphic-rules.d.ts.map +1 -1
  198. package/dist/visio/ui/data-graphics.d.ts +3 -2
  199. package/dist/visio/ui/data-graphics.d.ts.map +1 -1
  200. package/dist/visio/ui/edit-commands.d.ts.map +1 -1
  201. package/dist/visio/ui/index.cjs +16898 -15855
  202. package/dist/visio/ui/index.mjs +16898 -15856
  203. package/dist/visio/ui/scene-details.d.ts.map +1 -1
  204. package/dist/visio/ui/scene-validation.d.ts.map +1 -1
  205. package/dist/visio/ui/snapshot-scene.d.ts +2 -1
  206. package/dist/visio/ui/snapshot-scene.d.ts.map +1 -1
  207. package/dist/visio/xml-attribute-whitespace.d.ts +5 -0
  208. package/dist/visio/xml-attribute-whitespace.d.ts.map +1 -0
  209. package/dist/xlsx/edit/index.d.ts +1 -0
  210. package/dist/xlsx/edit/index.d.ts.map +1 -1
  211. package/dist/xlsx/edit/insert-guard.d.ts.map +1 -1
  212. package/dist/xlsx/edit/merge.d.ts.map +1 -1
  213. package/dist/xlsx/edit/page-breaks.d.ts +5 -0
  214. package/dist/xlsx/edit/page-breaks.d.ts.map +1 -0
  215. package/dist/xlsx/edit/pivots.d.ts +8 -0
  216. package/dist/xlsx/edit/pivots.d.ts.map +1 -0
  217. package/dist/xlsx/edit/print-titles.d.ts +7 -0
  218. package/dist/xlsx/edit/print-titles.d.ts.map +1 -0
  219. package/dist/xlsx/edit/session.d.ts.map +1 -1
  220. package/dist/xlsx/edit/sheets.d.ts.map +1 -1
  221. package/dist/xlsx/edit/shift-formulas.d.ts.map +1 -1
  222. package/dist/xlsx/edit/shift-sheet.d.ts.map +1 -1
  223. package/dist/xlsx/edit/sparkline-ranges.d.ts +11 -0
  224. package/dist/xlsx/edit/sparkline-ranges.d.ts.map +1 -0
  225. package/dist/xlsx/edit/sparkline-xml.d.ts +13 -0
  226. package/dist/xlsx/edit/sparkline-xml.d.ts.map +1 -0
  227. package/dist/xlsx/edit/sparklines.d.ts +16 -0
  228. package/dist/xlsx/edit/sparklines.d.ts.map +1 -0
  229. package/dist/xlsx/edit/table-edits.d.ts.map +1 -1
  230. package/dist/xlsx/edit/tables.d.ts.map +1 -1
  231. package/dist/xlsx/edit/types.d.ts +22 -1
  232. package/dist/xlsx/edit/types.d.ts.map +1 -1
  233. package/dist/xlsx/formula/functions/text-regex.d.ts.map +1 -1
  234. package/dist/xlsx/index.cjs +3415 -1374
  235. package/dist/xlsx/index.d.ts +1 -0
  236. package/dist/xlsx/index.d.ts.map +1 -1
  237. package/dist/xlsx/index.mjs +3396 -1375
  238. package/dist/xlsx/layout/cell-view.d.ts +2 -0
  239. package/dist/xlsx/layout/cell-view.d.ts.map +1 -1
  240. package/dist/xlsx/layout/header-footer.d.ts +58 -0
  241. package/dist/xlsx/layout/header-footer.d.ts.map +1 -0
  242. package/dist/xlsx/layout/index.d.ts +5 -0
  243. package/dist/xlsx/layout/index.d.ts.map +1 -1
  244. package/dist/xlsx/layout/print-breaks.d.ts +11 -0
  245. package/dist/xlsx/layout/print-breaks.d.ts.map +1 -0
  246. package/dist/xlsx/layout/print-layout.d.ts +34 -0
  247. package/dist/xlsx/layout/print-layout.d.ts.map +1 -0
  248. package/dist/xlsx/layout/print-merges.d.ts +18 -0
  249. package/dist/xlsx/layout/print-merges.d.ts.map +1 -0
  250. package/dist/xlsx/layout/print-repeat.d.ts +12 -0
  251. package/dist/xlsx/layout/print-repeat.d.ts.map +1 -0
  252. package/dist/xlsx/layout/print-titles.d.ts +20 -0
  253. package/dist/xlsx/layout/print-titles.d.ts.map +1 -0
  254. package/dist/xlsx/layout/sparkline-data.d.ts.map +1 -1
  255. package/dist/xlsx/layout/sparkline-view.d.ts.map +1 -1
  256. package/dist/xlsx/load/index.cjs +7831 -6961
  257. package/dist/xlsx/load/index.mjs +7831 -6961
  258. package/dist/xlsx/model-pivots.d.ts +46 -0
  259. package/dist/xlsx/model-pivots.d.ts.map +1 -0
  260. package/dist/xlsx/model.d.ts +5 -0
  261. package/dist/xlsx/model.d.ts.map +1 -1
  262. package/dist/xlsx/pivot/admission.d.ts +5 -0
  263. package/dist/xlsx/pivot/admission.d.ts.map +1 -0
  264. package/dist/xlsx/pivot/aggregate.d.ts +19 -0
  265. package/dist/xlsx/pivot/aggregate.d.ts.map +1 -0
  266. package/dist/xlsx/pivot/cache-xml.d.ts +9 -0
  267. package/dist/xlsx/pivot/cache-xml.d.ts.map +1 -0
  268. package/dist/xlsx/pivot/data.d.ts +12 -0
  269. package/dist/xlsx/pivot/data.d.ts.map +1 -0
  270. package/dist/xlsx/pivot/hierarchy.d.ts +7 -0
  271. package/dist/xlsx/pivot/hierarchy.d.ts.map +1 -0
  272. package/dist/xlsx/pivot/index.d.ts +3 -0
  273. package/dist/xlsx/pivot/index.d.ts.map +1 -0
  274. package/dist/xlsx/pivot/read.d.ts +5 -0
  275. package/dist/xlsx/pivot/read.d.ts.map +1 -0
  276. package/dist/xlsx/pivot/table-xml.d.ts +5 -0
  277. package/dist/xlsx/pivot/table-xml.d.ts.map +1 -0
  278. package/dist/xlsx/pivot/write.d.ts +11 -0
  279. package/dist/xlsx/pivot/write.d.ts.map +1 -0
  280. package/dist/xlsx/read/load.d.ts.map +1 -1
  281. package/dist/xlsx/read/sheet-props.d.ts.map +1 -1
  282. package/dist/xlsx/ui/commands/i18n/index.d.ts.map +1 -1
  283. package/dist/xlsx/ui/commands/i18n/navigation.d.ts.map +1 -1
  284. package/dist/xlsx/ui/commands/i18n/pivots.d.ts +3 -0
  285. package/dist/xlsx/ui/commands/i18n/pivots.d.ts.map +1 -0
  286. package/dist/xlsx/ui/commands/i18n/print-layout.d.ts +3 -0
  287. package/dist/xlsx/ui/commands/i18n/print-layout.d.ts.map +1 -0
  288. package/dist/xlsx/ui/commands/i18n/sparklines.d.ts +3 -0
  289. package/dist/xlsx/ui/commands/i18n/sparklines.d.ts.map +1 -0
  290. package/dist/xlsx/ui/commands/i18n/tools.d.ts.map +1 -1
  291. package/dist/xlsx/ui/commands/page-breaks.d.ts +3 -0
  292. package/dist/xlsx/ui/commands/page-breaks.d.ts.map +1 -0
  293. package/dist/xlsx/ui/index.cjs +2451 -737
  294. package/dist/xlsx/ui/index.d.ts +1 -0
  295. package/dist/xlsx/ui/index.d.ts.map +1 -1
  296. package/dist/xlsx/ui/index.mjs +2451 -738
  297. package/dist/xlsx/write/chart-cache.d.ts +8 -0
  298. package/dist/xlsx/write/chart-cache.d.ts.map +1 -0
  299. package/dist/xlsx/write/page-setup.d.ts.map +1 -1
  300. package/dist/xlsx/write/save.d.ts.map +1 -1
  301. package/dist/xlsx/write/sheet-parts.d.ts +1 -0
  302. package/dist/xlsx/write/sheet-parts.d.ts.map +1 -1
  303. package/dist/xlsx/write/workbook-part.d.ts +1 -1
  304. package/dist/xlsx/write/workbook-part.d.ts.map +1 -1
  305. package/dist/xlsx/write/worksheet.d.ts.map +1 -1
  306. package/package.json +1 -1
  307. package/dist/pptx/chunks/index-Cn-GzYGp.d.ts.map +0 -1
  308. package/dist/pptx/chunks/text-operations-BHfAm5K9.d.ts.map +0 -1
@@ -39,8 +39,8 @@ function named(element, local, ns, lenient = true) {
39
39
  }
40
40
  var children = (parent, local, ns, lenient = true) => elements(parent).filter((element) => named(element, local, ns, lenient));
41
41
  var first = (parent, local, ns, lenient = true) => parent ? children(parent, local, ns, lenient)[0] : void 0;
42
- function attr(element, local, ns, prefix) {
43
- return element?.getAttributeNS(ns, local) ?? element?.getAttribute(`${prefix}:${local}`) ?? void 0;
42
+ function attr(element, local, ns, prefix2) {
43
+ return element?.getAttributeNS(ns, local) ?? element?.getAttribute(`${prefix2}:${local}`) ?? void 0;
44
44
  }
45
45
 
46
46
  // docx/xml.ts
@@ -2651,11 +2651,11 @@ function cssFontFamily(name) {
2651
2651
  // chart/render/chart-svg-appearance.ts
2652
2652
  var nextPaintId = 0;
2653
2653
  function chartGradientPaint(model, width = 1024, height = 768) {
2654
- const prefix = `xlsx-chart-${++nextPaintId}`;
2654
+ const prefix2 = `xlsx-chart-${++nextPaintId}`;
2655
2655
  const appearance = { ...model.appearance };
2656
2656
  const defs = [];
2657
2657
  const shadowPaint = (shadow, suffix) => {
2658
- const id = `${prefix}-${suffix}`;
2658
+ const id = `${prefix2}-${suffix}`;
2659
2659
  const pad = Math.max(4, shadow.blur * 3 + Math.abs(shadow.dx) + Math.abs(shadow.dy));
2660
2660
  defs.push(
2661
2661
  `<filter id="${id}" filterUnits="userSpaceOnUse" x="${n(-pad)}" y="${n(-pad)}" width="${n(width + pad * 2)}" height="${n(height + pad * 2)}" color-interpolation-filters="sRGB">${svgDropShadowElement(shadow, { number: n, color: (value) => esc(value) })}</filter>`
@@ -2670,7 +2670,7 @@ function chartGradientPaint(model, width = 1024, height = 768) {
2670
2670
  };
2671
2671
  }
2672
2672
  const paint = (gradient, suffix, bounds) => {
2673
- const def = buildChartGradientDef(`${prefix}-${suffix}`, gradient, bounds);
2673
+ const def = buildChartGradientDef(`${prefix2}-${suffix}`, gradient, bounds);
2674
2674
  defs.push(chartGradientMarkup(def));
2675
2675
  return `url(#${def.id})`;
2676
2676
  };
@@ -3710,6 +3710,14 @@ function mapDocumentParagraphs(model, map) {
3710
3710
  ...section.footers ? { footers: slots(section.footers) } : {}
3711
3711
  }))
3712
3712
  } : {},
3713
+ ...model.footnoteSeparators ? {
3714
+ footnoteSeparators: Object.fromEntries(
3715
+ Object.entries(model.footnoteSeparators).map(([kind, note]) => [
3716
+ kind,
3717
+ { ...note, blocks: mapBlockParagraphs(note.blocks, map) }
3718
+ ])
3719
+ )
3720
+ } : {},
3713
3721
  ...model.footnotes ? {
3714
3722
  footnotes: model.footnotes.map((note) => ({
3715
3723
  ...note,
@@ -3730,6 +3738,32 @@ function fieldName(instr) {
3730
3738
  return instr.trim().split(/\s+/)[0]?.toUpperCase() ?? "";
3731
3739
  }
3732
3740
 
3741
+ // docx/cell-blocks.ts
3742
+ function cellSourceBlocks(cell) {
3743
+ if (!cell.sourceBlocks) return cell.paragraphs;
3744
+ const originalIds = new Set(
3745
+ cell.sourceBlocks.filter((block) => block.type === "paragraph").map((block) => block.id)
3746
+ );
3747
+ const positions = new Map(cell.paragraphs.map((paragraph, index) => [paragraph.id, index]));
3748
+ const blocks = [];
3749
+ let cursor = 0;
3750
+ for (const source of cell.sourceBlocks) {
3751
+ if (source.type === "table") {
3752
+ blocks.push(source);
3753
+ continue;
3754
+ }
3755
+ const position = positions.get(source.id);
3756
+ if (position !== void 0 && position >= cursor) {
3757
+ blocks.push(...cell.paragraphs.slice(cursor, position + 1));
3758
+ cursor = position + 1;
3759
+ }
3760
+ while (cursor < cell.paragraphs.length && !originalIds.has(cell.paragraphs[cursor].id))
3761
+ blocks.push(cell.paragraphs[cursor++]);
3762
+ }
3763
+ blocks.push(...cell.paragraphs.slice(cursor));
3764
+ return blocks;
3765
+ }
3766
+
3733
3767
  // docx/notes.ts
3734
3768
  var romanNumerals = [
3735
3769
  [1e3, "M"],
@@ -3794,7 +3828,7 @@ function walkBlocks(blocks, kind, order) {
3794
3828
  if (block.type === "paragraph") walkParagraph(block, kind, order);
3795
3829
  else
3796
3830
  for (const row of block.rows)
3797
- for (const cell of row) walkBlocks(cell.paragraphs, kind, order);
3831
+ for (const cell of row) walkBlocks(cellSourceBlocks(cell), kind, order);
3798
3832
  }
3799
3833
  }
3800
3834
  function numberNotesInOrder(blocks, kind) {
@@ -4505,6 +4539,9 @@ function resolveIndents(paragraph) {
4505
4539
  firstLineExtraPx
4506
4540
  };
4507
4541
  }
4542
+ function sourceLengthOf(run) {
4543
+ return run.sourceLength ?? (run.object ? 1 : run.text.length || (run.breakAfter ? 1 : 0));
4544
+ }
4508
4545
  function tokenizeParagraph(paragraph) {
4509
4546
  const tokens = [];
4510
4547
  const runOffsets = [];
@@ -4512,7 +4549,7 @@ function tokenizeParagraph(paragraph) {
4512
4549
  paragraph.runs.forEach((run, runIndex) => {
4513
4550
  runOffsets.push(cursor);
4514
4551
  if (run.hidden) {
4515
- if (!run.synthetic) cursor += run.sourceLength ?? run.text.length;
4552
+ if (!run.synthetic) cursor += sourceLengthOf(run);
4516
4553
  return;
4517
4554
  }
4518
4555
  if (run.object) tokens.push({ kind: "object", runIndex, sourceStart: 0 });
@@ -4528,7 +4565,7 @@ function tokenizeParagraph(paragraph) {
4528
4565
  if (run.breakAfter) appendBreakMarker(tokens, runIndex, run.text.length, run.breakAfter);
4529
4566
  if (run.synthetic) {
4530
4567
  for (const token of tokens) if (token.runIndex === runIndex) token.sourceStart = 0;
4531
- } else cursor += run.sourceLength ?? run.text.length;
4568
+ } else cursor += sourceLengthOf(run);
4532
4569
  });
4533
4570
  return { tokens, runOffsets };
4534
4571
  }
@@ -4550,7 +4587,9 @@ function lineMetrics(placed, fonts, paragraph, measurer) {
4550
4587
  let above = 0;
4551
4588
  let below = 0;
4552
4589
  for (const token of tokens) {
4553
- const extent = tokenExtent(token, paragraph, fonts, measurer);
4590
+ const run = paragraph.runs[token.runIndex];
4591
+ const metrics = run?.object?.noteSeparator ? fontMetrics(measurer, fonts[token.runIndex] ?? fontOf(run)) : void 0;
4592
+ const extent = metrics ? { above: metrics.ascent, below: metrics.descent } : tokenExtent(token, paragraph, fonts, measurer);
4554
4593
  above = Math.max(above, extent.above);
4555
4594
  below = Math.max(below, extent.below);
4556
4595
  }
@@ -4625,7 +4664,18 @@ function layoutParagraph(paragraph, contentWidthPx, measurer, note, lineBox, sta
4625
4664
  const bodyWidth = Math.max(1, contentWidthPx - leftPx - rightPx);
4626
4665
  const fonts = paragraph.runs.map(fontOf);
4627
4666
  const { tokens, runOffsets } = tokenizeParagraph(paragraph);
4628
- const globalOffset = (token) => expectDefined(runOffsets[token.runIndex], "run offset index") + token.sourceStart;
4667
+ const globalOffset = (token, advance = 0) => {
4668
+ const run = expectDefined(paragraph.runs[token.runIndex], "source run");
4669
+ const length = sourceLengthOf(run);
4670
+ const offset = run.synthetic ? 0 : Math.max(
4671
+ 0,
4672
+ Math.min(
4673
+ length,
4674
+ Math.round((token.sourceStart + advance) * length / Math.max(1, run.text.length))
4675
+ )
4676
+ );
4677
+ return expectDefined(runOffsets[token.runIndex], "run offset index") + offset;
4678
+ };
4629
4679
  const effectiveAlign = paragraph.align ?? (paragraph.direction === "rtl" ? "right" : "left");
4630
4680
  const lines = [];
4631
4681
  const pageBreakAfterLine = /* @__PURE__ */ new Set();
@@ -4676,9 +4726,9 @@ function layoutParagraph(paragraph, contentWidthPx, measurer, note, lineBox, sta
4676
4726
  const token = expectDefined(tokens[next], "token index");
4677
4727
  if (ends.has(token.kind)) break;
4678
4728
  if (beforeDecimalPx === void 0 && token.kind === "word" && /[.,]/.test(token.text)) {
4679
- const prefix = token.text.slice(0, token.text.search(/[.,]/));
4729
+ const prefix2 = token.text.slice(0, token.text.search(/[.,]/));
4680
4730
  beforeDecimalPx = followingPx + runTextWidth(
4681
- prefix,
4731
+ prefix2,
4682
4732
  expectDefined(paragraph.runs[token.runIndex], "decimal run"),
4683
4733
  measurer
4684
4734
  );
@@ -4697,14 +4747,17 @@ function layoutParagraph(paragraph, contentWidthPx, measurer, note, lineBox, sta
4697
4747
  const [firstPlaced] = placed;
4698
4748
  const start = firstPlaced ? globalOffset(firstPlaced.token) : 0;
4699
4749
  const last = placed.at(-1)?.token;
4700
- const end = last ? globalOffset(last) + (last.kind === "word" || last.kind === "space" ? last.text.length : 1) : start;
4750
+ const end = last ? globalOffset(last, last.kind === "word" || last.kind === "space" ? last.text.length : 1) : start;
4701
4751
  const gapBeforePx = boxFor(lineIndex).gapBeforePx;
4702
4752
  const { heightPx, baselinePx } = lineMetrics(placed, fonts, paragraph, measurer);
4703
4753
  rendered.forEach(({ token }, index) => {
4704
4754
  const extent2 = tokenExtent(token, paragraph, fonts, measurer);
4705
4755
  const fragment = expectDefined(fragments[index], "fragment for rendered token");
4706
4756
  fragment.sourceStart = globalOffset(token);
4707
- fragment.sourceEnd = fragment.sourceStart + (paragraph.runs[token.runIndex]?.synthetic ? 0 : token.kind === "word" || token.kind === "space" ? token.text.length : 1);
4757
+ fragment.sourceEnd = globalOffset(
4758
+ token,
4759
+ token.kind === "word" || token.kind === "space" ? token.text.length : 1
4760
+ );
4708
4761
  fragment.topPx = baselinePx - extent2.above;
4709
4762
  if (token.kind !== "object") fragment.boxHeightPx = extent2.ascent + extent2.descent;
4710
4763
  });
@@ -4838,6 +4891,57 @@ function widowControlEnabled(value) {
4838
4891
  return value !== false;
4839
4892
  }
4840
4893
 
4894
+ // docx/layout/table-cell-layout.ts
4895
+ function cellParagraphs(cell) {
4896
+ const walk = (blocks) => blocks.flatMap(
4897
+ (block) => block.kind === "paragraph" ? [block] : block.rows.flatMap((row) => row.cells.flatMap(cellParagraphs))
4898
+ );
4899
+ return cell.blocks ? walk(cell.blocks) : cell.paragraphs;
4900
+ }
4901
+ function stackCellBlocks(cell, width, measurer, note, top, starts) {
4902
+ if (!cell.blocks)
4903
+ return stackParagraphs(
4904
+ starts ? cell.paragraphs.filter((p) => starts.has(p.id)) : cell.paragraphs,
4905
+ width,
4906
+ measurer,
4907
+ note,
4908
+ top,
4909
+ starts
4910
+ );
4911
+ let y = top;
4912
+ const boxes = [];
4913
+ for (const block of cell.blocks) {
4914
+ if (block.kind === "paragraph") {
4915
+ if (starts && !starts.has(block.id)) continue;
4916
+ const result = stackParagraphs([block], width, measurer, note, y, starts);
4917
+ boxes.push(...result.boxes);
4918
+ y = result.heightPx;
4919
+ continue;
4920
+ }
4921
+ const tableWidth = block.widthPx ?? width;
4922
+ const x = block.alignment === "center" ? Math.max(0, (width - tableWidth) / 2) : block.alignment === "right" ? Math.max(0, width - tableWidth) : block.indentPx ?? 0;
4923
+ for (const row of block.rows) {
4924
+ if (starts && !row.cells.some((nested) => cellParagraphs(nested).some((p) => starts.has(p.id))))
4925
+ continue;
4926
+ const layout = layoutRow(row, tableWidth, measurer, note, void 0, starts);
4927
+ layout.cells.forEach((paragraphs, index) => {
4928
+ const geometry = layout.geometry[index];
4929
+ for (const paragraph of paragraphs)
4930
+ boxes.push({
4931
+ ...paragraph,
4932
+ yPx: y + paragraph.yPx,
4933
+ xPx: x + geometry.xPx + geometry.paddingLeftPx + (paragraph.xPx ?? 0),
4934
+ widthPx: paragraph.widthPx ?? Math.max(1, geometry.widthPx - geometry.paddingLeftPx - geometry.paddingRightPx),
4935
+ readOnly: true
4936
+ });
4937
+ });
4938
+ y += layout.heightPx;
4939
+ }
4940
+ }
4941
+ boxes.sort((a, b) => a.yPx - b.yPx);
4942
+ return { heightPx: y, boxes };
4943
+ }
4944
+
4841
4945
  // docx/layout/table-layout.ts
4842
4946
  function stackParagraphs(paragraphs, widthPx, measurer, note, topPx = 0, startTokens) {
4843
4947
  let y = topPx;
@@ -4853,29 +4957,30 @@ function stackParagraphs(paragraphs, widthPx, measurer, note, topPx = 0, startTo
4853
4957
  );
4854
4958
  y += layout.spacingBeforePx;
4855
4959
  const top = y;
4856
- const lines = layout.lines.map((line2) => ({ ...line2, yPx: line2.yPx + top }));
4960
+ const lines = layout.lines.map((line2) => ({ ...line2 }));
4857
4961
  boxes.push({
4858
4962
  kind: "paragraph",
4859
4963
  blockId: paragraph.id,
4860
4964
  yPx: top,
4861
4965
  heightPx: layout.contentHeightPx,
4862
- lines
4966
+ lines,
4967
+ ...layout.frame ? { frame: layout.frame } : {}
4863
4968
  });
4864
4969
  y += layout.contentHeightPx + layout.spacingAfterPx;
4865
4970
  }
4866
4971
  return { heightPx: y, boxes };
4867
4972
  }
4868
- function layoutRow(row, tableWidthPx, measurer, note, remaining) {
4973
+ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
4869
4974
  const count = row.cells.length || 1;
4870
4975
  let x = 0;
4871
4976
  const geometry = [];
4872
4977
  const laidOut = row.cells.map((cell) => {
4873
4978
  const continuation = remaining?.[geometry.length];
4874
- const starts = continuation ? new Map(continuation.map((box) => [box.blockId, box.lines[0]?.firstToken ?? 0])) : void 0;
4979
+ const starts = continuation ? new Map(continuation.map((box) => [box.blockId, box.lines[0]?.firstToken ?? 0])) : startTokens;
4875
4980
  const widthPx = cell.widthPx ?? tableWidthPx / count;
4876
4981
  const padding = cell.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
4877
- const content2 = stackParagraphs(
4878
- starts ? cell.paragraphs.filter((paragraph) => starts.has(paragraph.id)) : cell.paragraphs,
4982
+ const content2 = stackCellBlocks(
4983
+ cell,
4879
4984
  Math.max(1, widthPx - padding.left - padding.right),
4880
4985
  measurer,
4881
4986
  note,
@@ -4898,26 +5003,24 @@ function layoutRow(row, tableWidthPx, measurer, note, remaining) {
4898
5003
  });
4899
5004
  const content = Math.max(0, ...laidOut.map((cell) => cell.heightPx));
4900
5005
  const heightPx = row.heightPx === void 0 ? content : row.heightRule === "exact" ? row.heightPx : Math.max(content, row.heightPx);
4901
- return { heightPx, cells: laidOut.map((cell) => cell.boxes), geometry };
5006
+ return {
5007
+ heightPx,
5008
+ cells: laidOut.map((cell) => cell.boxes),
5009
+ geometry,
5010
+ cellPadding: row.cells.map((cell) => ({
5011
+ top: cell.padding?.top ?? 0,
5012
+ bottom: cell.padding?.bottom ?? 0
5013
+ }))
5014
+ };
4902
5015
  }
4903
5016
  function splitCellAtHeight(boxes, cutHeightPx) {
4904
5017
  const before = [];
4905
5018
  const after = [];
4906
- let afterOrigin = null;
4907
5019
  let beforeHeightPx = 0;
4908
5020
  for (const box of boxes) {
4909
- if (afterOrigin !== null) {
4910
- after.push({ ...box, yPx: box.yPx - afterOrigin });
4911
- continue;
4912
- }
4913
5021
  if (box.yPx + box.heightPx <= cutHeightPx) {
4914
5022
  before.push(box);
4915
- beforeHeightPx = box.yPx + box.heightPx;
4916
- continue;
4917
- }
4918
- if (box.yPx >= cutHeightPx) {
4919
- afterOrigin = box.yPx;
4920
- after.push({ ...box, yPx: 0 });
5023
+ beforeHeightPx = Math.max(beforeHeightPx, box.yPx + box.heightPx);
4921
5024
  continue;
4922
5025
  }
4923
5026
  const beforeLines = box.lines.filter(
@@ -4927,50 +5030,407 @@ function splitCellAtHeight(boxes, cutHeightPx) {
4927
5030
  if (beforeLines.length) {
4928
5031
  const bottom = beforeLines.at(-1).yPx + beforeLines.at(-1).heightPx;
4929
5032
  before.push({ ...box, heightPx: bottom, lines: beforeLines });
4930
- beforeHeightPx = box.yPx + bottom;
5033
+ beforeHeightPx = Math.max(beforeHeightPx, box.yPx + bottom);
4931
5034
  }
4932
5035
  if (afterLines.length) {
4933
5036
  const shift = expectDefined(afterLines[0], "first line after split").yPx;
4934
- afterOrigin = box.yPx + shift;
4935
5037
  after.push({
4936
5038
  ...box,
4937
- yPx: 0,
5039
+ yPx: box.yPx + shift,
4938
5040
  heightPx: afterLines.at(-1).yPx + afterLines.at(-1).heightPx - shift,
4939
5041
  lines: afterLines.map((line2) => ({ ...line2, yPx: line2.yPx - shift }))
4940
5042
  });
4941
- } else {
4942
- afterOrigin = box.yPx + box.heightPx;
4943
5043
  }
4944
5044
  }
5045
+ const afterOrigin = Math.min(...after.map((box) => box.yPx));
5046
+ for (const box of after) box.yPx -= afterOrigin;
4945
5047
  return { before, after, beforeHeightPx };
4946
5048
  }
4947
- function splitRowAtHeight(row, cutHeightPx) {
4948
- const splits = row.cells.map((cell) => splitCellAtHeight(cell, cutHeightPx));
5049
+ function splitRowAtHeight(row, cutHeightPx, cellCuts) {
5050
+ const splits = row.cells.map((cell, index) => {
5051
+ const padding = row.cellPadding?.[index] ?? { top: 0, bottom: 0 };
5052
+ const split = splitCellAtHeight(cell, (cellCuts?.[index] ?? cutHeightPx) - padding.bottom);
5053
+ if (split.before.length) split.beforeHeightPx += padding.bottom;
5054
+ if (split.after.length)
5055
+ split.after = split.after.map((box) => ({ ...box, yPx: box.yPx + padding.top }));
5056
+ return split;
5057
+ });
4949
5058
  const anyAfter = splits.some((split) => split.after.length > 0);
4950
5059
  const before = {
4951
5060
  heightPx: Math.max(0, ...splits.map((split) => split.beforeHeightPx)),
4952
5061
  cells: splits.map((split) => split.before),
4953
- geometry: row.geometry
5062
+ geometry: row.geometry.map((geometry, index) => ({
5063
+ ...geometry,
5064
+ contentHeightPx: splits[index].beforeHeightPx
5065
+ })),
5066
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
4954
5067
  };
4955
5068
  if (!anyAfter) return { before, after: null };
4956
5069
  const after = {
4957
5070
  heightPx: Math.max(
4958
5071
  0,
4959
5072
  ...splits.map(
4960
- (split) => split.after.reduce((max, box) => Math.max(max, box.yPx + box.heightPx), 0)
5073
+ (split, index) => split.after.length ? split.after.at(-1).yPx + split.after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
4961
5074
  )
4962
5075
  ),
4963
5076
  cells: splits.map((split) => split.after),
4964
- geometry: row.geometry
5077
+ geometry: row.geometry.map((geometry, index) => ({
5078
+ ...geometry,
5079
+ contentHeightPx: splits[index].after.length ? splits[index].after.at(-1).yPx + splits[index].after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
5080
+ })),
5081
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
4965
5082
  };
4966
5083
  return { before, after };
4967
5084
  }
4968
5085
 
4969
- // docx/layout/page-cursor.ts
5086
+ // docx/layout/footnote-split.ts
5087
+ function splitNote(note, budget) {
5088
+ if (note.heightPx <= budget + 1e-6) return { before: note };
5089
+ const before = [];
5090
+ const after = [];
5091
+ let origin;
5092
+ const sourceTokenStarts = { ...note.sourceTokenStarts };
5093
+ for (const paragraph of note.paragraphs) {
5094
+ if (origin !== void 0) {
5095
+ after.push({ ...paragraph, yPx: paragraph.yPx - origin });
5096
+ continue;
5097
+ }
5098
+ if (paragraph.yPx + paragraph.heightPx <= budget + 1e-6) {
5099
+ before.push(paragraph);
5100
+ continue;
5101
+ }
5102
+ const count = paragraph.lines.filter(
5103
+ (line2) => paragraph.yPx + line2.yPx + line2.heightPx <= budget + 1e-6
5104
+ ).length;
5105
+ const head = paragraph.lines.slice(0, count);
5106
+ const tail = paragraph.lines.slice(count);
5107
+ if (head.length) {
5108
+ const bottom = head.at(-1).yPx + head.at(-1).heightPx;
5109
+ before.push({ ...paragraph, heightPx: bottom, lines: head });
5110
+ }
5111
+ const first3 = tail[0];
5112
+ if (head.length)
5113
+ sourceTokenStarts[paragraph.blockId] = head.at(-1).nextToken ?? sourceTokenStarts[paragraph.blockId] ?? 0;
5114
+ origin = paragraph.yPx + (first3?.yPx ?? 0);
5115
+ after.push({
5116
+ ...paragraph,
5117
+ yPx: 0,
5118
+ heightPx: Math.max(0, paragraph.heightPx - (first3?.yPx ?? 0)),
5119
+ lines: tail.map((line2) => ({ ...line2, yPx: line2.yPx - (first3?.yPx ?? 0) }))
5120
+ });
5121
+ }
5122
+ if (!before.length) return { after: note };
5123
+ const last = before.at(-1);
5124
+ return {
5125
+ before: { ...note, heightPx: last.yPx + last.heightPx, paragraphs: before },
5126
+ ...after.length ? {
5127
+ after: {
5128
+ ...note,
5129
+ yPx: 0,
5130
+ heightPx: Math.max(0, note.heightPx - (origin ?? 0)),
5131
+ paragraphs: after,
5132
+ continued: true,
5133
+ sourceTokenStarts
5134
+ }
5135
+ } : {}
5136
+ };
5137
+ }
5138
+
5139
+ // docx/layout/footnote-columns.ts
5140
+ function footnoteColumns(page, columns) {
5141
+ const width = page.widthPx - page.marginLeftPx - page.marginRightPx;
5142
+ const equal = (width - columns.gapPx * (columns.count - 1)) / columns.count;
5143
+ let x = 0;
5144
+ return Array.from({ length: columns.count }, (_, index) => {
5145
+ const own = columns.widths?.[index];
5146
+ const column = { xPx: x, widthPx: Math.max(1, own?.widthPx ?? equal) };
5147
+ x += column.widthPx + (own?.gapPx ?? columns.gapPx);
5148
+ return column;
5149
+ });
5150
+ }
5151
+ function packFootnotes(notes, columns, height, reflow) {
5152
+ const remaining = [...notes], placed = [];
5153
+ let used = 0;
5154
+ for (const [columnIndex, column] of columns.entries()) {
5155
+ let y = 0;
5156
+ while (remaining.length) {
5157
+ const current = reflow(remaining[0], column.widthPx);
5158
+ const split = splitNote(current, Math.max(0, height - y));
5159
+ if (!split.before) break;
5160
+ placed.push({
5161
+ ...split.before,
5162
+ yPx: y,
5163
+ xPx: column.xPx,
5164
+ widthPx: column.widthPx,
5165
+ columnIndex
5166
+ });
5167
+ y += split.before.heightPx;
5168
+ used = Math.max(used, y);
5169
+ if (split.after) {
5170
+ remaining[0] = { ...split.after, continued: !!current.continued };
5171
+ break;
5172
+ }
5173
+ remaining.shift();
5174
+ }
5175
+ }
5176
+ return { placed, remaining, heightPx: used };
5177
+ }
5178
+ function balancedFootnotes(notes, columns, limit, reflow) {
5179
+ if (!notes.length) return { placed: [], remaining: [], heightPx: 0 };
5180
+ const full = packFootnotes(notes, columns, limit, reflow);
5181
+ if (full.remaining.length) return full;
5182
+ let lower = 0, upper = limit;
5183
+ for (let step = 0; step < 24 && upper - lower > 5e-3; step++) {
5184
+ const middle = (lower + upper) / 2;
5185
+ if (packFootnotes(notes, columns, middle, reflow).remaining.length) lower = middle;
5186
+ else upper = middle;
5187
+ }
5188
+ return packFootnotes(notes, columns, Math.ceil(upper * 100) / 100, reflow);
5189
+ }
5190
+ function minimumFootnoteHeight(notes, columns, reflow) {
5191
+ const width = columns[0].widthPx;
5192
+ const maximum = notes.reduce((sum, note) => sum + reflow(note, width).heightPx, 0);
5193
+ return balancedFootnotes(notes, columns, maximum, reflow).heightPx;
5194
+ }
5195
+
5196
+ // docx/layout/footnote-flow.ts
5197
+ function firstLineBottom(note) {
5198
+ const first3 = note?.paragraphs[0];
5199
+ return first3 ? first3.yPx + (first3.lines[0]?.yPx ?? 0) + (first3.lines[0]?.heightPx ?? first3.heightPx) : note?.heightPx ?? 0;
5200
+ }
4970
5201
  var FOOTNOTE_SEPARATOR_PX = 12;
5202
+ var FootnoteFlow = class {
5203
+ constructor(report, separator, columns, reflow = (note) => note) {
5204
+ this.report = report;
5205
+ this.separator = separator;
5206
+ this.columns = columns;
5207
+ this.reflow = reflow;
5208
+ }
5209
+ report;
5210
+ separator;
5211
+ columns;
5212
+ reflow;
5213
+ pending;
5214
+ remaining = [];
5215
+ sources = /* @__PURE__ */ new WeakMap();
5216
+ separatorHeight(page, notes = this.pending?.notes ?? []) {
5217
+ return (page.footnoteSeparator ?? this.separator?.(page, !!notes[0]?.continued))?.heightPx ?? FOOTNOTE_SEPARATOR_PX;
5218
+ }
5219
+ get hasContinuation() {
5220
+ return this.remaining.length > 0;
5221
+ }
5222
+ hold(blockId, notes, bodyMinimum, freshBodyMinimum = bodyMinimum) {
5223
+ this.pending = notes.length ? { blockId, notes, bodyMinimum, freshBodyMinimum } : void 0;
5224
+ }
5225
+ reserved(page, _columnIndex = 0) {
5226
+ return page.footnotes?.length ? this.separatorHeight(page) + (page.footnoteHeightPx ?? page.footnotes.reduce((sum, note) => sum + note.heightPx, 0)) : 0;
5227
+ }
5228
+ pendingHeight(page, capacity, bodyUsed, _columnIndex = 0) {
5229
+ if (!this.pending) return 0;
5230
+ if (this.columns) {
5231
+ const existing = this.sources.get(page) ?? [];
5232
+ const height = minimumFootnoteHeight(
5233
+ [...existing, ...this.pending.notes],
5234
+ footnoteColumns(page, this.columns),
5235
+ this.reflow
5236
+ ) + this.separatorHeight(page);
5237
+ if (height + this.pending.freshBodyMinimum <= capacity)
5238
+ return Math.max(0, height - this.reserved(page));
5239
+ return Math.max(
5240
+ firstLineBottom(this.pending.notes[0]) + (page.footnotes?.length ? 0 : this.separatorHeight(page)),
5241
+ capacity - bodyUsed - this.reserved(page) - this.pending.bodyMinimum
5242
+ );
5243
+ }
5244
+ const total = this.pending.notes.reduce((sum, note) => sum + note.heightPx, 0);
5245
+ const separator = page.footnotes?.length ? 0 : this.separatorHeight(page);
5246
+ if (total + this.separatorHeight(page) + this.pending.freshBodyMinimum <= capacity)
5247
+ return total + separator;
5248
+ return Math.max(
5249
+ firstLineBottom(this.pending.notes[0]) + separator,
5250
+ capacity - bodyUsed - this.reserved(page) - this.pending.bodyMinimum
5251
+ );
5252
+ }
5253
+ /** A later column cannot grow the page-wide note area into body content already placed. */
5254
+ blockedByEarlierColumn(page, capacity, bodyBottom) {
5255
+ if (!this.pending) return false;
5256
+ if (this.columns)
5257
+ return bodyBottom + this.reserved(page) + this.pendingHeight(page, capacity, bodyBottom) > capacity;
5258
+ const total = this.pending.notes.reduce((sum, note) => sum + note.heightPx, 0);
5259
+ const minimum = firstLineBottom(this.pending.notes[0]);
5260
+ const needed = total + this.separatorHeight(page) + this.pending.freshBodyMinimum <= capacity ? total : minimum;
5261
+ return bodyBottom + this.reserved(page) + needed + (page.footnotes?.length ? 0 : this.separatorHeight(page)) > capacity;
5262
+ }
5263
+ commit(blockId, page, available, _columnIndex = 0) {
5264
+ if (this.pending?.blockId !== blockId) return false;
5265
+ const notes = this.pending.notes;
5266
+ this.pending = void 0;
5267
+ this.append(page, notes, available);
5268
+ return true;
5269
+ }
5270
+ resume(page, capacity, _columnIndex, width) {
5271
+ const notes = width === void 0 ? this.remaining : this.remaining.map((note) => this.reflow(note, width));
5272
+ this.remaining = [];
5273
+ this.append(page, notes, capacity);
5274
+ }
5275
+ append(page, notes, available) {
5276
+ if (!page.footnotes?.length && notes.length) {
5277
+ const separator = this.separator?.(page, !!notes[0]?.continued);
5278
+ if (separator) page.footnoteSeparator = separator;
5279
+ }
5280
+ if (this.columns) {
5281
+ const sources = [...this.sources.get(page) ?? [], ...notes];
5282
+ this.sources.set(page, sources);
5283
+ const geometry = footnoteColumns(page, this.columns);
5284
+ const previousHeight = page.footnoteHeightPx ?? 0;
5285
+ const capacity = Math.max(
5286
+ 0,
5287
+ available + previousHeight - (page.footnotes?.length ? 0 : this.separatorHeight(page))
5288
+ );
5289
+ let packed = balancedFootnotes(sources, geometry, capacity, this.reflow);
5290
+ if (!packed.placed.length && sources.length) {
5291
+ this.report(
5292
+ "A footnote line was taller than its available page area and was placed without splitting."
5293
+ );
5294
+ packed = balancedFootnotes(
5295
+ sources,
5296
+ geometry,
5297
+ firstLineBottom(this.reflow(sources[0], geometry[0].widthPx)),
5298
+ this.reflow
5299
+ );
5300
+ }
5301
+ page.footnotes = packed.placed;
5302
+ page.footnoteHeightPx = packed.heightPx;
5303
+ this.remaining = packed.remaining.map((note) => ({
5304
+ ...note,
5305
+ continued: !!note.continued || packed.placed.some((fragment) => fragment.id === note.id)
5306
+ }));
5307
+ return;
5308
+ }
5309
+ let top = page.footnotes?.reduce((sum, note) => sum + note.heightPx, 0) ?? 0;
5310
+ let budget = Math.max(0, available - (page.footnotes?.length ? 0 : this.separatorHeight(page)));
5311
+ for (let index = 0; index < notes.length; index++) {
5312
+ const note = notes[index];
5313
+ let split = splitNote(note, budget);
5314
+ if (!split.before && !page.footnotes?.length) {
5315
+ split = splitNote(note, firstLineBottom(note));
5316
+ this.report(
5317
+ "A footnote line was taller than its available page area and was placed without splitting."
5318
+ );
5319
+ }
5320
+ if (split.before) {
5321
+ (page.footnotes ??= []).push({ ...split.before, yPx: top });
5322
+ top += split.before.heightPx;
5323
+ budget -= split.before.heightPx;
5324
+ }
5325
+ if (split.after) {
5326
+ this.remaining.push(split.after, ...notes.slice(index + 1));
5327
+ break;
5328
+ }
5329
+ }
5330
+ }
5331
+ };
5332
+
5333
+ // docx/layout/legacy-footnote-flow.ts
5334
+ var LegacyFootnoteFlow = class {
5335
+ flow;
5336
+ views = /* @__PURE__ */ new WeakMap();
5337
+ constructor(report, separator, reflow = (note) => note) {
5338
+ this.flow = new FootnoteFlow(report, separator, void 0, reflow);
5339
+ }
5340
+ view(page, index) {
5341
+ let views = this.views.get(page);
5342
+ if (!views) {
5343
+ views = /* @__PURE__ */ new Map();
5344
+ this.views.set(page, views);
5345
+ }
5346
+ let view = views.get(index);
5347
+ if (!view) {
5348
+ const {
5349
+ footnotes: _notes,
5350
+ footnoteSeparator: _separator,
5351
+ footnoteHeightPx: _height,
5352
+ footnoteColumnAreas: _areas,
5353
+ ...geometry
5354
+ } = page;
5355
+ view = {
5356
+ ...geometry,
5357
+ widthPx: page.columns[index].widthPx,
5358
+ marginLeftPx: 0,
5359
+ marginRightPx: 0,
5360
+ columns: []
5361
+ };
5362
+ views.set(index, view);
5363
+ }
5364
+ return view;
5365
+ }
5366
+ sync(page) {
5367
+ const views = this.views.get(page);
5368
+ if (!views) return;
5369
+ const areas = [...views].filter(([, view]) => view.footnotes?.length).map(([columnIndex, view]) => {
5370
+ const column = page.columns[columnIndex];
5371
+ return {
5372
+ columnIndex,
5373
+ xPx: column.xPx,
5374
+ widthPx: column.widthPx,
5375
+ heightPx: view.footnotes.reduce((sum, note) => sum + note.heightPx, 0),
5376
+ continued: !!view.footnotes[0]?.continued,
5377
+ ...view.footnoteSeparator ? { separator: view.footnoteSeparator } : {},
5378
+ yPx: 0
5379
+ };
5380
+ });
5381
+ const height = Math.max(0, ...areas.map((area) => area.heightPx));
5382
+ for (const area of areas) area.yPx = height - area.heightPx;
5383
+ page.footnoteColumnAreas = areas;
5384
+ page.footnoteHeightPx = height;
5385
+ page.footnotes = areas.flatMap(
5386
+ (area) => this.view(page, area.columnIndex).footnotes.map((note) => ({
5387
+ ...note,
5388
+ xPx: area.xPx,
5389
+ widthPx: area.widthPx,
5390
+ columnIndex: area.columnIndex,
5391
+ yPx: area.yPx + note.yPx
5392
+ }))
5393
+ );
5394
+ }
5395
+ get hasContinuation() {
5396
+ return this.flow.hasContinuation;
5397
+ }
5398
+ hold(blockId, notes, bodyMinimum, freshBodyMinimum = bodyMinimum) {
5399
+ this.flow.hold(blockId, notes, bodyMinimum, freshBodyMinimum);
5400
+ }
5401
+ reserved(page, columnIndex = 0) {
5402
+ return this.flow.reserved(this.view(page, columnIndex));
5403
+ }
5404
+ pendingHeight(page, capacity, bodyUsed, columnIndex = 0) {
5405
+ return this.flow.pendingHeight(this.view(page, columnIndex), capacity, bodyUsed);
5406
+ }
5407
+ blockedByEarlierColumn(_page, _capacity, _bodyBottom) {
5408
+ return false;
5409
+ }
5410
+ commit(blockId, page, available, columnIndex = 0) {
5411
+ if (this.flow.commit(blockId, this.view(page, columnIndex), available)) this.sync(page);
5412
+ }
5413
+ resume(page, capacity, columnIndex) {
5414
+ if (!this.flow.hasContinuation) return;
5415
+ const indexes = columnIndex === void 0 ? page.columns.map((_, index) => index) : [columnIndex];
5416
+ for (const index of indexes) {
5417
+ if (!this.flow.hasContinuation) break;
5418
+ const view = this.view(page, index);
5419
+ if (view.footnotes?.length) continue;
5420
+ this.flow.resume(view, capacity, void 0, view.widthPx);
5421
+ }
5422
+ this.sync(page);
5423
+ }
5424
+ };
5425
+
5426
+ // docx/layout/page-cursor.ts
4971
5427
  var PageCursor = class {
4972
- constructor(pages, geometry, columns, sectionIndex = 0, continueAt, balance) {
5428
+ constructor(pages, geometry, columns, sectionIndex = 0, continueAt, balance, report = () => {
5429
+ }, separator, noteColumns, reflow, legacyFootnoteColumns = false) {
4973
5430
  this.balance = balance;
5431
+ this.noteColumns = noteColumns;
5432
+ this.legacyFootnoteColumns = legacyFootnoteColumns;
5433
+ this.footnotes = legacyFootnoteColumns ? new LegacyFootnoteFlow(report, separator, reflow) : new FootnoteFlow(report, separator, noteColumns, reflow);
4974
5434
  this.sectionIndex = sectionIndex;
4975
5435
  this.pages = pages;
4976
5436
  this.geometry = geometry;
@@ -4994,6 +5454,8 @@ var PageCursor = class {
4994
5454
  }
4995
5455
  }
4996
5456
  balance;
5457
+ noteColumns;
5458
+ legacyFootnoteColumns;
4997
5459
  pages;
4998
5460
  equalColumnWidthPx;
4999
5461
  activeColumns = [];
@@ -5004,10 +5466,7 @@ var PageCursor = class {
5004
5466
  yPx = 0;
5005
5467
  sectionIndex;
5006
5468
  pageInSection = 0;
5007
- /** Height taken by footnotes (and their separator) at the bottom of the current page. */
5008
- reservedPx = 0;
5009
- /** Footnotes of the paragraph about to be placed; they join the page its first box lands on. */
5010
- pending;
5469
+ footnotes;
5011
5470
  columnBoxes(offsetPx = 0) {
5012
5471
  const columnBoxes = [];
5013
5472
  let xPx = offsetPx;
@@ -5042,7 +5501,7 @@ var PageCursor = class {
5042
5501
  this.columnIndex = 0;
5043
5502
  this.columnTopPx = 0;
5044
5503
  this.yPx = 0;
5045
- this.reservedPx = 0;
5504
+ this.footnotes.resume(this.page, this.physicalColumnHeightPx);
5046
5505
  }
5047
5506
  get page() {
5048
5507
  return this.pages.at(-1);
@@ -5050,6 +5509,9 @@ var PageCursor = class {
5050
5509
  get column() {
5051
5510
  return expectDefined(this.activeColumns[this.columnIndex], "current page column");
5052
5511
  }
5512
+ get noteColumnIndex() {
5513
+ return this.legacyFootnoteColumns ? this.page.columns.indexOf(this.column) : this.columnIndex;
5514
+ }
5053
5515
  get columnWidthPx() {
5054
5516
  return this.column.widthPx;
5055
5517
  }
@@ -5064,6 +5526,9 @@ var PageCursor = class {
5064
5526
  endBalance() {
5065
5527
  this.balance = void 0;
5066
5528
  }
5529
+ get footnoteWidthPx() {
5530
+ return this.noteColumns ? footnoteColumns(this.page, this.noteColumns)[0].widthPx : this.columnWidthPx;
5531
+ }
5067
5532
  get y() {
5068
5533
  return this.yPx;
5069
5534
  }
@@ -5087,7 +5552,7 @@ var PageCursor = class {
5087
5552
  );
5088
5553
  }
5089
5554
  remainingHeightPx() {
5090
- return this.columnHeightPx - (this.yPx - this.columnTopPx) - this.reservedPx - this.pendingHeightPx();
5555
+ return this.columnHeightPx - (this.yPx - this.columnTopPx) - this.footnotes.reserved(this.page, this.noteColumnIndex) - this.pendingHeightPx();
5091
5556
  }
5092
5557
  /** Close this section's column band before another section shares its page. */
5093
5558
  finishBand() {
@@ -5106,16 +5571,41 @@ var PageCursor = class {
5106
5571
  return bottom;
5107
5572
  }
5108
5573
  pendingHeightPx() {
5109
- if (!this.pending) return 0;
5110
- return this.pending.heightPx + (this.page.footnotes?.length ? 0 : FOOTNOTE_SEPARATOR_PX);
5574
+ return this.footnotes.pendingHeight(
5575
+ this.page,
5576
+ this.columnHeightPx,
5577
+ this.usedColumnHeightPx,
5578
+ this.noteColumnIndex
5579
+ );
5111
5580
  }
5112
5581
  /**
5113
5582
  * Reserves room for a paragraph's footnotes on whichever page its first box is placed, so the
5114
5583
  * notes print on the same page as their reference, as Word does.
5115
5584
  */
5116
- holdFootnotes(blockId, notes) {
5117
- const heightPx = notes.reduce((sum, note) => sum + note.heightPx, 0);
5118
- this.pending = notes.length ? { blockId, notes, heightPx } : void 0;
5585
+ holdFootnotes(blockId, notes, bodyMinimum = 0, freshBodyMinimum = bodyMinimum) {
5586
+ this.footnotes.hold(blockId, notes, bodyMinimum, freshBodyMinimum);
5587
+ }
5588
+ /** Finish long notes even when the section has no more body paragraphs. */
5589
+ finishFootnotes() {
5590
+ while (this.footnotes.hasContinuation) {
5591
+ if (this.legacyFootnoteColumns) this.newColumn();
5592
+ else this.newPage();
5593
+ }
5594
+ }
5595
+ /** Continued notes can occupy an otherwise empty page, before body/table content starts. */
5596
+ advancePastFootnotes(minimumBodyHeight) {
5597
+ while (this.page.footnotes?.length && this.atColumnTop && this.remainingHeightPx() < minimumBodyHeight - 1e-6 || this.columnIndex > 0 && this.footnotes.blockedByEarlierColumn(
5598
+ this.page,
5599
+ this.columnHeightPx,
5600
+ Math.max(
5601
+ 0,
5602
+ ...this.activeColumns.slice(0, this.columnIndex).flatMap(
5603
+ (column) => column.blocks.map((block) => block.yPx + block.heightPx - this.columnTopPx)
5604
+ )
5605
+ )
5606
+ ))
5607
+ if (this.legacyFootnoteColumns) this.newColumn();
5608
+ else this.newPage();
5119
5609
  }
5120
5610
  newPage() {
5121
5611
  this.pushPage();
@@ -5125,6 +5615,8 @@ var PageCursor = class {
5125
5615
  if (this.columnIndex + 1 < this.columns.count) {
5126
5616
  this.columnIndex++;
5127
5617
  this.yPx = this.columnTopPx;
5618
+ if (this.legacyFootnoteColumns)
5619
+ this.footnotes.resume(this.page, this.physicalColumnHeightPx, this.noteColumnIndex);
5128
5620
  } else {
5129
5621
  this.pushPage();
5130
5622
  }
@@ -5138,25 +5630,54 @@ var PageCursor = class {
5138
5630
  box.yPx = this.yPx;
5139
5631
  this.column.blocks.push(box);
5140
5632
  this.yPx += advancePx;
5141
- if (this.pending && this.pending.blockId === box.blockId) {
5142
- this.reservedPx += this.pendingHeightPx();
5143
- const page = this.page;
5144
- let top = page.footnotes?.reduce((sum, note) => sum + note.heightPx, 0) ?? 0;
5145
- for (const note of this.pending.notes) {
5146
- (page.footnotes ??= []).push({ ...note, yPx: top });
5147
- top += note.heightPx;
5148
- }
5149
- this.pending = void 0;
5150
- }
5633
+ const bodyBottom = this.legacyFootnoteColumns ? this.yPx : Math.max(
5634
+ this.yPx,
5635
+ ...this.activeColumns.flatMap(
5636
+ (column) => column.blocks.map((block) => block.yPx + block.heightPx)
5637
+ )
5638
+ );
5639
+ this.footnotes.commit(
5640
+ box.blockId,
5641
+ this.page,
5642
+ this.columnHeightPx - (bodyBottom - this.columnTopPx) - this.footnotes.reserved(this.page, this.noteColumnIndex),
5643
+ this.noteColumnIndex
5644
+ );
5151
5645
  }
5152
5646
  };
5153
5647
 
5154
5648
  // docx/layout/flow-paragraph.ts
5155
5649
  function placeParagraph(cursor, placement, note) {
5156
5650
  const { paragraph, spacingBeforePx, requiredTogetherPx, reflow } = placement;
5157
- let layout = placement.layout;
5651
+ const trailingBreakLine = (result) => {
5652
+ const last = result.lines.at(-1);
5653
+ if (!placement.trimLeadingColumnBreak || !last || !result.columnBreakAfterLine.has(result.lines.length - 1))
5654
+ return result;
5655
+ return {
5656
+ ...result,
5657
+ contentHeightPx: result.contentHeightPx + last.heightPx,
5658
+ lines: [
5659
+ ...result.lines,
5660
+ {
5661
+ ...last,
5662
+ yPx: last.yPx + last.heightPx,
5663
+ fragments: [],
5664
+ sourceStart: last.sourceEnd,
5665
+ sourceEnd: last.sourceEnd
5666
+ }
5667
+ ]
5668
+ };
5669
+ };
5670
+ let layout = trailingBreakLine(placement.layout);
5158
5671
  let width = cursor.columnWidthPx;
5159
5672
  if (paragraph.pageBreakBefore && cursor.hasPageContent) cursor.newPage();
5673
+ if (requiredTogetherPx > 0 && placement.footnotes) {
5674
+ cursor.holdFootnotes(
5675
+ paragraph.id,
5676
+ placement.footnotes(layout.lines, true),
5677
+ requiredTogetherPx,
5678
+ layout.lines[0]?.heightPx ?? 0
5679
+ );
5680
+ }
5160
5681
  if (requiredTogetherPx > 0 && requiredTogetherPx <= cursor.columnHeightPx && requiredTogetherPx > cursor.remainingHeightPx() && !cursor.atColumnTop) {
5161
5682
  cursor.newColumn();
5162
5683
  }
@@ -5164,9 +5685,23 @@ function placeParagraph(cursor, placement, note) {
5164
5685
  let lineCursor = 0;
5165
5686
  let nextToken = 0;
5166
5687
  let firstSegment = true;
5688
+ const lineHeight = (line2, index) => placement.trimLeadingColumnBreak && layout.columnBreakAfterLine.has(index) && line2.fragments.every((fragment) => !fragment.text && !fragment.object) ? 0 : line2.heightPx;
5689
+ const placedNotes = /* @__PURE__ */ new Set();
5690
+ const notesFor = (lines) => (placement.footnotes?.(lines, firstSegment) ?? []).filter((item) => !placedNotes.has(item.id));
5691
+ const hold = (lines, bodyMinimum) => {
5692
+ const notes = notesFor(lines);
5693
+ if (placement.footnotes)
5694
+ cursor.holdFootnotes(
5695
+ paragraph.id,
5696
+ notes,
5697
+ bodyMinimum,
5698
+ Math.max(0, ...lines.map((line2) => line2.heightPx))
5699
+ );
5700
+ return notes;
5701
+ };
5167
5702
  const reflowIfNeeded = () => {
5168
5703
  if (!reflow || cursor.columnWidthPx === width) return;
5169
- layout = reflow(nextToken);
5704
+ layout = trailingBreakLine(reflow(nextToken));
5170
5705
  lineCursor = 0;
5171
5706
  width = cursor.columnWidthPx;
5172
5707
  };
@@ -5180,15 +5715,27 @@ function placeParagraph(cursor, placement, note) {
5180
5715
  }
5181
5716
  while (lineCursor < layout.lines.length) {
5182
5717
  reflowIfNeeded();
5183
- const budget = cursor.remainingHeightPx() - (firstSegment ? spacingBeforePx : 0);
5718
+ hold(
5719
+ layout.lines.slice(lineCursor, lineCursor + 1),
5720
+ layout.lines[lineCursor] ? lineHeight(layout.lines[lineCursor], lineCursor) : 0
5721
+ );
5722
+ cursor.advancePastFootnotes(
5723
+ layout.lines[lineCursor] ? lineHeight(layout.lines[lineCursor], lineCursor) : 0
5724
+ );
5725
+ reflowIfNeeded();
5184
5726
  let cumulative = 0;
5185
5727
  let end = lineCursor;
5186
5728
  let forced;
5187
5729
  for (let i = lineCursor; i < layout.lines.length; i++) {
5188
5730
  const line2 = expectDefined(layout.lines[i], "paragraph line index");
5189
5731
  const gap = i === lineCursor && !firstSegment ? 0 : line2.gapBeforePx ?? 0;
5190
- const height = line2.heightPx + gap;
5191
- if (cumulative + height > budget && (end > lineCursor || !cursor.atColumnTop)) break;
5732
+ const height = lineHeight(line2, i) + gap;
5733
+ hold(
5734
+ layout.lines.slice(lineCursor, i + 1),
5735
+ cumulative + height + (firstSegment ? spacingBeforePx : 0)
5736
+ );
5737
+ const budget = cursor.remainingHeightPx() - (firstSegment ? spacingBeforePx : 0);
5738
+ if (cumulative + height > budget + 1e-6 && (end > lineCursor || !cursor.atColumnTop)) break;
5192
5739
  cumulative += height;
5193
5740
  end = i + 1;
5194
5741
  if (layout.pageBreakAfterLine.has(i)) {
@@ -5213,9 +5760,17 @@ function placeParagraph(cursor, placement, note) {
5213
5760
  );
5214
5761
  placedCount = Math.max(1, end - lineCursor);
5215
5762
  }
5763
+ const committedNotes = hold(
5764
+ layout.lines.slice(lineCursor, lineCursor + placedCount),
5765
+ cumulative + (firstSegment ? spacingBeforePx : 0)
5766
+ );
5216
5767
  const first3 = expectDefined(layout.lines[lineCursor], "first line of paragraph segment");
5217
5768
  const origin = first3.yPx - (firstSegment ? (first3.gapBeforePx ?? 0) + layout.insetTopPx : 0);
5218
- const segmentLines = layout.lines.slice(lineCursor, lineCursor + placedCount).map((line2) => ({ ...line2, yPx: line2.yPx - origin }));
5769
+ const segmentLines = layout.lines.slice(lineCursor, lineCursor + placedCount).map((line2, index) => ({
5770
+ ...line2,
5771
+ yPx: line2.yPx - origin,
5772
+ heightPx: lineHeight(line2, lineCursor + index)
5773
+ }));
5219
5774
  const isFinal = lineCursor + placedCount >= layout.lines.length;
5220
5775
  const lastLine = segmentLines.at(-1);
5221
5776
  const isLastSegment = lineCursor + placedCount >= layout.lines.length;
@@ -5230,6 +5785,7 @@ function placeParagraph(cursor, placement, note) {
5230
5785
  };
5231
5786
  const advance = (firstSegment ? spacingBeforePx : 0) + segmentHeight + (isFinal ? layout.spacingAfterPx : 0);
5232
5787
  cursor.place(box, advance);
5788
+ for (const item of committedNotes) placedNotes.add(item.id);
5233
5789
  nextToken = layout.lines[lineCursor + placedCount - 1]?.nextToken ?? nextToken;
5234
5790
  lineCursor += placedCount;
5235
5791
  firstSegment = false;
@@ -5239,8 +5795,170 @@ function placeParagraph(cursor, placement, note) {
5239
5795
  }
5240
5796
  }
5241
5797
 
5798
+ // docx/layout/table-footnotes.ts
5799
+ function prefix(row, cut, cellCuts) {
5800
+ const cells = row.cells.map((cell, index) => {
5801
+ const contentCut = (cellCuts?.[index] ?? cut) - (row.cellPadding?.[index]?.bottom ?? 0);
5802
+ const boxes = [];
5803
+ for (const box of cell) {
5804
+ if (box.yPx >= contentCut + 1e-6) break;
5805
+ if (box.yPx + box.heightPx <= contentCut + 1e-6) {
5806
+ boxes.push(box);
5807
+ continue;
5808
+ }
5809
+ const lines = [];
5810
+ for (const line2 of box.lines) {
5811
+ if (box.yPx + line2.yPx + line2.heightPx > contentCut + 1e-6) break;
5812
+ lines.push(line2);
5813
+ }
5814
+ if (lines.length)
5815
+ boxes.push({ ...box, lines, heightPx: lines.at(-1).yPx + lines.at(-1).heightPx });
5816
+ continue;
5817
+ }
5818
+ return boxes;
5819
+ });
5820
+ if (cells.every(
5821
+ (cell, index) => cell.length === row.cells[index].length && cell.every(
5822
+ (box, boxIndex) => box.lines.length === row.cells[index][boxIndex].lines.length
5823
+ )
5824
+ ))
5825
+ return row;
5826
+ return {
5827
+ ...row,
5828
+ cells,
5829
+ heightPx: Math.max(
5830
+ 0,
5831
+ ...cells.flatMap(
5832
+ (cell, index) => cell.map((box) => box.yPx + box.heightPx + (row.cellPadding?.[index]?.bottom ?? 0))
5833
+ )
5834
+ )
5835
+ };
5836
+ }
5837
+ var TableFootnotes = class {
5838
+ constructor(cursor, table, height, resolve) {
5839
+ this.cursor = cursor;
5840
+ this.table = table;
5841
+ this.height = height;
5842
+ this.resolve = resolve;
5843
+ for (const row of table.rows)
5844
+ for (const cell of row.cells)
5845
+ for (const paragraph of cellParagraphs(cell))
5846
+ if (paragraph.footnotes?.length) this.paragraphs.set(paragraph.id, paragraph);
5847
+ }
5848
+ cursor;
5849
+ table;
5850
+ height;
5851
+ resolve;
5852
+ paragraphs = /* @__PURE__ */ new Map();
5853
+ placed = /* @__PURE__ */ new Set();
5854
+ fragment = /* @__PURE__ */ new Map();
5855
+ references(row) {
5856
+ const refs = /* @__PURE__ */ new Map();
5857
+ for (const cell of row.cells)
5858
+ for (const box of cell) {
5859
+ const source = this.paragraphs.get(box.blockId);
5860
+ if (!source) continue;
5861
+ for (const note of this.resolve(
5862
+ source,
5863
+ box.lines,
5864
+ (box.lines[0]?.sourceStart ?? 0) === 0
5865
+ )) {
5866
+ if (this.placed.has(note.id)) continue;
5867
+ const line2 = box.lines.find(
5868
+ (line3) => line3.fragments.some(
5869
+ (fragment) => source.runs[fragment.runIndex]?.footnoteReferenceId === note.id
5870
+ )
5871
+ ) ?? box.lines[0];
5872
+ const bottom = Math.min(
5873
+ row.heightPx,
5874
+ box.yPx + (line2?.yPx ?? 0) + (line2?.heightPx ?? box.heightPx)
5875
+ );
5876
+ const prior = refs.get(note.id);
5877
+ if (!prior || bottom < prior.bottom) refs.set(note.id, { note, bottom });
5878
+ }
5879
+ }
5880
+ return [...refs.values()];
5881
+ }
5882
+ hold(refs, minimumBody = 0) {
5883
+ const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5884
+ const bodyHeight = Math.max(referenceBottom, minimumBody);
5885
+ this.cursor.holdFootnotes(
5886
+ this.table.id,
5887
+ [...refs.values()].map((ref) => ref.note),
5888
+ bodyHeight,
5889
+ this.cursor.usedColumnHeightPx + referenceBottom
5890
+ );
5891
+ }
5892
+ holdCandidate(row) {
5893
+ const refs = new Map(this.fragment);
5894
+ for (const ref of this.references(row))
5895
+ if (!refs.has(ref.note.id))
5896
+ refs.set(ref.note.id, { ...ref, bottom: this.height() + ref.bottom });
5897
+ const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5898
+ const contentBottom = this.height() + Math.max(0, ...row.cells.flatMap((cell) => cell.map((box) => box.yPx + box.heightPx)));
5899
+ const paddingBottom = Math.max(0, ...(row.cellPadding ?? []).map((padding) => padding.bottom));
5900
+ this.hold(
5901
+ refs,
5902
+ contentBottom <= referenceBottom + 1e-6 ? this.height() + row.heightPx : referenceBottom + paddingBottom
5903
+ );
5904
+ }
5905
+ /** Largest complete prefix that keeps each new marker with available note content. */
5906
+ choose(row, atomic) {
5907
+ this.cursor.holdFootnotes(this.table.id, [], 0);
5908
+ const available = this.cursor.remainingHeightPx() - this.height();
5909
+ if (row.heightPx <= available + 1e-6) {
5910
+ this.holdCandidate(row);
5911
+ if (row.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6)
5912
+ return { layout: row, cut: row.heightPx };
5913
+ }
5914
+ if (!atomic) {
5915
+ const cuts = /* @__PURE__ */ new Set();
5916
+ for (const [index, cell] of row.cells.entries())
5917
+ for (const box of cell) {
5918
+ if (box.yPx > available + 1e-6) break;
5919
+ for (const line2 of box.lines) {
5920
+ const bottom = box.yPx + line2.yPx + line2.heightPx + (row.cellPadding?.[index]?.bottom ?? 0);
5921
+ if (bottom > available + 1e-6) break;
5922
+ if (bottom > 0) cuts.add(bottom);
5923
+ }
5924
+ }
5925
+ for (const cut of [...cuts].sort((a, b) => b - a)) {
5926
+ const candidate = prefix(row, cut);
5927
+ this.holdCandidate(candidate);
5928
+ if (candidate.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6) {
5929
+ const space = this.cursor.remainingHeightPx() - this.height();
5930
+ const cellCuts = row.cells.map(
5931
+ (cell) => cell.some((box) => this.paragraphs.has(box.blockId)) ? cut : space
5932
+ );
5933
+ const expanded = prefix(row, cut, cellCuts);
5934
+ this.holdCandidate(expanded);
5935
+ if (expanded.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6)
5936
+ return { layout: expanded, cut, cellCuts };
5937
+ this.holdCandidate(candidate);
5938
+ return { layout: candidate, cut };
5939
+ }
5940
+ }
5941
+ }
5942
+ this.holdFragment();
5943
+ return void 0;
5944
+ }
5945
+ add(row, repeated) {
5946
+ if (repeated) return;
5947
+ for (const ref of this.references(row))
5948
+ if (!this.fragment.has(ref.note.id))
5949
+ this.fragment.set(ref.note.id, { ...ref, bottom: this.height() + ref.bottom });
5950
+ }
5951
+ holdFragment() {
5952
+ this.hold(this.fragment);
5953
+ }
5954
+ flushed() {
5955
+ for (const id of this.fragment.keys()) this.placed.add(id);
5956
+ this.fragment.clear();
5957
+ }
5958
+ };
5959
+
5242
5960
  // docx/layout/flow-table.ts
5243
- function placeTable(cursor, table, measurer, note) {
5961
+ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
5244
5962
  const firstBodyRow = table.rows.findIndex((row) => !row.isHeader);
5245
5963
  const headerRows = table.rows.slice(0, firstBodyRow < 0 ? table.rows.length : firstBodyRow);
5246
5964
  const xPosition = () => {
@@ -5249,7 +5967,9 @@ function placeTable(cursor, table, measurer, note) {
5249
5967
  };
5250
5968
  let fragmentRows = [];
5251
5969
  let fragmentHeight = 0;
5970
+ const footnotes = resolveFootnotes ? new TableFootnotes(cursor, table, () => fragmentHeight, resolveFootnotes) : void 0;
5252
5971
  const toRowBox = (layout, repeated) => {
5972
+ footnotes?.add(layout, repeated);
5253
5973
  const box = {
5254
5974
  yPx: fragmentHeight,
5255
5975
  heightPx: layout.heightPx,
@@ -5261,7 +5981,10 @@ function placeTable(cursor, table, measurer, note) {
5261
5981
  return box;
5262
5982
  };
5263
5983
  function flushFragment() {
5264
- if (!fragmentRows.length) return;
5984
+ if (!fragmentRows.length) {
5985
+ footnotes?.holdFragment();
5986
+ return;
5987
+ }
5265
5988
  const box = {
5266
5989
  kind: "table",
5267
5990
  blockId: table.id,
@@ -5270,19 +5993,89 @@ function placeTable(cursor, table, measurer, note) {
5270
5993
  heightPx: fragmentHeight,
5271
5994
  rows: fragmentRows
5272
5995
  };
5996
+ footnotes?.holdFragment();
5273
5997
  cursor.place(box, fragmentHeight);
5998
+ footnotes?.flushed();
5274
5999
  fragmentRows = [];
5275
6000
  fragmentHeight = 0;
5276
6001
  }
5277
6002
  function startNewFragment() {
5278
6003
  flushFragment();
5279
6004
  cursor.newColumn();
6005
+ addHeaders();
6006
+ }
6007
+ function addHeaders() {
5280
6008
  for (const header of headerRows)
5281
6009
  fragmentRows.push(toRowBox(layoutRow(header, cursor.columnWidthPx, measurer, note), true));
5282
6010
  }
5283
6011
  function placeRow(row, initial, cantSplit) {
5284
6012
  let remainingRow = initial;
5285
6013
  while (remainingRow) {
6014
+ const minimum = cantSplit ? Math.min(remainingRow.heightPx, cursor.columnHeightPx) : Math.min(
6015
+ ...remainingRow.cells.flatMap(
6016
+ (cell) => cell.map(
6017
+ (paragraph) => paragraph.yPx + (paragraph.lines[0]?.yPx ?? 0) + (paragraph.lines[0]?.heightPx ?? 0)
6018
+ )
6019
+ )
6020
+ );
6021
+ if (fragmentRows.every((item) => item.repeated)) {
6022
+ const width = cursor.columnWidthPx;
6023
+ cursor.advancePastFootnotes(fragmentHeight + (Number.isFinite(minimum) ? minimum : 0));
6024
+ if (width !== cursor.columnWidthPx) {
6025
+ remainingRow = layoutRow(
6026
+ remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6027
+ cursor.columnWidthPx,
6028
+ measurer,
6029
+ note,
6030
+ remainingRow.cells
6031
+ );
6032
+ if (fragmentRows.length) {
6033
+ fragmentRows = [];
6034
+ fragmentHeight = 0;
6035
+ addHeaders();
6036
+ }
6037
+ continue;
6038
+ }
6039
+ }
6040
+ if (footnotes) {
6041
+ const selected = footnotes.choose(remainingRow, cantSplit);
6042
+ if (selected) {
6043
+ if (selected.layout === remainingRow) {
6044
+ fragmentRows.push(toRowBox(remainingRow, false));
6045
+ remainingRow = null;
6046
+ continue;
6047
+ }
6048
+ const { before: before2, after: after2 } = splitRowAtHeight(
6049
+ remainingRow,
6050
+ selected.cut + 1e-6,
6051
+ selected.cellCuts?.map((cut) => cut + 1e-6)
6052
+ );
6053
+ fragmentRows.push(toRowBox(before2, false));
6054
+ const previousWidth2 = cursor.columnWidthPx;
6055
+ startNewFragment();
6056
+ remainingRow = after2 && previousWidth2 !== cursor.columnWidthPx ? layoutRow(
6057
+ { ...row, heightPx: 0, heightRule: "atLeast" },
6058
+ cursor.columnWidthPx,
6059
+ measurer,
6060
+ note,
6061
+ after2.cells
6062
+ ) : after2;
6063
+ continue;
6064
+ }
6065
+ if (fragmentRows.length || !cursor.atColumnTop) {
6066
+ const previousWidth2 = cursor.columnWidthPx;
6067
+ startNewFragment();
6068
+ if (previousWidth2 !== cursor.columnWidthPx)
6069
+ remainingRow = layoutRow(
6070
+ remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6071
+ cursor.columnWidthPx,
6072
+ measurer,
6073
+ note,
6074
+ remainingRow === initial ? void 0 : remainingRow.cells
6075
+ );
6076
+ continue;
6077
+ }
6078
+ }
5286
6079
  const remaining = cursor.remainingHeightPx() - fragmentHeight;
5287
6080
  if (remainingRow.heightPx <= remaining) {
5288
6081
  fragmentRows.push(toRowBox(remainingRow, false));
@@ -5294,6 +6087,18 @@ function placeTable(cursor, table, measurer, note) {
5294
6087
  startNewFragment();
5295
6088
  remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
5296
6089
  }
6090
+ const noteWidth = cursor.columnWidthPx;
6091
+ cursor.advancePastFootnotes(
6092
+ fragmentHeight + Math.min(remainingRow.heightPx, cursor.columnHeightPx)
6093
+ );
6094
+ if (noteWidth !== cursor.columnWidthPx) {
6095
+ remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
6096
+ if (fragmentRows.length) {
6097
+ fragmentRows = [];
6098
+ fragmentHeight = 0;
6099
+ addHeaders();
6100
+ }
6101
+ }
5297
6102
  const freshRemaining = cursor.remainingHeightPx() - fragmentHeight;
5298
6103
  if (remainingRow.heightPx > freshRemaining)
5299
6104
  note('A table row marked "keep row together" is taller than an empty page/column.');
@@ -5324,11 +6129,37 @@ function placeTable(cursor, table, measurer, note) {
5324
6129
  }
5325
6130
  for (const row of table.rows) {
5326
6131
  const layout = layoutRow(row, cursor.columnWidthPx, measurer, note);
5327
- placeRow(row, layout, Boolean(row.cantSplit) || headerRows.includes(row));
6132
+ placeRow(
6133
+ row,
6134
+ layout,
6135
+ Boolean(row.cantSplit) || headerRows.includes(row) || !!footnotes && row.heightRule === "exact"
6136
+ );
5328
6137
  }
5329
6138
  flushFragment();
5330
6139
  }
5331
6140
 
6141
+ // docx/layout/reference-footnotes.ts
6142
+ function createReferenceFootnotes(width, measurer, report) {
6143
+ const cache = /* @__PURE__ */ new Map();
6144
+ return (paragraph, lines, includeUnanchored) => {
6145
+ const selected = new Set(
6146
+ lines.flatMap(
6147
+ (line2) => line2.fragments.map((fragment) => paragraph.runs[fragment.runIndex]?.footnoteReferenceId)
6148
+ )
6149
+ );
6150
+ const anchored = new Set(paragraph.runs.map((run) => run.footnoteReferenceId));
6151
+ return (paragraph.footnotes ?? []).filter((note) => selected.has(note.id) || includeUnanchored && !anchored.has(note.id)).map((note) => {
6152
+ const key = `${note.id}:${width()}`;
6153
+ const prior = cache.get(key);
6154
+ if (prior) return prior;
6155
+ const stacked = stackParagraphs(note.paragraphs, width(), measurer, report);
6156
+ const box = { id: note.id, yPx: 0, heightPx: stacked.heightPx, paragraphs: stacked.boxes };
6157
+ cache.set(key, box);
6158
+ return box;
6159
+ });
6160
+ };
6161
+ }
6162
+
5332
6163
  // docx/layout/wrap.ts
5333
6164
  var SIDE_DISTANCE_PX = 12;
5334
6165
  var MIN_SIDE_WIDTH_PX = 48;
@@ -5382,20 +6213,117 @@ function lineBoxesFor(exclusions, topPx, columnLeftPx, columnWidthPx) {
5382
6213
  // docx/layout/section-flow.ts
5383
6214
  var SectionFlow = class {
5384
6215
  constructor(section, pages, measurer, note, sectionIndex = 0, exclusions, continueAt, balance) {
6216
+ this.section = section;
5385
6217
  this.exclusions = exclusions;
5386
6218
  this.balance = balance;
6219
+ const noteColumns = section.footnoteColumns ?? (section.modernFootnoteSeparator ? section.columns ?? { count: 1, gapPx: 0 } : void 0);
6220
+ if (!noteColumns && !section.legacyFootnoteColumns && (section.columns?.count ?? 1) > 1 && section.blocks.some(
6221
+ (block) => block.kind === "paragraph" ? !!block.footnotes?.length : block.rows.some(
6222
+ (row) => row.cells.some(
6223
+ (cell) => cellParagraphs(cell).some((paragraph) => !!paragraph.footnotes?.length)
6224
+ )
6225
+ )
6226
+ ))
6227
+ note(
6228
+ "Multi-column footnotes use a page-wide area; native per-column footnote balancing is not yet modeled."
6229
+ );
6230
+ const sources = /* @__PURE__ */ new Map();
6231
+ for (const block of section.blocks) {
6232
+ const paragraphs = block.kind === "paragraph" ? [block] : block.rows.flatMap((row) => row.cells.flatMap(cellParagraphs));
6233
+ for (const paragraph of paragraphs)
6234
+ for (const source of paragraph.footnotes ?? []) sources.set(source.id, source);
6235
+ }
6236
+ const reflowCache = /* @__PURE__ */ new Map();
6237
+ const reflow = (box, width) => {
6238
+ const source = sources.get(box.id);
6239
+ if (!source) return box;
6240
+ const ids = new Set(box.paragraphs.map((paragraph) => paragraph.blockId));
6241
+ const key = JSON.stringify([box.id, width, [...ids], box.sourceTokenStarts]);
6242
+ const cached = reflowCache.get(key);
6243
+ if (cached) return { ...cached, continued: !!box.continued };
6244
+ const paragraphs = source.paragraphs.filter((paragraph) => ids.has(paragraph.id)).map(
6245
+ (paragraph) => box.sourceTokenStarts?.[paragraph.id] ? { ...paragraph, spacingBeforeTwips: NO_TWIPS } : paragraph
6246
+ );
6247
+ const stacked = stackParagraphs(
6248
+ paragraphs,
6249
+ width,
6250
+ measurer,
6251
+ note,
6252
+ 0,
6253
+ new Map(Object.entries(box.sourceTokenStarts ?? {}))
6254
+ );
6255
+ const result = { ...box, yPx: 0, heightPx: stacked.heightPx, paragraphs: stacked.boxes };
6256
+ reflowCache.set(key, result);
6257
+ return result;
6258
+ };
5387
6259
  this.blocks = section.blocks;
5388
6260
  this.measurer = measurer;
5389
6261
  this.note = note;
6262
+ const separatorCache = /* @__PURE__ */ new Map();
5390
6263
  this.cursor = new PageCursor(
5391
6264
  pages,
5392
6265
  section.page,
5393
6266
  section.columns ?? { count: 1, gapPx: 0 },
5394
6267
  sectionIndex,
5395
6268
  continueAt,
5396
- balance
6269
+ balance,
6270
+ note,
6271
+ (page, continued) => {
6272
+ const paragraphs = section.footnoteSeparators?.[continued && !section.modernFootnoteSeparator ? "continuationSeparator" : "separator"];
6273
+ if (!paragraphs) return void 0;
6274
+ const width = page.widthPx - page.marginLeftPx - page.marginRightPx;
6275
+ const key = `${width}:${continued}`;
6276
+ const cached = separatorCache.get(key);
6277
+ if (cached) return cached;
6278
+ const adapted = paragraphs.map((paragraph) => {
6279
+ const probe = section.legacyFootnoteColumns ? layoutParagraph(
6280
+ {
6281
+ ...paragraph,
6282
+ runs: paragraph.runs.map(
6283
+ (run) => run.object?.noteSeparator ? { ...run, object: { ...run.object, widthPx: 0 } } : run
6284
+ )
6285
+ },
6286
+ width,
6287
+ measurer,
6288
+ note
6289
+ ) : void 0;
6290
+ return {
6291
+ ...paragraph,
6292
+ runs: paragraph.runs.map(
6293
+ (run, index) => run.object?.noteSeparator ? {
6294
+ ...run,
6295
+ object: {
6296
+ ...run.object,
6297
+ widthPx: section.legacyFootnoteColumns ? Math.min(
6298
+ run.object.noteSeparator === "continuationSeparator" ? width : 192,
6299
+ Math.max(
6300
+ 0,
6301
+ width - (probe?.lines.flatMap((line2) => line2.fragments).find(
6302
+ (fragment) => fragment.runIndex === index && fragment.object?.noteSeparator
6303
+ )?.xPx ?? 0)
6304
+ )
6305
+ ) : run.object.noteSeparator === "continuationSeparator" ? width : Math.min(192, width)
6306
+ }
6307
+ } : run
6308
+ )
6309
+ };
6310
+ });
6311
+ const stacked = stackParagraphs(adapted, width, measurer, note);
6312
+ const result = { heightPx: stacked.heightPx, paragraphs: stacked.boxes };
6313
+ separatorCache.set(key, result);
6314
+ return result;
6315
+ },
6316
+ noteColumns,
6317
+ reflow,
6318
+ section.legacyFootnoteColumns
6319
+ );
6320
+ this.referenceFootnotes = createReferenceFootnotes(
6321
+ () => this.cursor.footnoteWidthPx,
6322
+ measurer,
6323
+ note
5397
6324
  );
5398
6325
  }
6326
+ section;
5399
6327
  exclusions;
5400
6328
  balance;
5401
6329
  cursor;
@@ -5405,6 +6333,7 @@ var SectionFlow = class {
5405
6333
  measurer;
5406
6334
  note;
5407
6335
  paragraphCache = /* @__PURE__ */ new Map();
6336
+ referenceFootnotes;
5408
6337
  firstRowHeightCache = /* @__PURE__ */ new Map();
5409
6338
  paragraphLayout(index) {
5410
6339
  const key = `${index}:${this.cursor.columnWidthPx}`;
@@ -5479,7 +6408,17 @@ var SectionFlow = class {
5479
6408
  for (let index = 0; index < this.blocks.length; index++) {
5480
6409
  const block = expectDefined(this.blocks[index], "flow block index");
5481
6410
  if (block.kind === "table") {
5482
- placeTable(this.cursor, block, this.measurer, this.note);
6411
+ placeTable(
6412
+ this.cursor,
6413
+ block,
6414
+ this.measurer,
6415
+ this.note,
6416
+ block.rows.some(
6417
+ (row) => row.cells.some(
6418
+ (cell) => cellParagraphs(cell).some((paragraph) => paragraph.footnotes?.length)
6419
+ )
6420
+ ) ? this.referenceFootnotes : void 0
6421
+ );
5483
6422
  if (this.balance?.pageIndex === this.cursor.page.index && !this.cursor.columnContentFits)
5484
6423
  this.balanceValid = false;
5485
6424
  continue;
@@ -5487,19 +6426,7 @@ var SectionFlow = class {
5487
6426
  if (block.afterTableSectionBreak) this.cursor.endBalance();
5488
6427
  const spacingBeforePx = this.spacingBeforeFor(index);
5489
6428
  const layout = this.wrappedLayout(index, spacingBeforePx) ?? this.paragraphLayout(index);
5490
- if (block.footnotes?.length)
5491
- this.cursor.holdFootnotes(
5492
- block.id,
5493
- block.footnotes.map((note) => {
5494
- const stacked = stackParagraphs(
5495
- note.paragraphs,
5496
- this.cursor.columnWidthPx,
5497
- this.measurer,
5498
- this.note
5499
- );
5500
- return { id: note.id, yPx: 0, heightPx: stacked.heightPx, paragraphs: stacked.boxes };
5501
- })
5502
- );
6429
+ const footnotes = (lines, includeUnanchored) => this.referenceFootnotes(block, lines, includeUnanchored);
5503
6430
  const requiredTogetherPx = block.keepNext ? this.requiredKeepHeight(index) : block.keepLines ? spacingBeforePx + layout.contentHeightPx + layout.spacingAfterPx : 0;
5504
6431
  if (requiredTogetherPx > this.cursor.columnHeightPx && requiredTogetherPx <= this.cursor.physicalColumnHeightPx)
5505
6432
  this.balanceValid = false;
@@ -5508,7 +6435,9 @@ var SectionFlow = class {
5508
6435
  {
5509
6436
  paragraph: block,
5510
6437
  layout,
6438
+ footnotes,
5511
6439
  spacingBeforePx,
6440
+ ...this.section.modernFootnoteSeparator || this.section.legacyFootnoteColumns ? { trimLeadingColumnBreak: true } : {},
5512
6441
  requiredTogetherPx,
5513
6442
  reflow: (token) => layoutParagraph(
5514
6443
  block,
@@ -5524,6 +6453,7 @@ var SectionFlow = class {
5524
6453
  if (this.balance?.pageIndex === this.cursor.page.index && this.cursor.usedColumnHeightPx > this.cursor.columnHeightPx)
5525
6454
  this.balanceValid = false;
5526
6455
  }
6456
+ this.cursor.finishFootnotes();
5527
6457
  }
5528
6458
  };
5529
6459
 
@@ -5566,8 +6496,16 @@ function insertParityBlank(pages, section) {
5566
6496
  const nextNumber = pages.length + 1;
5567
6497
  const wanted = section.break === "evenPage" ? 0 : 1;
5568
6498
  if (nextNumber % 2 === wanted) return;
6499
+ const {
6500
+ footnotes: _footnotes,
6501
+ footnoteSeparator: _separator,
6502
+ footnoteHeightPx: _noteHeight,
6503
+ footnoteColumnAreas: _noteColumnAreas,
6504
+ floats: _floats,
6505
+ ...geometry
6506
+ } = previous;
5569
6507
  pages.push({
5570
- ...previous,
6508
+ ...geometry,
5571
6509
  index: pages.length,
5572
6510
  pageInSection: previous.pageInSection + 1,
5573
6511
  columns: previous.columns.map((column) => ({ ...column, blocks: [] }))
@@ -5581,7 +6519,12 @@ function alignVertically(pages, section, note) {
5581
6519
  return;
5582
6520
  }
5583
6521
  for (const page of pages) {
5584
- const available = page.heightPx - page.marginTopPx - page.marginBottomPx;
6522
+ const footnoteHeight = page.footnoteColumnAreas?.length ? Math.max(
6523
+ ...page.footnoteColumnAreas.map(
6524
+ (area) => area.heightPx + (area.separator?.heightPx ?? FOOTNOTE_SEPARATOR_PX)
6525
+ )
6526
+ ) : page.footnotes?.length ? (page.footnoteSeparator?.heightPx ?? FOOTNOTE_SEPARATOR_PX) + (page.footnoteHeightPx ?? page.footnotes.reduce((sum, footnote) => sum + footnote.heightPx, 0)) : 0;
6527
+ const available = page.heightPx - page.marginTopPx - page.marginBottomPx - footnoteHeight;
5585
6528
  const used = Math.max(
5586
6529
  0,
5587
6530
  ...page.columns.flatMap((column) => column.blocks.map((block) => block.yPx + block.heightPx))
@@ -5774,6 +6717,11 @@ function adaptTable(table, model, adaptParagraph) {
5774
6717
  const fill = cssHex(visuals.shadingFill);
5775
6718
  return {
5776
6719
  paragraphs: cell.paragraphs.map(adaptParagraph),
6720
+ ...cell.sourceBlocks ? {
6721
+ blocks: cellSourceBlocks(cell).map(
6722
+ (block) => block.type === "paragraph" ? adaptParagraph(block) : adaptTable(block, model, adaptParagraph)
6723
+ )
6724
+ } : {},
5777
6725
  ...width !== void 0 ? { widthPx: width } : {},
5778
6726
  ...grid && xPx !== void 0 ? { xPx } : {},
5779
6727
  padding,
@@ -5953,7 +6901,13 @@ function adaptDocumentModel(model, note = () => {
5953
6901
  const script = formatting.verticalAlign === "superscript" || noteText !== void 0 && formatting.verticalAlign === void 0 ? "super" : formatting.verticalAlign === "subscript" ? "sub" : void 0;
5954
6902
  const image = run.image;
5955
6903
  if (image?.unsupported === "Text box") reportOnce(TEXT_BOX_NOTE);
5956
- const object = image && !image.anchored ? {
6904
+ const object = run.noteSeparator ? {
6905
+ partName: "",
6906
+ contentType: "",
6907
+ widthPx: 192,
6908
+ heightPx: 1,
6909
+ noteSeparator: run.noteSeparator
6910
+ } : image && !image.anchored ? {
5957
6911
  partName: image.partName,
5958
6912
  contentType: image.contentType,
5959
6913
  widthPx: image.widthPx,
@@ -5961,8 +6915,10 @@ function adaptDocumentModel(model, note = () => {
5961
6915
  ...image.chart ? { chart: docxChartPaint(image.chart, image.widthPx, image.heightPx, theme) } : {}
5962
6916
  } : void 0;
5963
6917
  return {
6918
+ sourceLength: reviewSourceLength(run),
5964
6919
  // Hidden text (`w:vanish`) takes no space when printed, as in Word by default.
5965
6920
  text: image || formatting.vanish ? "" : formatting.caps ? text2.toUpperCase() : text2,
6921
+ ...run.noteReference?.kind === "footnote" ? { footnoteReferenceId: run.noteReference.id } : {},
5966
6922
  ...object ? { object } : {},
5967
6923
  ...definedProps({
5968
6924
  bold: formatting.bold,
@@ -6061,6 +7017,10 @@ function adaptDocumentModel(model, note = () => {
6061
7017
  if (block.type === "paragraph") return adaptParagraph(block);
6062
7018
  reportOnce(TABLE_ROW_NOTE);
6063
7019
  reportOnce(VERTICAL_MERGE_NOTE);
7020
+ if (block.rows.some((row) => row.some((cell) => cell.nestedTables?.length)))
7021
+ reportOnce(
7022
+ "Nested tables are projected as read-only cell paragraphs in Print Layout; their borders, shading, vertical merge and repeated header geometry are not rendered."
7023
+ );
6064
7024
  return adaptTable(block, model, adaptParagraph);
6065
7025
  }
6066
7026
  const blocks = [
@@ -6070,7 +7030,25 @@ function adaptDocumentModel(model, note = () => {
6070
7030
  groupParagraphBorders(blocks, betweenBorders);
6071
7031
  const folded = foldDropCaps(model.blocks, blocks);
6072
7032
  if (folded.size) reportOnce(DROP_CAP_NOTE);
7033
+ if (Object.values(model.footnoteSeparators ?? {}).some(
7034
+ (note2) => note2.blocks.some((block) => block.type === "table")
7035
+ ))
7036
+ reportOnce(
7037
+ "Tables in custom footnote separators are preserved but are not rendered in Print Layout."
7038
+ );
7039
+ const footnoteSeparators = model.footnoteSeparators ? Object.fromEntries(
7040
+ Object.entries(model.footnoteSeparators).map(([kind, note2]) => [
7041
+ kind,
7042
+ note2.blocks.filter((block) => block.type === "paragraph").map((paragraph) => adaptParagraph(paragraph))
7043
+ ])
7044
+ ) : void 0;
6073
7045
  const sections = model.sections;
7046
+ const modernFootnoteSeparator = model.compatibilityMode === 15;
7047
+ const legacyFootnoteColumns = model.compatibilityMode === 12 || model.compatibilityMode === 14;
7048
+ if (!modernFootnoteSeparator && !legacyFootnoteColumns && model.sections?.some((section) => section.footnoteColumns !== void 0))
7049
+ reportOnce(
7050
+ "Footnote column settings are preserved, but this compatibility mode has not been verified against native Word; Print Layout uses the existing page-wide fallback."
7051
+ );
6074
7052
  if (!sections || !sections.length) {
6075
7053
  return {
6076
7054
  sections: [
@@ -6083,6 +7061,9 @@ function adaptDocumentModel(model, note = () => {
6083
7061
  marginBottomPx: model.page.marginBottom,
6084
7062
  marginLeftPx: model.page.marginLeft
6085
7063
  },
7064
+ ...footnoteSeparators ? { footnoteSeparators } : {},
7065
+ ...modernFootnoteSeparator ? { modernFootnoteSeparator: true } : {},
7066
+ ...legacyFootnoteColumns ? { legacyFootnoteColumns: true } : {},
6086
7067
  blocks: blocks.filter((block) => !folded.has(block))
6087
7068
  }
6088
7069
  ]
@@ -6096,8 +7077,34 @@ function adaptDocumentModel(model, note = () => {
6096
7077
  const end = Math.max(start, endIndex);
6097
7078
  const slice = blocks.slice(start, end);
6098
7079
  start = end;
7080
+ if (legacyFootnoteColumns && section.footnoteColumns)
7081
+ reportOnce(
7082
+ "Independent footnote column counts are preserved but ignored in measured compatibility modes 12 and 14; Print Layout attaches notes to body columns."
7083
+ );
7084
+ const footnoteWidth = twipsToPx3(section.pageWidthTwips) - twipsToPx3(section.marginLeftTwips) - twipsToPx3(section.marginRightTwips);
7085
+ if (modernFootnoteSeparator && section.footnoteColumns && (section.footnoteColumns > 45 || footnoteWidth <= 48 * (section.footnoteColumns - 1)))
7086
+ reportOnce(
7087
+ "Footnote columns exceeding the available page width or 45 rendered columns use one column in Print Layout; the saved count is preserved."
7088
+ );
6099
7089
  const sectionStart = index === 0 ? void 0 : sectionBreak(section.type, reportOnce);
6100
7090
  return {
7091
+ ...footnoteSeparators ? { footnoteSeparators } : {},
7092
+ ...modernFootnoteSeparator ? { modernFootnoteSeparator: true } : {},
7093
+ ...legacyFootnoteColumns ? { legacyFootnoteColumns: true } : {},
7094
+ ...section.footnoteColumns !== void 0 && modernFootnoteSeparator ? {
7095
+ footnoteColumns: {
7096
+ count: section.footnoteColumns && (section.footnoteColumns > 45 || footnoteWidth <= 48 * (section.footnoteColumns - 1)) ? 1 : Math.max(1, section.footnoteColumns || section.columns.count),
7097
+ gapPx: section.footnoteColumns ? 48 : twipsToPx3(section.columns.spacingTwips ?? twips(720)),
7098
+ ...!section.footnoteColumns && !section.columns.equalWidth && section.columns.widths?.length ? {
7099
+ widths: section.columns.widths.map((column) => ({
7100
+ widthPx: twipsToPx3(column.widthTwips),
7101
+ gapPx: twipsToPx3(
7102
+ column.spacingTwips ?? section.columns.spacingTwips ?? twips(720)
7103
+ )
7104
+ }))
7105
+ } : {}
7106
+ }
7107
+ } : {},
6101
7108
  page: {
6102
7109
  widthPx: twipsToPx3(section.pageWidthTwips),
6103
7110
  heightPx: twipsToPx3(section.pageHeightTwips),