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
@@ -37,8 +37,8 @@ function named(element, local, ns, lenient = true) {
37
37
  }
38
38
  var children = (parent, local, ns, lenient = true) => elements(parent).filter((element) => named(element, local, ns, lenient));
39
39
  var first = (parent, local, ns, lenient = true) => parent ? children(parent, local, ns, lenient)[0] : void 0;
40
- function attr(element, local, ns, prefix) {
41
- return element?.getAttributeNS(ns, local) ?? element?.getAttribute(`${prefix}:${local}`) ?? void 0;
40
+ function attr(element, local, ns, prefix2) {
41
+ return element?.getAttributeNS(ns, local) ?? element?.getAttribute(`${prefix2}:${local}`) ?? void 0;
42
42
  }
43
43
 
44
44
  // docx/xml.ts
@@ -2649,11 +2649,11 @@ function cssFontFamily(name) {
2649
2649
  // chart/render/chart-svg-appearance.ts
2650
2650
  var nextPaintId = 0;
2651
2651
  function chartGradientPaint(model, width = 1024, height = 768) {
2652
- const prefix = `xlsx-chart-${++nextPaintId}`;
2652
+ const prefix2 = `xlsx-chart-${++nextPaintId}`;
2653
2653
  const appearance = { ...model.appearance };
2654
2654
  const defs = [];
2655
2655
  const shadowPaint = (shadow, suffix) => {
2656
- const id = `${prefix}-${suffix}`;
2656
+ const id = `${prefix2}-${suffix}`;
2657
2657
  const pad = Math.max(4, shadow.blur * 3 + Math.abs(shadow.dx) + Math.abs(shadow.dy));
2658
2658
  defs.push(
2659
2659
  `<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>`
@@ -2668,7 +2668,7 @@ function chartGradientPaint(model, width = 1024, height = 768) {
2668
2668
  };
2669
2669
  }
2670
2670
  const paint = (gradient, suffix, bounds) => {
2671
- const def = buildChartGradientDef(`${prefix}-${suffix}`, gradient, bounds);
2671
+ const def = buildChartGradientDef(`${prefix2}-${suffix}`, gradient, bounds);
2672
2672
  defs.push(chartGradientMarkup(def));
2673
2673
  return `url(#${def.id})`;
2674
2674
  };
@@ -3708,6 +3708,14 @@ function mapDocumentParagraphs(model, map) {
3708
3708
  ...section.footers ? { footers: slots(section.footers) } : {}
3709
3709
  }))
3710
3710
  } : {},
3711
+ ...model.footnoteSeparators ? {
3712
+ footnoteSeparators: Object.fromEntries(
3713
+ Object.entries(model.footnoteSeparators).map(([kind, note]) => [
3714
+ kind,
3715
+ { ...note, blocks: mapBlockParagraphs(note.blocks, map) }
3716
+ ])
3717
+ )
3718
+ } : {},
3711
3719
  ...model.footnotes ? {
3712
3720
  footnotes: model.footnotes.map((note) => ({
3713
3721
  ...note,
@@ -3728,6 +3736,32 @@ function fieldName(instr) {
3728
3736
  return instr.trim().split(/\s+/)[0]?.toUpperCase() ?? "";
3729
3737
  }
3730
3738
 
3739
+ // docx/cell-blocks.ts
3740
+ function cellSourceBlocks(cell) {
3741
+ if (!cell.sourceBlocks) return cell.paragraphs;
3742
+ const originalIds = new Set(
3743
+ cell.sourceBlocks.filter((block) => block.type === "paragraph").map((block) => block.id)
3744
+ );
3745
+ const positions = new Map(cell.paragraphs.map((paragraph, index) => [paragraph.id, index]));
3746
+ const blocks = [];
3747
+ let cursor = 0;
3748
+ for (const source of cell.sourceBlocks) {
3749
+ if (source.type === "table") {
3750
+ blocks.push(source);
3751
+ continue;
3752
+ }
3753
+ const position = positions.get(source.id);
3754
+ if (position !== void 0 && position >= cursor) {
3755
+ blocks.push(...cell.paragraphs.slice(cursor, position + 1));
3756
+ cursor = position + 1;
3757
+ }
3758
+ while (cursor < cell.paragraphs.length && !originalIds.has(cell.paragraphs[cursor].id))
3759
+ blocks.push(cell.paragraphs[cursor++]);
3760
+ }
3761
+ blocks.push(...cell.paragraphs.slice(cursor));
3762
+ return blocks;
3763
+ }
3764
+
3731
3765
  // docx/notes.ts
3732
3766
  var romanNumerals = [
3733
3767
  [1e3, "M"],
@@ -3792,7 +3826,7 @@ function walkBlocks(blocks, kind, order) {
3792
3826
  if (block.type === "paragraph") walkParagraph(block, kind, order);
3793
3827
  else
3794
3828
  for (const row of block.rows)
3795
- for (const cell of row) walkBlocks(cell.paragraphs, kind, order);
3829
+ for (const cell of row) walkBlocks(cellSourceBlocks(cell), kind, order);
3796
3830
  }
3797
3831
  }
3798
3832
  function numberNotesInOrder(blocks, kind) {
@@ -4503,6 +4537,9 @@ function resolveIndents(paragraph) {
4503
4537
  firstLineExtraPx
4504
4538
  };
4505
4539
  }
4540
+ function sourceLengthOf(run) {
4541
+ return run.sourceLength ?? (run.object ? 1 : run.text.length || (run.breakAfter ? 1 : 0));
4542
+ }
4506
4543
  function tokenizeParagraph(paragraph) {
4507
4544
  const tokens = [];
4508
4545
  const runOffsets = [];
@@ -4510,7 +4547,7 @@ function tokenizeParagraph(paragraph) {
4510
4547
  paragraph.runs.forEach((run, runIndex) => {
4511
4548
  runOffsets.push(cursor);
4512
4549
  if (run.hidden) {
4513
- if (!run.synthetic) cursor += run.sourceLength ?? run.text.length;
4550
+ if (!run.synthetic) cursor += sourceLengthOf(run);
4514
4551
  return;
4515
4552
  }
4516
4553
  if (run.object) tokens.push({ kind: "object", runIndex, sourceStart: 0 });
@@ -4526,7 +4563,7 @@ function tokenizeParagraph(paragraph) {
4526
4563
  if (run.breakAfter) appendBreakMarker(tokens, runIndex, run.text.length, run.breakAfter);
4527
4564
  if (run.synthetic) {
4528
4565
  for (const token of tokens) if (token.runIndex === runIndex) token.sourceStart = 0;
4529
- } else cursor += run.sourceLength ?? run.text.length;
4566
+ } else cursor += sourceLengthOf(run);
4530
4567
  });
4531
4568
  return { tokens, runOffsets };
4532
4569
  }
@@ -4548,7 +4585,9 @@ function lineMetrics(placed, fonts, paragraph, measurer) {
4548
4585
  let above = 0;
4549
4586
  let below = 0;
4550
4587
  for (const token of tokens) {
4551
- const extent = tokenExtent(token, paragraph, fonts, measurer);
4588
+ const run = paragraph.runs[token.runIndex];
4589
+ const metrics = run?.object?.noteSeparator ? fontMetrics(measurer, fonts[token.runIndex] ?? fontOf(run)) : void 0;
4590
+ const extent = metrics ? { above: metrics.ascent, below: metrics.descent } : tokenExtent(token, paragraph, fonts, measurer);
4552
4591
  above = Math.max(above, extent.above);
4553
4592
  below = Math.max(below, extent.below);
4554
4593
  }
@@ -4623,7 +4662,18 @@ function layoutParagraph(paragraph, contentWidthPx, measurer, note, lineBox, sta
4623
4662
  const bodyWidth = Math.max(1, contentWidthPx - leftPx - rightPx);
4624
4663
  const fonts = paragraph.runs.map(fontOf);
4625
4664
  const { tokens, runOffsets } = tokenizeParagraph(paragraph);
4626
- const globalOffset = (token) => expectDefined(runOffsets[token.runIndex], "run offset index") + token.sourceStart;
4665
+ const globalOffset = (token, advance = 0) => {
4666
+ const run = expectDefined(paragraph.runs[token.runIndex], "source run");
4667
+ const length = sourceLengthOf(run);
4668
+ const offset = run.synthetic ? 0 : Math.max(
4669
+ 0,
4670
+ Math.min(
4671
+ length,
4672
+ Math.round((token.sourceStart + advance) * length / Math.max(1, run.text.length))
4673
+ )
4674
+ );
4675
+ return expectDefined(runOffsets[token.runIndex], "run offset index") + offset;
4676
+ };
4627
4677
  const effectiveAlign = paragraph.align ?? (paragraph.direction === "rtl" ? "right" : "left");
4628
4678
  const lines = [];
4629
4679
  const pageBreakAfterLine = /* @__PURE__ */ new Set();
@@ -4674,9 +4724,9 @@ function layoutParagraph(paragraph, contentWidthPx, measurer, note, lineBox, sta
4674
4724
  const token = expectDefined(tokens[next], "token index");
4675
4725
  if (ends.has(token.kind)) break;
4676
4726
  if (beforeDecimalPx === void 0 && token.kind === "word" && /[.,]/.test(token.text)) {
4677
- const prefix = token.text.slice(0, token.text.search(/[.,]/));
4727
+ const prefix2 = token.text.slice(0, token.text.search(/[.,]/));
4678
4728
  beforeDecimalPx = followingPx + runTextWidth(
4679
- prefix,
4729
+ prefix2,
4680
4730
  expectDefined(paragraph.runs[token.runIndex], "decimal run"),
4681
4731
  measurer
4682
4732
  );
@@ -4695,14 +4745,17 @@ function layoutParagraph(paragraph, contentWidthPx, measurer, note, lineBox, sta
4695
4745
  const [firstPlaced] = placed;
4696
4746
  const start = firstPlaced ? globalOffset(firstPlaced.token) : 0;
4697
4747
  const last = placed.at(-1)?.token;
4698
- const end = last ? globalOffset(last) + (last.kind === "word" || last.kind === "space" ? last.text.length : 1) : start;
4748
+ const end = last ? globalOffset(last, last.kind === "word" || last.kind === "space" ? last.text.length : 1) : start;
4699
4749
  const gapBeforePx = boxFor(lineIndex).gapBeforePx;
4700
4750
  const { heightPx, baselinePx } = lineMetrics(placed, fonts, paragraph, measurer);
4701
4751
  rendered.forEach(({ token }, index) => {
4702
4752
  const extent2 = tokenExtent(token, paragraph, fonts, measurer);
4703
4753
  const fragment = expectDefined(fragments[index], "fragment for rendered token");
4704
4754
  fragment.sourceStart = globalOffset(token);
4705
- fragment.sourceEnd = fragment.sourceStart + (paragraph.runs[token.runIndex]?.synthetic ? 0 : token.kind === "word" || token.kind === "space" ? token.text.length : 1);
4755
+ fragment.sourceEnd = globalOffset(
4756
+ token,
4757
+ token.kind === "word" || token.kind === "space" ? token.text.length : 1
4758
+ );
4706
4759
  fragment.topPx = baselinePx - extent2.above;
4707
4760
  if (token.kind !== "object") fragment.boxHeightPx = extent2.ascent + extent2.descent;
4708
4761
  });
@@ -4836,6 +4889,57 @@ function widowControlEnabled(value) {
4836
4889
  return value !== false;
4837
4890
  }
4838
4891
 
4892
+ // docx/layout/table-cell-layout.ts
4893
+ function cellParagraphs(cell) {
4894
+ const walk = (blocks) => blocks.flatMap(
4895
+ (block) => block.kind === "paragraph" ? [block] : block.rows.flatMap((row) => row.cells.flatMap(cellParagraphs))
4896
+ );
4897
+ return cell.blocks ? walk(cell.blocks) : cell.paragraphs;
4898
+ }
4899
+ function stackCellBlocks(cell, width, measurer, note, top, starts) {
4900
+ if (!cell.blocks)
4901
+ return stackParagraphs(
4902
+ starts ? cell.paragraphs.filter((p) => starts.has(p.id)) : cell.paragraphs,
4903
+ width,
4904
+ measurer,
4905
+ note,
4906
+ top,
4907
+ starts
4908
+ );
4909
+ let y = top;
4910
+ const boxes = [];
4911
+ for (const block of cell.blocks) {
4912
+ if (block.kind === "paragraph") {
4913
+ if (starts && !starts.has(block.id)) continue;
4914
+ const result = stackParagraphs([block], width, measurer, note, y, starts);
4915
+ boxes.push(...result.boxes);
4916
+ y = result.heightPx;
4917
+ continue;
4918
+ }
4919
+ const tableWidth = block.widthPx ?? width;
4920
+ const x = block.alignment === "center" ? Math.max(0, (width - tableWidth) / 2) : block.alignment === "right" ? Math.max(0, width - tableWidth) : block.indentPx ?? 0;
4921
+ for (const row of block.rows) {
4922
+ if (starts && !row.cells.some((nested) => cellParagraphs(nested).some((p) => starts.has(p.id))))
4923
+ continue;
4924
+ const layout = layoutRow(row, tableWidth, measurer, note, void 0, starts);
4925
+ layout.cells.forEach((paragraphs, index) => {
4926
+ const geometry = layout.geometry[index];
4927
+ for (const paragraph of paragraphs)
4928
+ boxes.push({
4929
+ ...paragraph,
4930
+ yPx: y + paragraph.yPx,
4931
+ xPx: x + geometry.xPx + geometry.paddingLeftPx + (paragraph.xPx ?? 0),
4932
+ widthPx: paragraph.widthPx ?? Math.max(1, geometry.widthPx - geometry.paddingLeftPx - geometry.paddingRightPx),
4933
+ readOnly: true
4934
+ });
4935
+ });
4936
+ y += layout.heightPx;
4937
+ }
4938
+ }
4939
+ boxes.sort((a, b) => a.yPx - b.yPx);
4940
+ return { heightPx: y, boxes };
4941
+ }
4942
+
4839
4943
  // docx/layout/table-layout.ts
4840
4944
  function stackParagraphs(paragraphs, widthPx, measurer, note, topPx = 0, startTokens) {
4841
4945
  let y = topPx;
@@ -4851,29 +4955,30 @@ function stackParagraphs(paragraphs, widthPx, measurer, note, topPx = 0, startTo
4851
4955
  );
4852
4956
  y += layout.spacingBeforePx;
4853
4957
  const top = y;
4854
- const lines = layout.lines.map((line2) => ({ ...line2, yPx: line2.yPx + top }));
4958
+ const lines = layout.lines.map((line2) => ({ ...line2 }));
4855
4959
  boxes.push({
4856
4960
  kind: "paragraph",
4857
4961
  blockId: paragraph.id,
4858
4962
  yPx: top,
4859
4963
  heightPx: layout.contentHeightPx,
4860
- lines
4964
+ lines,
4965
+ ...layout.frame ? { frame: layout.frame } : {}
4861
4966
  });
4862
4967
  y += layout.contentHeightPx + layout.spacingAfterPx;
4863
4968
  }
4864
4969
  return { heightPx: y, boxes };
4865
4970
  }
4866
- function layoutRow(row, tableWidthPx, measurer, note, remaining) {
4971
+ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
4867
4972
  const count = row.cells.length || 1;
4868
4973
  let x = 0;
4869
4974
  const geometry = [];
4870
4975
  const laidOut = row.cells.map((cell) => {
4871
4976
  const continuation = remaining?.[geometry.length];
4872
- const starts = continuation ? new Map(continuation.map((box) => [box.blockId, box.lines[0]?.firstToken ?? 0])) : void 0;
4977
+ const starts = continuation ? new Map(continuation.map((box) => [box.blockId, box.lines[0]?.firstToken ?? 0])) : startTokens;
4873
4978
  const widthPx = cell.widthPx ?? tableWidthPx / count;
4874
4979
  const padding = cell.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
4875
- const content2 = stackParagraphs(
4876
- starts ? cell.paragraphs.filter((paragraph) => starts.has(paragraph.id)) : cell.paragraphs,
4980
+ const content2 = stackCellBlocks(
4981
+ cell,
4877
4982
  Math.max(1, widthPx - padding.left - padding.right),
4878
4983
  measurer,
4879
4984
  note,
@@ -4896,26 +5001,24 @@ function layoutRow(row, tableWidthPx, measurer, note, remaining) {
4896
5001
  });
4897
5002
  const content = Math.max(0, ...laidOut.map((cell) => cell.heightPx));
4898
5003
  const heightPx = row.heightPx === void 0 ? content : row.heightRule === "exact" ? row.heightPx : Math.max(content, row.heightPx);
4899
- return { heightPx, cells: laidOut.map((cell) => cell.boxes), geometry };
5004
+ return {
5005
+ heightPx,
5006
+ cells: laidOut.map((cell) => cell.boxes),
5007
+ geometry,
5008
+ cellPadding: row.cells.map((cell) => ({
5009
+ top: cell.padding?.top ?? 0,
5010
+ bottom: cell.padding?.bottom ?? 0
5011
+ }))
5012
+ };
4900
5013
  }
4901
5014
  function splitCellAtHeight(boxes, cutHeightPx) {
4902
5015
  const before = [];
4903
5016
  const after = [];
4904
- let afterOrigin = null;
4905
5017
  let beforeHeightPx = 0;
4906
5018
  for (const box of boxes) {
4907
- if (afterOrigin !== null) {
4908
- after.push({ ...box, yPx: box.yPx - afterOrigin });
4909
- continue;
4910
- }
4911
5019
  if (box.yPx + box.heightPx <= cutHeightPx) {
4912
5020
  before.push(box);
4913
- beforeHeightPx = box.yPx + box.heightPx;
4914
- continue;
4915
- }
4916
- if (box.yPx >= cutHeightPx) {
4917
- afterOrigin = box.yPx;
4918
- after.push({ ...box, yPx: 0 });
5021
+ beforeHeightPx = Math.max(beforeHeightPx, box.yPx + box.heightPx);
4919
5022
  continue;
4920
5023
  }
4921
5024
  const beforeLines = box.lines.filter(
@@ -4925,50 +5028,407 @@ function splitCellAtHeight(boxes, cutHeightPx) {
4925
5028
  if (beforeLines.length) {
4926
5029
  const bottom = beforeLines.at(-1).yPx + beforeLines.at(-1).heightPx;
4927
5030
  before.push({ ...box, heightPx: bottom, lines: beforeLines });
4928
- beforeHeightPx = box.yPx + bottom;
5031
+ beforeHeightPx = Math.max(beforeHeightPx, box.yPx + bottom);
4929
5032
  }
4930
5033
  if (afterLines.length) {
4931
5034
  const shift = expectDefined(afterLines[0], "first line after split").yPx;
4932
- afterOrigin = box.yPx + shift;
4933
5035
  after.push({
4934
5036
  ...box,
4935
- yPx: 0,
5037
+ yPx: box.yPx + shift,
4936
5038
  heightPx: afterLines.at(-1).yPx + afterLines.at(-1).heightPx - shift,
4937
5039
  lines: afterLines.map((line2) => ({ ...line2, yPx: line2.yPx - shift }))
4938
5040
  });
4939
- } else {
4940
- afterOrigin = box.yPx + box.heightPx;
4941
5041
  }
4942
5042
  }
5043
+ const afterOrigin = Math.min(...after.map((box) => box.yPx));
5044
+ for (const box of after) box.yPx -= afterOrigin;
4943
5045
  return { before, after, beforeHeightPx };
4944
5046
  }
4945
- function splitRowAtHeight(row, cutHeightPx) {
4946
- const splits = row.cells.map((cell) => splitCellAtHeight(cell, cutHeightPx));
5047
+ function splitRowAtHeight(row, cutHeightPx, cellCuts) {
5048
+ const splits = row.cells.map((cell, index) => {
5049
+ const padding = row.cellPadding?.[index] ?? { top: 0, bottom: 0 };
5050
+ const split = splitCellAtHeight(cell, (cellCuts?.[index] ?? cutHeightPx) - padding.bottom);
5051
+ if (split.before.length) split.beforeHeightPx += padding.bottom;
5052
+ if (split.after.length)
5053
+ split.after = split.after.map((box) => ({ ...box, yPx: box.yPx + padding.top }));
5054
+ return split;
5055
+ });
4947
5056
  const anyAfter = splits.some((split) => split.after.length > 0);
4948
5057
  const before = {
4949
5058
  heightPx: Math.max(0, ...splits.map((split) => split.beforeHeightPx)),
4950
5059
  cells: splits.map((split) => split.before),
4951
- geometry: row.geometry
5060
+ geometry: row.geometry.map((geometry, index) => ({
5061
+ ...geometry,
5062
+ contentHeightPx: splits[index].beforeHeightPx
5063
+ })),
5064
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
4952
5065
  };
4953
5066
  if (!anyAfter) return { before, after: null };
4954
5067
  const after = {
4955
5068
  heightPx: Math.max(
4956
5069
  0,
4957
5070
  ...splits.map(
4958
- (split) => split.after.reduce((max, box) => Math.max(max, box.yPx + box.heightPx), 0)
5071
+ (split, index) => split.after.length ? split.after.at(-1).yPx + split.after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
4959
5072
  )
4960
5073
  ),
4961
5074
  cells: splits.map((split) => split.after),
4962
- geometry: row.geometry
5075
+ geometry: row.geometry.map((geometry, index) => ({
5076
+ ...geometry,
5077
+ contentHeightPx: splits[index].after.length ? splits[index].after.at(-1).yPx + splits[index].after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
5078
+ })),
5079
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
4963
5080
  };
4964
5081
  return { before, after };
4965
5082
  }
4966
5083
 
4967
- // docx/layout/page-cursor.ts
5084
+ // docx/layout/footnote-split.ts
5085
+ function splitNote(note, budget) {
5086
+ if (note.heightPx <= budget + 1e-6) return { before: note };
5087
+ const before = [];
5088
+ const after = [];
5089
+ let origin;
5090
+ const sourceTokenStarts = { ...note.sourceTokenStarts };
5091
+ for (const paragraph of note.paragraphs) {
5092
+ if (origin !== void 0) {
5093
+ after.push({ ...paragraph, yPx: paragraph.yPx - origin });
5094
+ continue;
5095
+ }
5096
+ if (paragraph.yPx + paragraph.heightPx <= budget + 1e-6) {
5097
+ before.push(paragraph);
5098
+ continue;
5099
+ }
5100
+ const count = paragraph.lines.filter(
5101
+ (line2) => paragraph.yPx + line2.yPx + line2.heightPx <= budget + 1e-6
5102
+ ).length;
5103
+ const head = paragraph.lines.slice(0, count);
5104
+ const tail = paragraph.lines.slice(count);
5105
+ if (head.length) {
5106
+ const bottom = head.at(-1).yPx + head.at(-1).heightPx;
5107
+ before.push({ ...paragraph, heightPx: bottom, lines: head });
5108
+ }
5109
+ const first3 = tail[0];
5110
+ if (head.length)
5111
+ sourceTokenStarts[paragraph.blockId] = head.at(-1).nextToken ?? sourceTokenStarts[paragraph.blockId] ?? 0;
5112
+ origin = paragraph.yPx + (first3?.yPx ?? 0);
5113
+ after.push({
5114
+ ...paragraph,
5115
+ yPx: 0,
5116
+ heightPx: Math.max(0, paragraph.heightPx - (first3?.yPx ?? 0)),
5117
+ lines: tail.map((line2) => ({ ...line2, yPx: line2.yPx - (first3?.yPx ?? 0) }))
5118
+ });
5119
+ }
5120
+ if (!before.length) return { after: note };
5121
+ const last = before.at(-1);
5122
+ return {
5123
+ before: { ...note, heightPx: last.yPx + last.heightPx, paragraphs: before },
5124
+ ...after.length ? {
5125
+ after: {
5126
+ ...note,
5127
+ yPx: 0,
5128
+ heightPx: Math.max(0, note.heightPx - (origin ?? 0)),
5129
+ paragraphs: after,
5130
+ continued: true,
5131
+ sourceTokenStarts
5132
+ }
5133
+ } : {}
5134
+ };
5135
+ }
5136
+
5137
+ // docx/layout/footnote-columns.ts
5138
+ function footnoteColumns(page, columns) {
5139
+ const width = page.widthPx - page.marginLeftPx - page.marginRightPx;
5140
+ const equal = (width - columns.gapPx * (columns.count - 1)) / columns.count;
5141
+ let x = 0;
5142
+ return Array.from({ length: columns.count }, (_, index) => {
5143
+ const own = columns.widths?.[index];
5144
+ const column = { xPx: x, widthPx: Math.max(1, own?.widthPx ?? equal) };
5145
+ x += column.widthPx + (own?.gapPx ?? columns.gapPx);
5146
+ return column;
5147
+ });
5148
+ }
5149
+ function packFootnotes(notes, columns, height, reflow) {
5150
+ const remaining = [...notes], placed = [];
5151
+ let used = 0;
5152
+ for (const [columnIndex, column] of columns.entries()) {
5153
+ let y = 0;
5154
+ while (remaining.length) {
5155
+ const current = reflow(remaining[0], column.widthPx);
5156
+ const split = splitNote(current, Math.max(0, height - y));
5157
+ if (!split.before) break;
5158
+ placed.push({
5159
+ ...split.before,
5160
+ yPx: y,
5161
+ xPx: column.xPx,
5162
+ widthPx: column.widthPx,
5163
+ columnIndex
5164
+ });
5165
+ y += split.before.heightPx;
5166
+ used = Math.max(used, y);
5167
+ if (split.after) {
5168
+ remaining[0] = { ...split.after, continued: !!current.continued };
5169
+ break;
5170
+ }
5171
+ remaining.shift();
5172
+ }
5173
+ }
5174
+ return { placed, remaining, heightPx: used };
5175
+ }
5176
+ function balancedFootnotes(notes, columns, limit, reflow) {
5177
+ if (!notes.length) return { placed: [], remaining: [], heightPx: 0 };
5178
+ const full = packFootnotes(notes, columns, limit, reflow);
5179
+ if (full.remaining.length) return full;
5180
+ let lower = 0, upper = limit;
5181
+ for (let step = 0; step < 24 && upper - lower > 5e-3; step++) {
5182
+ const middle = (lower + upper) / 2;
5183
+ if (packFootnotes(notes, columns, middle, reflow).remaining.length) lower = middle;
5184
+ else upper = middle;
5185
+ }
5186
+ return packFootnotes(notes, columns, Math.ceil(upper * 100) / 100, reflow);
5187
+ }
5188
+ function minimumFootnoteHeight(notes, columns, reflow) {
5189
+ const width = columns[0].widthPx;
5190
+ const maximum = notes.reduce((sum, note) => sum + reflow(note, width).heightPx, 0);
5191
+ return balancedFootnotes(notes, columns, maximum, reflow).heightPx;
5192
+ }
5193
+
5194
+ // docx/layout/footnote-flow.ts
5195
+ function firstLineBottom(note) {
5196
+ const first3 = note?.paragraphs[0];
5197
+ return first3 ? first3.yPx + (first3.lines[0]?.yPx ?? 0) + (first3.lines[0]?.heightPx ?? first3.heightPx) : note?.heightPx ?? 0;
5198
+ }
4968
5199
  var FOOTNOTE_SEPARATOR_PX = 12;
5200
+ var FootnoteFlow = class {
5201
+ constructor(report, separator, columns, reflow = (note) => note) {
5202
+ this.report = report;
5203
+ this.separator = separator;
5204
+ this.columns = columns;
5205
+ this.reflow = reflow;
5206
+ }
5207
+ report;
5208
+ separator;
5209
+ columns;
5210
+ reflow;
5211
+ pending;
5212
+ remaining = [];
5213
+ sources = /* @__PURE__ */ new WeakMap();
5214
+ separatorHeight(page, notes = this.pending?.notes ?? []) {
5215
+ return (page.footnoteSeparator ?? this.separator?.(page, !!notes[0]?.continued))?.heightPx ?? FOOTNOTE_SEPARATOR_PX;
5216
+ }
5217
+ get hasContinuation() {
5218
+ return this.remaining.length > 0;
5219
+ }
5220
+ hold(blockId, notes, bodyMinimum, freshBodyMinimum = bodyMinimum) {
5221
+ this.pending = notes.length ? { blockId, notes, bodyMinimum, freshBodyMinimum } : void 0;
5222
+ }
5223
+ reserved(page, _columnIndex = 0) {
5224
+ return page.footnotes?.length ? this.separatorHeight(page) + (page.footnoteHeightPx ?? page.footnotes.reduce((sum, note) => sum + note.heightPx, 0)) : 0;
5225
+ }
5226
+ pendingHeight(page, capacity, bodyUsed, _columnIndex = 0) {
5227
+ if (!this.pending) return 0;
5228
+ if (this.columns) {
5229
+ const existing = this.sources.get(page) ?? [];
5230
+ const height = minimumFootnoteHeight(
5231
+ [...existing, ...this.pending.notes],
5232
+ footnoteColumns(page, this.columns),
5233
+ this.reflow
5234
+ ) + this.separatorHeight(page);
5235
+ if (height + this.pending.freshBodyMinimum <= capacity)
5236
+ return Math.max(0, height - this.reserved(page));
5237
+ return Math.max(
5238
+ firstLineBottom(this.pending.notes[0]) + (page.footnotes?.length ? 0 : this.separatorHeight(page)),
5239
+ capacity - bodyUsed - this.reserved(page) - this.pending.bodyMinimum
5240
+ );
5241
+ }
5242
+ const total = this.pending.notes.reduce((sum, note) => sum + note.heightPx, 0);
5243
+ const separator = page.footnotes?.length ? 0 : this.separatorHeight(page);
5244
+ if (total + this.separatorHeight(page) + this.pending.freshBodyMinimum <= capacity)
5245
+ return total + separator;
5246
+ return Math.max(
5247
+ firstLineBottom(this.pending.notes[0]) + separator,
5248
+ capacity - bodyUsed - this.reserved(page) - this.pending.bodyMinimum
5249
+ );
5250
+ }
5251
+ /** A later column cannot grow the page-wide note area into body content already placed. */
5252
+ blockedByEarlierColumn(page, capacity, bodyBottom) {
5253
+ if (!this.pending) return false;
5254
+ if (this.columns)
5255
+ return bodyBottom + this.reserved(page) + this.pendingHeight(page, capacity, bodyBottom) > capacity;
5256
+ const total = this.pending.notes.reduce((sum, note) => sum + note.heightPx, 0);
5257
+ const minimum = firstLineBottom(this.pending.notes[0]);
5258
+ const needed = total + this.separatorHeight(page) + this.pending.freshBodyMinimum <= capacity ? total : minimum;
5259
+ return bodyBottom + this.reserved(page) + needed + (page.footnotes?.length ? 0 : this.separatorHeight(page)) > capacity;
5260
+ }
5261
+ commit(blockId, page, available, _columnIndex = 0) {
5262
+ if (this.pending?.blockId !== blockId) return false;
5263
+ const notes = this.pending.notes;
5264
+ this.pending = void 0;
5265
+ this.append(page, notes, available);
5266
+ return true;
5267
+ }
5268
+ resume(page, capacity, _columnIndex, width) {
5269
+ const notes = width === void 0 ? this.remaining : this.remaining.map((note) => this.reflow(note, width));
5270
+ this.remaining = [];
5271
+ this.append(page, notes, capacity);
5272
+ }
5273
+ append(page, notes, available) {
5274
+ if (!page.footnotes?.length && notes.length) {
5275
+ const separator = this.separator?.(page, !!notes[0]?.continued);
5276
+ if (separator) page.footnoteSeparator = separator;
5277
+ }
5278
+ if (this.columns) {
5279
+ const sources = [...this.sources.get(page) ?? [], ...notes];
5280
+ this.sources.set(page, sources);
5281
+ const geometry = footnoteColumns(page, this.columns);
5282
+ const previousHeight = page.footnoteHeightPx ?? 0;
5283
+ const capacity = Math.max(
5284
+ 0,
5285
+ available + previousHeight - (page.footnotes?.length ? 0 : this.separatorHeight(page))
5286
+ );
5287
+ let packed = balancedFootnotes(sources, geometry, capacity, this.reflow);
5288
+ if (!packed.placed.length && sources.length) {
5289
+ this.report(
5290
+ "A footnote line was taller than its available page area and was placed without splitting."
5291
+ );
5292
+ packed = balancedFootnotes(
5293
+ sources,
5294
+ geometry,
5295
+ firstLineBottom(this.reflow(sources[0], geometry[0].widthPx)),
5296
+ this.reflow
5297
+ );
5298
+ }
5299
+ page.footnotes = packed.placed;
5300
+ page.footnoteHeightPx = packed.heightPx;
5301
+ this.remaining = packed.remaining.map((note) => ({
5302
+ ...note,
5303
+ continued: !!note.continued || packed.placed.some((fragment) => fragment.id === note.id)
5304
+ }));
5305
+ return;
5306
+ }
5307
+ let top = page.footnotes?.reduce((sum, note) => sum + note.heightPx, 0) ?? 0;
5308
+ let budget = Math.max(0, available - (page.footnotes?.length ? 0 : this.separatorHeight(page)));
5309
+ for (let index = 0; index < notes.length; index++) {
5310
+ const note = notes[index];
5311
+ let split = splitNote(note, budget);
5312
+ if (!split.before && !page.footnotes?.length) {
5313
+ split = splitNote(note, firstLineBottom(note));
5314
+ this.report(
5315
+ "A footnote line was taller than its available page area and was placed without splitting."
5316
+ );
5317
+ }
5318
+ if (split.before) {
5319
+ (page.footnotes ??= []).push({ ...split.before, yPx: top });
5320
+ top += split.before.heightPx;
5321
+ budget -= split.before.heightPx;
5322
+ }
5323
+ if (split.after) {
5324
+ this.remaining.push(split.after, ...notes.slice(index + 1));
5325
+ break;
5326
+ }
5327
+ }
5328
+ }
5329
+ };
5330
+
5331
+ // docx/layout/legacy-footnote-flow.ts
5332
+ var LegacyFootnoteFlow = class {
5333
+ flow;
5334
+ views = /* @__PURE__ */ new WeakMap();
5335
+ constructor(report, separator, reflow = (note) => note) {
5336
+ this.flow = new FootnoteFlow(report, separator, void 0, reflow);
5337
+ }
5338
+ view(page, index) {
5339
+ let views = this.views.get(page);
5340
+ if (!views) {
5341
+ views = /* @__PURE__ */ new Map();
5342
+ this.views.set(page, views);
5343
+ }
5344
+ let view = views.get(index);
5345
+ if (!view) {
5346
+ const {
5347
+ footnotes: _notes,
5348
+ footnoteSeparator: _separator,
5349
+ footnoteHeightPx: _height,
5350
+ footnoteColumnAreas: _areas,
5351
+ ...geometry
5352
+ } = page;
5353
+ view = {
5354
+ ...geometry,
5355
+ widthPx: page.columns[index].widthPx,
5356
+ marginLeftPx: 0,
5357
+ marginRightPx: 0,
5358
+ columns: []
5359
+ };
5360
+ views.set(index, view);
5361
+ }
5362
+ return view;
5363
+ }
5364
+ sync(page) {
5365
+ const views = this.views.get(page);
5366
+ if (!views) return;
5367
+ const areas = [...views].filter(([, view]) => view.footnotes?.length).map(([columnIndex, view]) => {
5368
+ const column = page.columns[columnIndex];
5369
+ return {
5370
+ columnIndex,
5371
+ xPx: column.xPx,
5372
+ widthPx: column.widthPx,
5373
+ heightPx: view.footnotes.reduce((sum, note) => sum + note.heightPx, 0),
5374
+ continued: !!view.footnotes[0]?.continued,
5375
+ ...view.footnoteSeparator ? { separator: view.footnoteSeparator } : {},
5376
+ yPx: 0
5377
+ };
5378
+ });
5379
+ const height = Math.max(0, ...areas.map((area) => area.heightPx));
5380
+ for (const area of areas) area.yPx = height - area.heightPx;
5381
+ page.footnoteColumnAreas = areas;
5382
+ page.footnoteHeightPx = height;
5383
+ page.footnotes = areas.flatMap(
5384
+ (area) => this.view(page, area.columnIndex).footnotes.map((note) => ({
5385
+ ...note,
5386
+ xPx: area.xPx,
5387
+ widthPx: area.widthPx,
5388
+ columnIndex: area.columnIndex,
5389
+ yPx: area.yPx + note.yPx
5390
+ }))
5391
+ );
5392
+ }
5393
+ get hasContinuation() {
5394
+ return this.flow.hasContinuation;
5395
+ }
5396
+ hold(blockId, notes, bodyMinimum, freshBodyMinimum = bodyMinimum) {
5397
+ this.flow.hold(blockId, notes, bodyMinimum, freshBodyMinimum);
5398
+ }
5399
+ reserved(page, columnIndex = 0) {
5400
+ return this.flow.reserved(this.view(page, columnIndex));
5401
+ }
5402
+ pendingHeight(page, capacity, bodyUsed, columnIndex = 0) {
5403
+ return this.flow.pendingHeight(this.view(page, columnIndex), capacity, bodyUsed);
5404
+ }
5405
+ blockedByEarlierColumn(_page, _capacity, _bodyBottom) {
5406
+ return false;
5407
+ }
5408
+ commit(blockId, page, available, columnIndex = 0) {
5409
+ if (this.flow.commit(blockId, this.view(page, columnIndex), available)) this.sync(page);
5410
+ }
5411
+ resume(page, capacity, columnIndex) {
5412
+ if (!this.flow.hasContinuation) return;
5413
+ const indexes = columnIndex === void 0 ? page.columns.map((_, index) => index) : [columnIndex];
5414
+ for (const index of indexes) {
5415
+ if (!this.flow.hasContinuation) break;
5416
+ const view = this.view(page, index);
5417
+ if (view.footnotes?.length) continue;
5418
+ this.flow.resume(view, capacity, void 0, view.widthPx);
5419
+ }
5420
+ this.sync(page);
5421
+ }
5422
+ };
5423
+
5424
+ // docx/layout/page-cursor.ts
4969
5425
  var PageCursor = class {
4970
- constructor(pages, geometry, columns, sectionIndex = 0, continueAt, balance) {
5426
+ constructor(pages, geometry, columns, sectionIndex = 0, continueAt, balance, report = () => {
5427
+ }, separator, noteColumns, reflow, legacyFootnoteColumns = false) {
4971
5428
  this.balance = balance;
5429
+ this.noteColumns = noteColumns;
5430
+ this.legacyFootnoteColumns = legacyFootnoteColumns;
5431
+ this.footnotes = legacyFootnoteColumns ? new LegacyFootnoteFlow(report, separator, reflow) : new FootnoteFlow(report, separator, noteColumns, reflow);
4972
5432
  this.sectionIndex = sectionIndex;
4973
5433
  this.pages = pages;
4974
5434
  this.geometry = geometry;
@@ -4992,6 +5452,8 @@ var PageCursor = class {
4992
5452
  }
4993
5453
  }
4994
5454
  balance;
5455
+ noteColumns;
5456
+ legacyFootnoteColumns;
4995
5457
  pages;
4996
5458
  equalColumnWidthPx;
4997
5459
  activeColumns = [];
@@ -5002,10 +5464,7 @@ var PageCursor = class {
5002
5464
  yPx = 0;
5003
5465
  sectionIndex;
5004
5466
  pageInSection = 0;
5005
- /** Height taken by footnotes (and their separator) at the bottom of the current page. */
5006
- reservedPx = 0;
5007
- /** Footnotes of the paragraph about to be placed; they join the page its first box lands on. */
5008
- pending;
5467
+ footnotes;
5009
5468
  columnBoxes(offsetPx = 0) {
5010
5469
  const columnBoxes = [];
5011
5470
  let xPx = offsetPx;
@@ -5040,7 +5499,7 @@ var PageCursor = class {
5040
5499
  this.columnIndex = 0;
5041
5500
  this.columnTopPx = 0;
5042
5501
  this.yPx = 0;
5043
- this.reservedPx = 0;
5502
+ this.footnotes.resume(this.page, this.physicalColumnHeightPx);
5044
5503
  }
5045
5504
  get page() {
5046
5505
  return this.pages.at(-1);
@@ -5048,6 +5507,9 @@ var PageCursor = class {
5048
5507
  get column() {
5049
5508
  return expectDefined(this.activeColumns[this.columnIndex], "current page column");
5050
5509
  }
5510
+ get noteColumnIndex() {
5511
+ return this.legacyFootnoteColumns ? this.page.columns.indexOf(this.column) : this.columnIndex;
5512
+ }
5051
5513
  get columnWidthPx() {
5052
5514
  return this.column.widthPx;
5053
5515
  }
@@ -5062,6 +5524,9 @@ var PageCursor = class {
5062
5524
  endBalance() {
5063
5525
  this.balance = void 0;
5064
5526
  }
5527
+ get footnoteWidthPx() {
5528
+ return this.noteColumns ? footnoteColumns(this.page, this.noteColumns)[0].widthPx : this.columnWidthPx;
5529
+ }
5065
5530
  get y() {
5066
5531
  return this.yPx;
5067
5532
  }
@@ -5085,7 +5550,7 @@ var PageCursor = class {
5085
5550
  );
5086
5551
  }
5087
5552
  remainingHeightPx() {
5088
- return this.columnHeightPx - (this.yPx - this.columnTopPx) - this.reservedPx - this.pendingHeightPx();
5553
+ return this.columnHeightPx - (this.yPx - this.columnTopPx) - this.footnotes.reserved(this.page, this.noteColumnIndex) - this.pendingHeightPx();
5089
5554
  }
5090
5555
  /** Close this section's column band before another section shares its page. */
5091
5556
  finishBand() {
@@ -5104,16 +5569,41 @@ var PageCursor = class {
5104
5569
  return bottom;
5105
5570
  }
5106
5571
  pendingHeightPx() {
5107
- if (!this.pending) return 0;
5108
- return this.pending.heightPx + (this.page.footnotes?.length ? 0 : FOOTNOTE_SEPARATOR_PX);
5572
+ return this.footnotes.pendingHeight(
5573
+ this.page,
5574
+ this.columnHeightPx,
5575
+ this.usedColumnHeightPx,
5576
+ this.noteColumnIndex
5577
+ );
5109
5578
  }
5110
5579
  /**
5111
5580
  * Reserves room for a paragraph's footnotes on whichever page its first box is placed, so the
5112
5581
  * notes print on the same page as their reference, as Word does.
5113
5582
  */
5114
- holdFootnotes(blockId, notes) {
5115
- const heightPx = notes.reduce((sum, note) => sum + note.heightPx, 0);
5116
- this.pending = notes.length ? { blockId, notes, heightPx } : void 0;
5583
+ holdFootnotes(blockId, notes, bodyMinimum = 0, freshBodyMinimum = bodyMinimum) {
5584
+ this.footnotes.hold(blockId, notes, bodyMinimum, freshBodyMinimum);
5585
+ }
5586
+ /** Finish long notes even when the section has no more body paragraphs. */
5587
+ finishFootnotes() {
5588
+ while (this.footnotes.hasContinuation) {
5589
+ if (this.legacyFootnoteColumns) this.newColumn();
5590
+ else this.newPage();
5591
+ }
5592
+ }
5593
+ /** Continued notes can occupy an otherwise empty page, before body/table content starts. */
5594
+ advancePastFootnotes(minimumBodyHeight) {
5595
+ while (this.page.footnotes?.length && this.atColumnTop && this.remainingHeightPx() < minimumBodyHeight - 1e-6 || this.columnIndex > 0 && this.footnotes.blockedByEarlierColumn(
5596
+ this.page,
5597
+ this.columnHeightPx,
5598
+ Math.max(
5599
+ 0,
5600
+ ...this.activeColumns.slice(0, this.columnIndex).flatMap(
5601
+ (column) => column.blocks.map((block) => block.yPx + block.heightPx - this.columnTopPx)
5602
+ )
5603
+ )
5604
+ ))
5605
+ if (this.legacyFootnoteColumns) this.newColumn();
5606
+ else this.newPage();
5117
5607
  }
5118
5608
  newPage() {
5119
5609
  this.pushPage();
@@ -5123,6 +5613,8 @@ var PageCursor = class {
5123
5613
  if (this.columnIndex + 1 < this.columns.count) {
5124
5614
  this.columnIndex++;
5125
5615
  this.yPx = this.columnTopPx;
5616
+ if (this.legacyFootnoteColumns)
5617
+ this.footnotes.resume(this.page, this.physicalColumnHeightPx, this.noteColumnIndex);
5126
5618
  } else {
5127
5619
  this.pushPage();
5128
5620
  }
@@ -5136,25 +5628,54 @@ var PageCursor = class {
5136
5628
  box.yPx = this.yPx;
5137
5629
  this.column.blocks.push(box);
5138
5630
  this.yPx += advancePx;
5139
- if (this.pending && this.pending.blockId === box.blockId) {
5140
- this.reservedPx += this.pendingHeightPx();
5141
- const page = this.page;
5142
- let top = page.footnotes?.reduce((sum, note) => sum + note.heightPx, 0) ?? 0;
5143
- for (const note of this.pending.notes) {
5144
- (page.footnotes ??= []).push({ ...note, yPx: top });
5145
- top += note.heightPx;
5146
- }
5147
- this.pending = void 0;
5148
- }
5631
+ const bodyBottom = this.legacyFootnoteColumns ? this.yPx : Math.max(
5632
+ this.yPx,
5633
+ ...this.activeColumns.flatMap(
5634
+ (column) => column.blocks.map((block) => block.yPx + block.heightPx)
5635
+ )
5636
+ );
5637
+ this.footnotes.commit(
5638
+ box.blockId,
5639
+ this.page,
5640
+ this.columnHeightPx - (bodyBottom - this.columnTopPx) - this.footnotes.reserved(this.page, this.noteColumnIndex),
5641
+ this.noteColumnIndex
5642
+ );
5149
5643
  }
5150
5644
  };
5151
5645
 
5152
5646
  // docx/layout/flow-paragraph.ts
5153
5647
  function placeParagraph(cursor, placement, note) {
5154
5648
  const { paragraph, spacingBeforePx, requiredTogetherPx, reflow } = placement;
5155
- let layout = placement.layout;
5649
+ const trailingBreakLine = (result) => {
5650
+ const last = result.lines.at(-1);
5651
+ if (!placement.trimLeadingColumnBreak || !last || !result.columnBreakAfterLine.has(result.lines.length - 1))
5652
+ return result;
5653
+ return {
5654
+ ...result,
5655
+ contentHeightPx: result.contentHeightPx + last.heightPx,
5656
+ lines: [
5657
+ ...result.lines,
5658
+ {
5659
+ ...last,
5660
+ yPx: last.yPx + last.heightPx,
5661
+ fragments: [],
5662
+ sourceStart: last.sourceEnd,
5663
+ sourceEnd: last.sourceEnd
5664
+ }
5665
+ ]
5666
+ };
5667
+ };
5668
+ let layout = trailingBreakLine(placement.layout);
5156
5669
  let width = cursor.columnWidthPx;
5157
5670
  if (paragraph.pageBreakBefore && cursor.hasPageContent) cursor.newPage();
5671
+ if (requiredTogetherPx > 0 && placement.footnotes) {
5672
+ cursor.holdFootnotes(
5673
+ paragraph.id,
5674
+ placement.footnotes(layout.lines, true),
5675
+ requiredTogetherPx,
5676
+ layout.lines[0]?.heightPx ?? 0
5677
+ );
5678
+ }
5158
5679
  if (requiredTogetherPx > 0 && requiredTogetherPx <= cursor.columnHeightPx && requiredTogetherPx > cursor.remainingHeightPx() && !cursor.atColumnTop) {
5159
5680
  cursor.newColumn();
5160
5681
  }
@@ -5162,9 +5683,23 @@ function placeParagraph(cursor, placement, note) {
5162
5683
  let lineCursor = 0;
5163
5684
  let nextToken = 0;
5164
5685
  let firstSegment = true;
5686
+ const lineHeight = (line2, index) => placement.trimLeadingColumnBreak && layout.columnBreakAfterLine.has(index) && line2.fragments.every((fragment) => !fragment.text && !fragment.object) ? 0 : line2.heightPx;
5687
+ const placedNotes = /* @__PURE__ */ new Set();
5688
+ const notesFor = (lines) => (placement.footnotes?.(lines, firstSegment) ?? []).filter((item) => !placedNotes.has(item.id));
5689
+ const hold = (lines, bodyMinimum) => {
5690
+ const notes = notesFor(lines);
5691
+ if (placement.footnotes)
5692
+ cursor.holdFootnotes(
5693
+ paragraph.id,
5694
+ notes,
5695
+ bodyMinimum,
5696
+ Math.max(0, ...lines.map((line2) => line2.heightPx))
5697
+ );
5698
+ return notes;
5699
+ };
5165
5700
  const reflowIfNeeded = () => {
5166
5701
  if (!reflow || cursor.columnWidthPx === width) return;
5167
- layout = reflow(nextToken);
5702
+ layout = trailingBreakLine(reflow(nextToken));
5168
5703
  lineCursor = 0;
5169
5704
  width = cursor.columnWidthPx;
5170
5705
  };
@@ -5178,15 +5713,27 @@ function placeParagraph(cursor, placement, note) {
5178
5713
  }
5179
5714
  while (lineCursor < layout.lines.length) {
5180
5715
  reflowIfNeeded();
5181
- const budget = cursor.remainingHeightPx() - (firstSegment ? spacingBeforePx : 0);
5716
+ hold(
5717
+ layout.lines.slice(lineCursor, lineCursor + 1),
5718
+ layout.lines[lineCursor] ? lineHeight(layout.lines[lineCursor], lineCursor) : 0
5719
+ );
5720
+ cursor.advancePastFootnotes(
5721
+ layout.lines[lineCursor] ? lineHeight(layout.lines[lineCursor], lineCursor) : 0
5722
+ );
5723
+ reflowIfNeeded();
5182
5724
  let cumulative = 0;
5183
5725
  let end = lineCursor;
5184
5726
  let forced;
5185
5727
  for (let i = lineCursor; i < layout.lines.length; i++) {
5186
5728
  const line2 = expectDefined(layout.lines[i], "paragraph line index");
5187
5729
  const gap = i === lineCursor && !firstSegment ? 0 : line2.gapBeforePx ?? 0;
5188
- const height = line2.heightPx + gap;
5189
- if (cumulative + height > budget && (end > lineCursor || !cursor.atColumnTop)) break;
5730
+ const height = lineHeight(line2, i) + gap;
5731
+ hold(
5732
+ layout.lines.slice(lineCursor, i + 1),
5733
+ cumulative + height + (firstSegment ? spacingBeforePx : 0)
5734
+ );
5735
+ const budget = cursor.remainingHeightPx() - (firstSegment ? spacingBeforePx : 0);
5736
+ if (cumulative + height > budget + 1e-6 && (end > lineCursor || !cursor.atColumnTop)) break;
5190
5737
  cumulative += height;
5191
5738
  end = i + 1;
5192
5739
  if (layout.pageBreakAfterLine.has(i)) {
@@ -5211,9 +5758,17 @@ function placeParagraph(cursor, placement, note) {
5211
5758
  );
5212
5759
  placedCount = Math.max(1, end - lineCursor);
5213
5760
  }
5761
+ const committedNotes = hold(
5762
+ layout.lines.slice(lineCursor, lineCursor + placedCount),
5763
+ cumulative + (firstSegment ? spacingBeforePx : 0)
5764
+ );
5214
5765
  const first3 = expectDefined(layout.lines[lineCursor], "first line of paragraph segment");
5215
5766
  const origin = first3.yPx - (firstSegment ? (first3.gapBeforePx ?? 0) + layout.insetTopPx : 0);
5216
- const segmentLines = layout.lines.slice(lineCursor, lineCursor + placedCount).map((line2) => ({ ...line2, yPx: line2.yPx - origin }));
5767
+ const segmentLines = layout.lines.slice(lineCursor, lineCursor + placedCount).map((line2, index) => ({
5768
+ ...line2,
5769
+ yPx: line2.yPx - origin,
5770
+ heightPx: lineHeight(line2, lineCursor + index)
5771
+ }));
5217
5772
  const isFinal = lineCursor + placedCount >= layout.lines.length;
5218
5773
  const lastLine = segmentLines.at(-1);
5219
5774
  const isLastSegment = lineCursor + placedCount >= layout.lines.length;
@@ -5228,6 +5783,7 @@ function placeParagraph(cursor, placement, note) {
5228
5783
  };
5229
5784
  const advance = (firstSegment ? spacingBeforePx : 0) + segmentHeight + (isFinal ? layout.spacingAfterPx : 0);
5230
5785
  cursor.place(box, advance);
5786
+ for (const item of committedNotes) placedNotes.add(item.id);
5231
5787
  nextToken = layout.lines[lineCursor + placedCount - 1]?.nextToken ?? nextToken;
5232
5788
  lineCursor += placedCount;
5233
5789
  firstSegment = false;
@@ -5237,8 +5793,170 @@ function placeParagraph(cursor, placement, note) {
5237
5793
  }
5238
5794
  }
5239
5795
 
5796
+ // docx/layout/table-footnotes.ts
5797
+ function prefix(row, cut, cellCuts) {
5798
+ const cells = row.cells.map((cell, index) => {
5799
+ const contentCut = (cellCuts?.[index] ?? cut) - (row.cellPadding?.[index]?.bottom ?? 0);
5800
+ const boxes = [];
5801
+ for (const box of cell) {
5802
+ if (box.yPx >= contentCut + 1e-6) break;
5803
+ if (box.yPx + box.heightPx <= contentCut + 1e-6) {
5804
+ boxes.push(box);
5805
+ continue;
5806
+ }
5807
+ const lines = [];
5808
+ for (const line2 of box.lines) {
5809
+ if (box.yPx + line2.yPx + line2.heightPx > contentCut + 1e-6) break;
5810
+ lines.push(line2);
5811
+ }
5812
+ if (lines.length)
5813
+ boxes.push({ ...box, lines, heightPx: lines.at(-1).yPx + lines.at(-1).heightPx });
5814
+ continue;
5815
+ }
5816
+ return boxes;
5817
+ });
5818
+ if (cells.every(
5819
+ (cell, index) => cell.length === row.cells[index].length && cell.every(
5820
+ (box, boxIndex) => box.lines.length === row.cells[index][boxIndex].lines.length
5821
+ )
5822
+ ))
5823
+ return row;
5824
+ return {
5825
+ ...row,
5826
+ cells,
5827
+ heightPx: Math.max(
5828
+ 0,
5829
+ ...cells.flatMap(
5830
+ (cell, index) => cell.map((box) => box.yPx + box.heightPx + (row.cellPadding?.[index]?.bottom ?? 0))
5831
+ )
5832
+ )
5833
+ };
5834
+ }
5835
+ var TableFootnotes = class {
5836
+ constructor(cursor, table, height, resolve) {
5837
+ this.cursor = cursor;
5838
+ this.table = table;
5839
+ this.height = height;
5840
+ this.resolve = resolve;
5841
+ for (const row of table.rows)
5842
+ for (const cell of row.cells)
5843
+ for (const paragraph of cellParagraphs(cell))
5844
+ if (paragraph.footnotes?.length) this.paragraphs.set(paragraph.id, paragraph);
5845
+ }
5846
+ cursor;
5847
+ table;
5848
+ height;
5849
+ resolve;
5850
+ paragraphs = /* @__PURE__ */ new Map();
5851
+ placed = /* @__PURE__ */ new Set();
5852
+ fragment = /* @__PURE__ */ new Map();
5853
+ references(row) {
5854
+ const refs = /* @__PURE__ */ new Map();
5855
+ for (const cell of row.cells)
5856
+ for (const box of cell) {
5857
+ const source = this.paragraphs.get(box.blockId);
5858
+ if (!source) continue;
5859
+ for (const note of this.resolve(
5860
+ source,
5861
+ box.lines,
5862
+ (box.lines[0]?.sourceStart ?? 0) === 0
5863
+ )) {
5864
+ if (this.placed.has(note.id)) continue;
5865
+ const line2 = box.lines.find(
5866
+ (line3) => line3.fragments.some(
5867
+ (fragment) => source.runs[fragment.runIndex]?.footnoteReferenceId === note.id
5868
+ )
5869
+ ) ?? box.lines[0];
5870
+ const bottom = Math.min(
5871
+ row.heightPx,
5872
+ box.yPx + (line2?.yPx ?? 0) + (line2?.heightPx ?? box.heightPx)
5873
+ );
5874
+ const prior = refs.get(note.id);
5875
+ if (!prior || bottom < prior.bottom) refs.set(note.id, { note, bottom });
5876
+ }
5877
+ }
5878
+ return [...refs.values()];
5879
+ }
5880
+ hold(refs, minimumBody = 0) {
5881
+ const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5882
+ const bodyHeight = Math.max(referenceBottom, minimumBody);
5883
+ this.cursor.holdFootnotes(
5884
+ this.table.id,
5885
+ [...refs.values()].map((ref) => ref.note),
5886
+ bodyHeight,
5887
+ this.cursor.usedColumnHeightPx + referenceBottom
5888
+ );
5889
+ }
5890
+ holdCandidate(row) {
5891
+ const refs = new Map(this.fragment);
5892
+ for (const ref of this.references(row))
5893
+ if (!refs.has(ref.note.id))
5894
+ refs.set(ref.note.id, { ...ref, bottom: this.height() + ref.bottom });
5895
+ const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5896
+ const contentBottom = this.height() + Math.max(0, ...row.cells.flatMap((cell) => cell.map((box) => box.yPx + box.heightPx)));
5897
+ const paddingBottom = Math.max(0, ...(row.cellPadding ?? []).map((padding) => padding.bottom));
5898
+ this.hold(
5899
+ refs,
5900
+ contentBottom <= referenceBottom + 1e-6 ? this.height() + row.heightPx : referenceBottom + paddingBottom
5901
+ );
5902
+ }
5903
+ /** Largest complete prefix that keeps each new marker with available note content. */
5904
+ choose(row, atomic) {
5905
+ this.cursor.holdFootnotes(this.table.id, [], 0);
5906
+ const available = this.cursor.remainingHeightPx() - this.height();
5907
+ if (row.heightPx <= available + 1e-6) {
5908
+ this.holdCandidate(row);
5909
+ if (row.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6)
5910
+ return { layout: row, cut: row.heightPx };
5911
+ }
5912
+ if (!atomic) {
5913
+ const cuts = /* @__PURE__ */ new Set();
5914
+ for (const [index, cell] of row.cells.entries())
5915
+ for (const box of cell) {
5916
+ if (box.yPx > available + 1e-6) break;
5917
+ for (const line2 of box.lines) {
5918
+ const bottom = box.yPx + line2.yPx + line2.heightPx + (row.cellPadding?.[index]?.bottom ?? 0);
5919
+ if (bottom > available + 1e-6) break;
5920
+ if (bottom > 0) cuts.add(bottom);
5921
+ }
5922
+ }
5923
+ for (const cut of [...cuts].sort((a, b) => b - a)) {
5924
+ const candidate = prefix(row, cut);
5925
+ this.holdCandidate(candidate);
5926
+ if (candidate.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6) {
5927
+ const space = this.cursor.remainingHeightPx() - this.height();
5928
+ const cellCuts = row.cells.map(
5929
+ (cell) => cell.some((box) => this.paragraphs.has(box.blockId)) ? cut : space
5930
+ );
5931
+ const expanded = prefix(row, cut, cellCuts);
5932
+ this.holdCandidate(expanded);
5933
+ if (expanded.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6)
5934
+ return { layout: expanded, cut, cellCuts };
5935
+ this.holdCandidate(candidate);
5936
+ return { layout: candidate, cut };
5937
+ }
5938
+ }
5939
+ }
5940
+ this.holdFragment();
5941
+ return void 0;
5942
+ }
5943
+ add(row, repeated) {
5944
+ if (repeated) return;
5945
+ for (const ref of this.references(row))
5946
+ if (!this.fragment.has(ref.note.id))
5947
+ this.fragment.set(ref.note.id, { ...ref, bottom: this.height() + ref.bottom });
5948
+ }
5949
+ holdFragment() {
5950
+ this.hold(this.fragment);
5951
+ }
5952
+ flushed() {
5953
+ for (const id of this.fragment.keys()) this.placed.add(id);
5954
+ this.fragment.clear();
5955
+ }
5956
+ };
5957
+
5240
5958
  // docx/layout/flow-table.ts
5241
- function placeTable(cursor, table, measurer, note) {
5959
+ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
5242
5960
  const firstBodyRow = table.rows.findIndex((row) => !row.isHeader);
5243
5961
  const headerRows = table.rows.slice(0, firstBodyRow < 0 ? table.rows.length : firstBodyRow);
5244
5962
  const xPosition = () => {
@@ -5247,7 +5965,9 @@ function placeTable(cursor, table, measurer, note) {
5247
5965
  };
5248
5966
  let fragmentRows = [];
5249
5967
  let fragmentHeight = 0;
5968
+ const footnotes = resolveFootnotes ? new TableFootnotes(cursor, table, () => fragmentHeight, resolveFootnotes) : void 0;
5250
5969
  const toRowBox = (layout, repeated) => {
5970
+ footnotes?.add(layout, repeated);
5251
5971
  const box = {
5252
5972
  yPx: fragmentHeight,
5253
5973
  heightPx: layout.heightPx,
@@ -5259,7 +5979,10 @@ function placeTable(cursor, table, measurer, note) {
5259
5979
  return box;
5260
5980
  };
5261
5981
  function flushFragment() {
5262
- if (!fragmentRows.length) return;
5982
+ if (!fragmentRows.length) {
5983
+ footnotes?.holdFragment();
5984
+ return;
5985
+ }
5263
5986
  const box = {
5264
5987
  kind: "table",
5265
5988
  blockId: table.id,
@@ -5268,19 +5991,89 @@ function placeTable(cursor, table, measurer, note) {
5268
5991
  heightPx: fragmentHeight,
5269
5992
  rows: fragmentRows
5270
5993
  };
5994
+ footnotes?.holdFragment();
5271
5995
  cursor.place(box, fragmentHeight);
5996
+ footnotes?.flushed();
5272
5997
  fragmentRows = [];
5273
5998
  fragmentHeight = 0;
5274
5999
  }
5275
6000
  function startNewFragment() {
5276
6001
  flushFragment();
5277
6002
  cursor.newColumn();
6003
+ addHeaders();
6004
+ }
6005
+ function addHeaders() {
5278
6006
  for (const header of headerRows)
5279
6007
  fragmentRows.push(toRowBox(layoutRow(header, cursor.columnWidthPx, measurer, note), true));
5280
6008
  }
5281
6009
  function placeRow(row, initial, cantSplit) {
5282
6010
  let remainingRow = initial;
5283
6011
  while (remainingRow) {
6012
+ const minimum = cantSplit ? Math.min(remainingRow.heightPx, cursor.columnHeightPx) : Math.min(
6013
+ ...remainingRow.cells.flatMap(
6014
+ (cell) => cell.map(
6015
+ (paragraph) => paragraph.yPx + (paragraph.lines[0]?.yPx ?? 0) + (paragraph.lines[0]?.heightPx ?? 0)
6016
+ )
6017
+ )
6018
+ );
6019
+ if (fragmentRows.every((item) => item.repeated)) {
6020
+ const width = cursor.columnWidthPx;
6021
+ cursor.advancePastFootnotes(fragmentHeight + (Number.isFinite(minimum) ? minimum : 0));
6022
+ if (width !== cursor.columnWidthPx) {
6023
+ remainingRow = layoutRow(
6024
+ remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6025
+ cursor.columnWidthPx,
6026
+ measurer,
6027
+ note,
6028
+ remainingRow.cells
6029
+ );
6030
+ if (fragmentRows.length) {
6031
+ fragmentRows = [];
6032
+ fragmentHeight = 0;
6033
+ addHeaders();
6034
+ }
6035
+ continue;
6036
+ }
6037
+ }
6038
+ if (footnotes) {
6039
+ const selected = footnotes.choose(remainingRow, cantSplit);
6040
+ if (selected) {
6041
+ if (selected.layout === remainingRow) {
6042
+ fragmentRows.push(toRowBox(remainingRow, false));
6043
+ remainingRow = null;
6044
+ continue;
6045
+ }
6046
+ const { before: before2, after: after2 } = splitRowAtHeight(
6047
+ remainingRow,
6048
+ selected.cut + 1e-6,
6049
+ selected.cellCuts?.map((cut) => cut + 1e-6)
6050
+ );
6051
+ fragmentRows.push(toRowBox(before2, false));
6052
+ const previousWidth2 = cursor.columnWidthPx;
6053
+ startNewFragment();
6054
+ remainingRow = after2 && previousWidth2 !== cursor.columnWidthPx ? layoutRow(
6055
+ { ...row, heightPx: 0, heightRule: "atLeast" },
6056
+ cursor.columnWidthPx,
6057
+ measurer,
6058
+ note,
6059
+ after2.cells
6060
+ ) : after2;
6061
+ continue;
6062
+ }
6063
+ if (fragmentRows.length || !cursor.atColumnTop) {
6064
+ const previousWidth2 = cursor.columnWidthPx;
6065
+ startNewFragment();
6066
+ if (previousWidth2 !== cursor.columnWidthPx)
6067
+ remainingRow = layoutRow(
6068
+ remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6069
+ cursor.columnWidthPx,
6070
+ measurer,
6071
+ note,
6072
+ remainingRow === initial ? void 0 : remainingRow.cells
6073
+ );
6074
+ continue;
6075
+ }
6076
+ }
5284
6077
  const remaining = cursor.remainingHeightPx() - fragmentHeight;
5285
6078
  if (remainingRow.heightPx <= remaining) {
5286
6079
  fragmentRows.push(toRowBox(remainingRow, false));
@@ -5292,6 +6085,18 @@ function placeTable(cursor, table, measurer, note) {
5292
6085
  startNewFragment();
5293
6086
  remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
5294
6087
  }
6088
+ const noteWidth = cursor.columnWidthPx;
6089
+ cursor.advancePastFootnotes(
6090
+ fragmentHeight + Math.min(remainingRow.heightPx, cursor.columnHeightPx)
6091
+ );
6092
+ if (noteWidth !== cursor.columnWidthPx) {
6093
+ remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
6094
+ if (fragmentRows.length) {
6095
+ fragmentRows = [];
6096
+ fragmentHeight = 0;
6097
+ addHeaders();
6098
+ }
6099
+ }
5295
6100
  const freshRemaining = cursor.remainingHeightPx() - fragmentHeight;
5296
6101
  if (remainingRow.heightPx > freshRemaining)
5297
6102
  note('A table row marked "keep row together" is taller than an empty page/column.');
@@ -5322,11 +6127,37 @@ function placeTable(cursor, table, measurer, note) {
5322
6127
  }
5323
6128
  for (const row of table.rows) {
5324
6129
  const layout = layoutRow(row, cursor.columnWidthPx, measurer, note);
5325
- placeRow(row, layout, Boolean(row.cantSplit) || headerRows.includes(row));
6130
+ placeRow(
6131
+ row,
6132
+ layout,
6133
+ Boolean(row.cantSplit) || headerRows.includes(row) || !!footnotes && row.heightRule === "exact"
6134
+ );
5326
6135
  }
5327
6136
  flushFragment();
5328
6137
  }
5329
6138
 
6139
+ // docx/layout/reference-footnotes.ts
6140
+ function createReferenceFootnotes(width, measurer, report) {
6141
+ const cache = /* @__PURE__ */ new Map();
6142
+ return (paragraph, lines, includeUnanchored) => {
6143
+ const selected = new Set(
6144
+ lines.flatMap(
6145
+ (line2) => line2.fragments.map((fragment) => paragraph.runs[fragment.runIndex]?.footnoteReferenceId)
6146
+ )
6147
+ );
6148
+ const anchored = new Set(paragraph.runs.map((run) => run.footnoteReferenceId));
6149
+ return (paragraph.footnotes ?? []).filter((note) => selected.has(note.id) || includeUnanchored && !anchored.has(note.id)).map((note) => {
6150
+ const key = `${note.id}:${width()}`;
6151
+ const prior = cache.get(key);
6152
+ if (prior) return prior;
6153
+ const stacked = stackParagraphs(note.paragraphs, width(), measurer, report);
6154
+ const box = { id: note.id, yPx: 0, heightPx: stacked.heightPx, paragraphs: stacked.boxes };
6155
+ cache.set(key, box);
6156
+ return box;
6157
+ });
6158
+ };
6159
+ }
6160
+
5330
6161
  // docx/layout/wrap.ts
5331
6162
  var SIDE_DISTANCE_PX = 12;
5332
6163
  var MIN_SIDE_WIDTH_PX = 48;
@@ -5380,20 +6211,117 @@ function lineBoxesFor(exclusions, topPx, columnLeftPx, columnWidthPx) {
5380
6211
  // docx/layout/section-flow.ts
5381
6212
  var SectionFlow = class {
5382
6213
  constructor(section, pages, measurer, note, sectionIndex = 0, exclusions, continueAt, balance) {
6214
+ this.section = section;
5383
6215
  this.exclusions = exclusions;
5384
6216
  this.balance = balance;
6217
+ const noteColumns = section.footnoteColumns ?? (section.modernFootnoteSeparator ? section.columns ?? { count: 1, gapPx: 0 } : void 0);
6218
+ if (!noteColumns && !section.legacyFootnoteColumns && (section.columns?.count ?? 1) > 1 && section.blocks.some(
6219
+ (block) => block.kind === "paragraph" ? !!block.footnotes?.length : block.rows.some(
6220
+ (row) => row.cells.some(
6221
+ (cell) => cellParagraphs(cell).some((paragraph) => !!paragraph.footnotes?.length)
6222
+ )
6223
+ )
6224
+ ))
6225
+ note(
6226
+ "Multi-column footnotes use a page-wide area; native per-column footnote balancing is not yet modeled."
6227
+ );
6228
+ const sources = /* @__PURE__ */ new Map();
6229
+ for (const block of section.blocks) {
6230
+ const paragraphs = block.kind === "paragraph" ? [block] : block.rows.flatMap((row) => row.cells.flatMap(cellParagraphs));
6231
+ for (const paragraph of paragraphs)
6232
+ for (const source of paragraph.footnotes ?? []) sources.set(source.id, source);
6233
+ }
6234
+ const reflowCache = /* @__PURE__ */ new Map();
6235
+ const reflow = (box, width) => {
6236
+ const source = sources.get(box.id);
6237
+ if (!source) return box;
6238
+ const ids = new Set(box.paragraphs.map((paragraph) => paragraph.blockId));
6239
+ const key = JSON.stringify([box.id, width, [...ids], box.sourceTokenStarts]);
6240
+ const cached = reflowCache.get(key);
6241
+ if (cached) return { ...cached, continued: !!box.continued };
6242
+ const paragraphs = source.paragraphs.filter((paragraph) => ids.has(paragraph.id)).map(
6243
+ (paragraph) => box.sourceTokenStarts?.[paragraph.id] ? { ...paragraph, spacingBeforeTwips: NO_TWIPS } : paragraph
6244
+ );
6245
+ const stacked = stackParagraphs(
6246
+ paragraphs,
6247
+ width,
6248
+ measurer,
6249
+ note,
6250
+ 0,
6251
+ new Map(Object.entries(box.sourceTokenStarts ?? {}))
6252
+ );
6253
+ const result = { ...box, yPx: 0, heightPx: stacked.heightPx, paragraphs: stacked.boxes };
6254
+ reflowCache.set(key, result);
6255
+ return result;
6256
+ };
5385
6257
  this.blocks = section.blocks;
5386
6258
  this.measurer = measurer;
5387
6259
  this.note = note;
6260
+ const separatorCache = /* @__PURE__ */ new Map();
5388
6261
  this.cursor = new PageCursor(
5389
6262
  pages,
5390
6263
  section.page,
5391
6264
  section.columns ?? { count: 1, gapPx: 0 },
5392
6265
  sectionIndex,
5393
6266
  continueAt,
5394
- balance
6267
+ balance,
6268
+ note,
6269
+ (page, continued) => {
6270
+ const paragraphs = section.footnoteSeparators?.[continued && !section.modernFootnoteSeparator ? "continuationSeparator" : "separator"];
6271
+ if (!paragraphs) return void 0;
6272
+ const width = page.widthPx - page.marginLeftPx - page.marginRightPx;
6273
+ const key = `${width}:${continued}`;
6274
+ const cached = separatorCache.get(key);
6275
+ if (cached) return cached;
6276
+ const adapted = paragraphs.map((paragraph) => {
6277
+ const probe = section.legacyFootnoteColumns ? layoutParagraph(
6278
+ {
6279
+ ...paragraph,
6280
+ runs: paragraph.runs.map(
6281
+ (run) => run.object?.noteSeparator ? { ...run, object: { ...run.object, widthPx: 0 } } : run
6282
+ )
6283
+ },
6284
+ width,
6285
+ measurer,
6286
+ note
6287
+ ) : void 0;
6288
+ return {
6289
+ ...paragraph,
6290
+ runs: paragraph.runs.map(
6291
+ (run, index) => run.object?.noteSeparator ? {
6292
+ ...run,
6293
+ object: {
6294
+ ...run.object,
6295
+ widthPx: section.legacyFootnoteColumns ? Math.min(
6296
+ run.object.noteSeparator === "continuationSeparator" ? width : 192,
6297
+ Math.max(
6298
+ 0,
6299
+ width - (probe?.lines.flatMap((line2) => line2.fragments).find(
6300
+ (fragment) => fragment.runIndex === index && fragment.object?.noteSeparator
6301
+ )?.xPx ?? 0)
6302
+ )
6303
+ ) : run.object.noteSeparator === "continuationSeparator" ? width : Math.min(192, width)
6304
+ }
6305
+ } : run
6306
+ )
6307
+ };
6308
+ });
6309
+ const stacked = stackParagraphs(adapted, width, measurer, note);
6310
+ const result = { heightPx: stacked.heightPx, paragraphs: stacked.boxes };
6311
+ separatorCache.set(key, result);
6312
+ return result;
6313
+ },
6314
+ noteColumns,
6315
+ reflow,
6316
+ section.legacyFootnoteColumns
6317
+ );
6318
+ this.referenceFootnotes = createReferenceFootnotes(
6319
+ () => this.cursor.footnoteWidthPx,
6320
+ measurer,
6321
+ note
5395
6322
  );
5396
6323
  }
6324
+ section;
5397
6325
  exclusions;
5398
6326
  balance;
5399
6327
  cursor;
@@ -5403,6 +6331,7 @@ var SectionFlow = class {
5403
6331
  measurer;
5404
6332
  note;
5405
6333
  paragraphCache = /* @__PURE__ */ new Map();
6334
+ referenceFootnotes;
5406
6335
  firstRowHeightCache = /* @__PURE__ */ new Map();
5407
6336
  paragraphLayout(index) {
5408
6337
  const key = `${index}:${this.cursor.columnWidthPx}`;
@@ -5477,7 +6406,17 @@ var SectionFlow = class {
5477
6406
  for (let index = 0; index < this.blocks.length; index++) {
5478
6407
  const block = expectDefined(this.blocks[index], "flow block index");
5479
6408
  if (block.kind === "table") {
5480
- placeTable(this.cursor, block, this.measurer, this.note);
6409
+ placeTable(
6410
+ this.cursor,
6411
+ block,
6412
+ this.measurer,
6413
+ this.note,
6414
+ block.rows.some(
6415
+ (row) => row.cells.some(
6416
+ (cell) => cellParagraphs(cell).some((paragraph) => paragraph.footnotes?.length)
6417
+ )
6418
+ ) ? this.referenceFootnotes : void 0
6419
+ );
5481
6420
  if (this.balance?.pageIndex === this.cursor.page.index && !this.cursor.columnContentFits)
5482
6421
  this.balanceValid = false;
5483
6422
  continue;
@@ -5485,19 +6424,7 @@ var SectionFlow = class {
5485
6424
  if (block.afterTableSectionBreak) this.cursor.endBalance();
5486
6425
  const spacingBeforePx = this.spacingBeforeFor(index);
5487
6426
  const layout = this.wrappedLayout(index, spacingBeforePx) ?? this.paragraphLayout(index);
5488
- if (block.footnotes?.length)
5489
- this.cursor.holdFootnotes(
5490
- block.id,
5491
- block.footnotes.map((note) => {
5492
- const stacked = stackParagraphs(
5493
- note.paragraphs,
5494
- this.cursor.columnWidthPx,
5495
- this.measurer,
5496
- this.note
5497
- );
5498
- return { id: note.id, yPx: 0, heightPx: stacked.heightPx, paragraphs: stacked.boxes };
5499
- })
5500
- );
6427
+ const footnotes = (lines, includeUnanchored) => this.referenceFootnotes(block, lines, includeUnanchored);
5501
6428
  const requiredTogetherPx = block.keepNext ? this.requiredKeepHeight(index) : block.keepLines ? spacingBeforePx + layout.contentHeightPx + layout.spacingAfterPx : 0;
5502
6429
  if (requiredTogetherPx > this.cursor.columnHeightPx && requiredTogetherPx <= this.cursor.physicalColumnHeightPx)
5503
6430
  this.balanceValid = false;
@@ -5506,7 +6433,9 @@ var SectionFlow = class {
5506
6433
  {
5507
6434
  paragraph: block,
5508
6435
  layout,
6436
+ footnotes,
5509
6437
  spacingBeforePx,
6438
+ ...this.section.modernFootnoteSeparator || this.section.legacyFootnoteColumns ? { trimLeadingColumnBreak: true } : {},
5510
6439
  requiredTogetherPx,
5511
6440
  reflow: (token) => layoutParagraph(
5512
6441
  block,
@@ -5522,6 +6451,7 @@ var SectionFlow = class {
5522
6451
  if (this.balance?.pageIndex === this.cursor.page.index && this.cursor.usedColumnHeightPx > this.cursor.columnHeightPx)
5523
6452
  this.balanceValid = false;
5524
6453
  }
6454
+ this.cursor.finishFootnotes();
5525
6455
  }
5526
6456
  };
5527
6457
 
@@ -5564,8 +6494,16 @@ function insertParityBlank(pages, section) {
5564
6494
  const nextNumber = pages.length + 1;
5565
6495
  const wanted = section.break === "evenPage" ? 0 : 1;
5566
6496
  if (nextNumber % 2 === wanted) return;
6497
+ const {
6498
+ footnotes: _footnotes,
6499
+ footnoteSeparator: _separator,
6500
+ footnoteHeightPx: _noteHeight,
6501
+ footnoteColumnAreas: _noteColumnAreas,
6502
+ floats: _floats,
6503
+ ...geometry
6504
+ } = previous;
5567
6505
  pages.push({
5568
- ...previous,
6506
+ ...geometry,
5569
6507
  index: pages.length,
5570
6508
  pageInSection: previous.pageInSection + 1,
5571
6509
  columns: previous.columns.map((column) => ({ ...column, blocks: [] }))
@@ -5579,7 +6517,12 @@ function alignVertically(pages, section, note) {
5579
6517
  return;
5580
6518
  }
5581
6519
  for (const page of pages) {
5582
- const available = page.heightPx - page.marginTopPx - page.marginBottomPx;
6520
+ const footnoteHeight = page.footnoteColumnAreas?.length ? Math.max(
6521
+ ...page.footnoteColumnAreas.map(
6522
+ (area) => area.heightPx + (area.separator?.heightPx ?? FOOTNOTE_SEPARATOR_PX)
6523
+ )
6524
+ ) : page.footnotes?.length ? (page.footnoteSeparator?.heightPx ?? FOOTNOTE_SEPARATOR_PX) + (page.footnoteHeightPx ?? page.footnotes.reduce((sum, footnote) => sum + footnote.heightPx, 0)) : 0;
6525
+ const available = page.heightPx - page.marginTopPx - page.marginBottomPx - footnoteHeight;
5583
6526
  const used = Math.max(
5584
6527
  0,
5585
6528
  ...page.columns.flatMap((column) => column.blocks.map((block) => block.yPx + block.heightPx))
@@ -5772,6 +6715,11 @@ function adaptTable(table, model, adaptParagraph) {
5772
6715
  const fill = cssHex(visuals.shadingFill);
5773
6716
  return {
5774
6717
  paragraphs: cell.paragraphs.map(adaptParagraph),
6718
+ ...cell.sourceBlocks ? {
6719
+ blocks: cellSourceBlocks(cell).map(
6720
+ (block) => block.type === "paragraph" ? adaptParagraph(block) : adaptTable(block, model, adaptParagraph)
6721
+ )
6722
+ } : {},
5775
6723
  ...width !== void 0 ? { widthPx: width } : {},
5776
6724
  ...grid && xPx !== void 0 ? { xPx } : {},
5777
6725
  padding,
@@ -5951,7 +6899,13 @@ function adaptDocumentModel(model, note = () => {
5951
6899
  const script = formatting.verticalAlign === "superscript" || noteText !== void 0 && formatting.verticalAlign === void 0 ? "super" : formatting.verticalAlign === "subscript" ? "sub" : void 0;
5952
6900
  const image = run.image;
5953
6901
  if (image?.unsupported === "Text box") reportOnce(TEXT_BOX_NOTE);
5954
- const object = image && !image.anchored ? {
6902
+ const object = run.noteSeparator ? {
6903
+ partName: "",
6904
+ contentType: "",
6905
+ widthPx: 192,
6906
+ heightPx: 1,
6907
+ noteSeparator: run.noteSeparator
6908
+ } : image && !image.anchored ? {
5955
6909
  partName: image.partName,
5956
6910
  contentType: image.contentType,
5957
6911
  widthPx: image.widthPx,
@@ -5959,8 +6913,10 @@ function adaptDocumentModel(model, note = () => {
5959
6913
  ...image.chart ? { chart: docxChartPaint(image.chart, image.widthPx, image.heightPx, theme) } : {}
5960
6914
  } : void 0;
5961
6915
  return {
6916
+ sourceLength: reviewSourceLength(run),
5962
6917
  // Hidden text (`w:vanish`) takes no space when printed, as in Word by default.
5963
6918
  text: image || formatting.vanish ? "" : formatting.caps ? text2.toUpperCase() : text2,
6919
+ ...run.noteReference?.kind === "footnote" ? { footnoteReferenceId: run.noteReference.id } : {},
5964
6920
  ...object ? { object } : {},
5965
6921
  ...definedProps({
5966
6922
  bold: formatting.bold,
@@ -6059,6 +7015,10 @@ function adaptDocumentModel(model, note = () => {
6059
7015
  if (block.type === "paragraph") return adaptParagraph(block);
6060
7016
  reportOnce(TABLE_ROW_NOTE);
6061
7017
  reportOnce(VERTICAL_MERGE_NOTE);
7018
+ if (block.rows.some((row) => row.some((cell) => cell.nestedTables?.length)))
7019
+ reportOnce(
7020
+ "Nested tables are projected as read-only cell paragraphs in Print Layout; their borders, shading, vertical merge and repeated header geometry are not rendered."
7021
+ );
6062
7022
  return adaptTable(block, model, adaptParagraph);
6063
7023
  }
6064
7024
  const blocks = [
@@ -6068,7 +7028,25 @@ function adaptDocumentModel(model, note = () => {
6068
7028
  groupParagraphBorders(blocks, betweenBorders);
6069
7029
  const folded = foldDropCaps(model.blocks, blocks);
6070
7030
  if (folded.size) reportOnce(DROP_CAP_NOTE);
7031
+ if (Object.values(model.footnoteSeparators ?? {}).some(
7032
+ (note2) => note2.blocks.some((block) => block.type === "table")
7033
+ ))
7034
+ reportOnce(
7035
+ "Tables in custom footnote separators are preserved but are not rendered in Print Layout."
7036
+ );
7037
+ const footnoteSeparators = model.footnoteSeparators ? Object.fromEntries(
7038
+ Object.entries(model.footnoteSeparators).map(([kind, note2]) => [
7039
+ kind,
7040
+ note2.blocks.filter((block) => block.type === "paragraph").map((paragraph) => adaptParagraph(paragraph))
7041
+ ])
7042
+ ) : void 0;
6071
7043
  const sections = model.sections;
7044
+ const modernFootnoteSeparator = model.compatibilityMode === 15;
7045
+ const legacyFootnoteColumns = model.compatibilityMode === 12 || model.compatibilityMode === 14;
7046
+ if (!modernFootnoteSeparator && !legacyFootnoteColumns && model.sections?.some((section) => section.footnoteColumns !== void 0))
7047
+ reportOnce(
7048
+ "Footnote column settings are preserved, but this compatibility mode has not been verified against native Word; Print Layout uses the existing page-wide fallback."
7049
+ );
6072
7050
  if (!sections || !sections.length) {
6073
7051
  return {
6074
7052
  sections: [
@@ -6081,6 +7059,9 @@ function adaptDocumentModel(model, note = () => {
6081
7059
  marginBottomPx: model.page.marginBottom,
6082
7060
  marginLeftPx: model.page.marginLeft
6083
7061
  },
7062
+ ...footnoteSeparators ? { footnoteSeparators } : {},
7063
+ ...modernFootnoteSeparator ? { modernFootnoteSeparator: true } : {},
7064
+ ...legacyFootnoteColumns ? { legacyFootnoteColumns: true } : {},
6084
7065
  blocks: blocks.filter((block) => !folded.has(block))
6085
7066
  }
6086
7067
  ]
@@ -6094,8 +7075,34 @@ function adaptDocumentModel(model, note = () => {
6094
7075
  const end = Math.max(start, endIndex);
6095
7076
  const slice = blocks.slice(start, end);
6096
7077
  start = end;
7078
+ if (legacyFootnoteColumns && section.footnoteColumns)
7079
+ reportOnce(
7080
+ "Independent footnote column counts are preserved but ignored in measured compatibility modes 12 and 14; Print Layout attaches notes to body columns."
7081
+ );
7082
+ const footnoteWidth = twipsToPx3(section.pageWidthTwips) - twipsToPx3(section.marginLeftTwips) - twipsToPx3(section.marginRightTwips);
7083
+ if (modernFootnoteSeparator && section.footnoteColumns && (section.footnoteColumns > 45 || footnoteWidth <= 48 * (section.footnoteColumns - 1)))
7084
+ reportOnce(
7085
+ "Footnote columns exceeding the available page width or 45 rendered columns use one column in Print Layout; the saved count is preserved."
7086
+ );
6097
7087
  const sectionStart = index === 0 ? void 0 : sectionBreak(section.type, reportOnce);
6098
7088
  return {
7089
+ ...footnoteSeparators ? { footnoteSeparators } : {},
7090
+ ...modernFootnoteSeparator ? { modernFootnoteSeparator: true } : {},
7091
+ ...legacyFootnoteColumns ? { legacyFootnoteColumns: true } : {},
7092
+ ...section.footnoteColumns !== void 0 && modernFootnoteSeparator ? {
7093
+ footnoteColumns: {
7094
+ count: section.footnoteColumns && (section.footnoteColumns > 45 || footnoteWidth <= 48 * (section.footnoteColumns - 1)) ? 1 : Math.max(1, section.footnoteColumns || section.columns.count),
7095
+ gapPx: section.footnoteColumns ? 48 : twipsToPx3(section.columns.spacingTwips ?? twips(720)),
7096
+ ...!section.footnoteColumns && !section.columns.equalWidth && section.columns.widths?.length ? {
7097
+ widths: section.columns.widths.map((column) => ({
7098
+ widthPx: twipsToPx3(column.widthTwips),
7099
+ gapPx: twipsToPx3(
7100
+ column.spacingTwips ?? section.columns.spacingTwips ?? twips(720)
7101
+ )
7102
+ }))
7103
+ } : {}
7104
+ }
7105
+ } : {},
6099
7106
  page: {
6100
7107
  widthPx: twipsToPx3(section.pageWidthTwips),
6101
7108
  heightPx: twipsToPx3(section.pageHeightTwips),