@stll/folio-core 0.47.7 → 0.49.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 (213) hide show
  1. package/dist/ai-edits/apply.js +565 -121
  2. package/dist/ai-edits/minimal-replacement.d.ts +64 -0
  3. package/dist/ai-edits/minimal-replacement.js +282 -0
  4. package/dist/controller/fontReadiness.js +3 -3
  5. package/dist/controller/hyphenationReadiness.d.ts +19 -0
  6. package/dist/controller/hyphenationReadiness.js +47 -0
  7. package/dist/controller/layoutPipeline.d.ts +3 -0
  8. package/dist/controller/layoutPipeline.js +70 -18
  9. package/dist/display-list/build/buildDisplayList.js +39 -12
  10. package/dist/display-list/build/pageFurniture.d.ts +7 -1
  11. package/dist/display-list/build/pageFurniture.js +21 -8
  12. package/dist/display-list/build/paragraphPrimitives.js +68 -11
  13. package/dist/display-list/build/tablePrimitives.js +10 -6
  14. package/dist/display-list/build/textBoxPrimitives.js +1 -1
  15. package/dist/display-list/build/unsupported.d.ts +2 -2
  16. package/dist/display-list/build/unsupported.js +0 -0
  17. package/dist/display-list/dom/renderDisplayListToDom.js +0 -1
  18. package/dist/docx/attributeRemainder.js +10 -2
  19. package/dist/docx/blockContentParser.d.ts +2 -0
  20. package/dist/docx/blockContentParser.js +79 -64
  21. package/dist/docx/bulletMarkers.d.ts +18 -2
  22. package/dist/docx/bulletMarkers.js +27 -2
  23. package/dist/docx/commentParser.d.ts +2 -1
  24. package/dist/docx/commentParser.js +57 -45
  25. package/dist/docx/containerChildren.d.ts +21 -8
  26. package/dist/docx/containerChildren.js +39 -9
  27. package/dist/docx/documentParser.d.ts +5 -1
  28. package/dist/docx/documentParser.js +8 -5
  29. package/dist/docx/documentSectionFacts.d.ts +22 -0
  30. package/dist/docx/documentSectionFacts.js +43 -0
  31. package/dist/docx/drawingGroupChildren.d.ts +60 -0
  32. package/dist/docx/drawingGroupChildren.js +176 -0
  33. package/dist/docx/drawingUtils.js +3 -0
  34. package/dist/docx/fieldParser.d.ts +8 -1
  35. package/dist/docx/fieldParser.js +47 -3
  36. package/dist/docx/fontTableParser.js +34 -27
  37. package/dist/docx/footnoteParser.d.ts +3 -2
  38. package/dist/docx/footnoteParser.js +15 -12
  39. package/dist/docx/groupDrawingParser.d.ts +9 -2
  40. package/dist/docx/groupDrawingParser.js +125 -37
  41. package/dist/docx/headerFooterParser.d.ts +7 -2
  42. package/dist/docx/headerFooterParser.js +15 -5
  43. package/dist/docx/hyperlinkParser.d.ts +48 -36
  44. package/dist/docx/hyperlinkParser.js +97 -82
  45. package/dist/docx/numberingParser.js +108 -84
  46. package/dist/docx/packageParts.d.ts +45 -1
  47. package/dist/docx/packageParts.js +93 -2
  48. package/dist/docx/paraIdAttribute.d.ts +3 -2
  49. package/dist/docx/paragraphParser.d.ts +5 -1
  50. package/dist/docx/paragraphParser.js +468 -365
  51. package/dist/docx/paragraphProperties.js +123 -109
  52. package/dist/docx/paragraphPropertySource.d.ts +6 -1
  53. package/dist/docx/paragraphPropertySource.js +9 -1
  54. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  55. package/dist/docx/paragraphTextBoxEnrichment.js +143 -10
  56. package/dist/docx/parser.d.ts +7 -1
  57. package/dist/docx/parser.js +33 -16
  58. package/dist/docx/previewBudget.d.ts +55 -33
  59. package/dist/docx/previewBudget.js +59 -65
  60. package/dist/docx/rezip.js +41 -58
  61. package/dist/docx/runParser.d.ts +4 -1
  62. package/dist/docx/runParser.js +182 -184
  63. package/dist/docx/sdtProperties.js +87 -84
  64. package/dist/docx/sectionParser.js +169 -160
  65. package/dist/docx/selectiveSaveFlags.d.ts +10 -3
  66. package/dist/docx/selectiveSaveFlags.js +1 -1
  67. package/dist/docx/selectiveXmlPatch.d.ts +56 -34
  68. package/dist/docx/selectiveXmlPatch.js +222 -115
  69. package/dist/docx/serializer/groupTextBoxWriteBack.d.ts +12 -0
  70. package/dist/docx/serializer/groupTextBoxWriteBack.js +101 -0
  71. package/dist/docx/serializer/paragraphSerializer.js +47 -42
  72. package/dist/docx/serializer/runSerializer.d.ts +5 -1
  73. package/dist/docx/serializer/runSerializer.js +4 -3
  74. package/dist/docx/serializer/settingsSerializer.js +19 -1
  75. package/dist/docx/settingsParser.js +8 -0
  76. package/dist/docx/shapeAlternateContent.d.ts +25 -0
  77. package/dist/docx/shapeAlternateContent.js +52 -0
  78. package/dist/docx/streamingXmlParser.d.ts +6 -2
  79. package/dist/docx/streamingXmlParser.js +12 -7
  80. package/dist/docx/styleParser.js +34 -8
  81. package/dist/docx/tableParser.d.ts +21 -12
  82. package/dist/docx/tableParser.js +518 -441
  83. package/dist/docx/textBoxParser.d.ts +18 -5
  84. package/dist/docx/textBoxParser.js +12 -6
  85. package/dist/docx/verbatimCapture.d.ts +10 -8
  86. package/dist/docx/verbatimCapture.js +45 -2
  87. package/dist/docx/vmlImageParser.d.ts +15 -13
  88. package/dist/docx/vmlImageParser.js +75 -60
  89. package/dist/docx/vmlPreview.d.ts +3 -3
  90. package/dist/docx/vmlPreview.js +3 -4
  91. package/dist/docx/xmlNamespaceContext.d.ts +9 -0
  92. package/dist/docx/xmlNamespaceContext.js +54 -0
  93. package/dist/docx/xmlParser.d.ts +6 -3
  94. package/dist/docx/xmlParser.js +11 -41
  95. package/dist/docx/xmlResourceLimits.d.ts +2 -1
  96. package/dist/docx/xmlResourceLimits.js +4 -1
  97. package/dist/fonts/headlessMeasure.js +0 -0
  98. package/dist/headless-layout.d.ts +11 -2
  99. package/dist/headless-layout.js +153 -107
  100. package/dist/internal/headlessRevisionResolution.js +50 -13
  101. package/dist/internal/paragraphFormattingSerialization.js +3 -3
  102. package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
  103. package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
  104. package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
  105. package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
  106. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
  107. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
  108. package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
  109. package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
  110. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
  111. package/dist/layout-bridge/convert/toFlowBlocks.js +284 -39
  112. package/dist/layout-bridge/engine/hitTest.js +5 -3
  113. package/dist/layout-bridge/engine/selectionRects.js +5 -3
  114. package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
  115. package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
  116. package/dist/layout-engine/index.d.ts +2 -2
  117. package/dist/layout-engine/index.js +102 -14
  118. package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
  119. package/dist/layout-engine/justificationCompatibility.js +9 -5
  120. package/dist/layout-engine/keep-together.d.ts +15 -6
  121. package/dist/layout-engine/keep-together.js +43 -16
  122. package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
  123. package/dist/layout-engine/layoutInstrumentation.js +8 -1
  124. package/dist/layout-engine/measure/advanceComposition.js +50 -7
  125. package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
  126. package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
  127. package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
  128. package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
  129. package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
  130. package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
  131. package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
  132. package/dist/layout-engine/measure/lineBreaks.js +10 -3
  133. package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
  134. package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
  135. package/dist/layout-engine/measure/measureBlocks.js +46 -10
  136. package/dist/layout-engine/measure/measureContainer.js +51 -8
  137. package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
  138. package/dist/layout-engine/measure/measureHelpers.js +34 -8
  139. package/dist/layout-engine/measure/measureParagraph.js +143 -101
  140. package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
  141. package/dist/layout-engine/measure/smallCapsCasing.d.ts +56 -0
  142. package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
  143. package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
  144. package/dist/layout-engine/measure/tabCalculator.js +2 -1
  145. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  146. package/dist/layout-engine/measure/tableCellFloating.js +51 -19
  147. package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
  148. package/dist/layout-engine/measure/tableCellGrid.js +45 -5
  149. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
  150. package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
  151. package/dist/layout-engine/noteAreaFlow.js +83 -0
  152. package/dist/layout-engine/paginator.d.ts +12 -1
  153. package/dist/layout-engine/paginator.js +18 -2
  154. package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
  155. package/dist/layout-engine/paragraphSpacing.js +2 -1
  156. package/dist/layout-engine/tableRowBreak.d.ts +8 -1
  157. package/dist/layout-engine/tableRowBreak.js +25 -2
  158. package/dist/layout-engine/types.d.ts +108 -3
  159. package/dist/layout-engine/types.js +96 -1
  160. package/dist/layout-painter/index.d.ts +3 -0
  161. package/dist/layout-painter/renderPage.js +32 -83
  162. package/dist/layout-painter/renderParagraph.d.ts +6 -1
  163. package/dist/layout-painter/renderParagraph.js +75 -26
  164. package/dist/layout-painter/renderTable.js +26 -13
  165. package/dist/layout-painter/renderTextBox.d.ts +12 -2
  166. package/dist/layout-painter/renderTextBox.js +26 -2
  167. package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
  168. package/dist/prosemirror/alternateContentAttrs.js +35 -0
  169. package/dist/prosemirror/attrs/index.d.ts +9 -9
  170. package/dist/prosemirror/attrs/index.js +133 -11
  171. package/dist/prosemirror/commands/comments.js +51 -41
  172. package/dist/prosemirror/contentControlRevisions.d.ts +43 -0
  173. package/dist/prosemirror/contentControlRevisions.js +84 -0
  174. package/dist/prosemirror/conversion/fromProseDoc.js +54 -23
  175. package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
  176. package/dist/prosemirror/conversion/markInterner.js +90 -0
  177. package/dist/prosemirror/conversion/toProseDoc.js +117 -58
  178. package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
  179. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  180. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
  181. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
  182. package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
  183. package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
  184. package/dist/prosemirror/extensions/nodes/FieldExtension.js +6 -3
  185. package/dist/prosemirror/extensions/nodes/SdtExtension.js +9 -2
  186. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
  187. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +5 -1
  188. package/dist/prosemirror/listMarker.d.ts +1 -0
  189. package/dist/prosemirror/listMarker.js +4 -2
  190. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  191. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  192. package/dist/prosemirror/paragraphDirection.d.ts +29 -2
  193. package/dist/prosemirror/paragraphDirection.js +21 -2
  194. package/dist/prosemirror/plugins/suggestionMode.js +37 -8
  195. package/dist/prosemirror/rejoinRunCarriers.d.ts +27 -0
  196. package/dist/prosemirror/rejoinRunCarriers.js +66 -0
  197. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +10 -0
  198. package/dist/prosemirror/runIdentityAcrossRevisions.js +90 -0
  199. package/dist/prosemirror/schema/nodes.d.ts +50 -6
  200. package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
  201. package/dist/prosemirror/textBoxHostParagraph.js +21 -0
  202. package/dist/prosemirror/validation.js +91 -80
  203. package/dist/types/content.d.ts +2 -2
  204. package/dist/utils/createDocument.js +10 -1
  205. package/dist/utils/scriptSegments.d.ts +33 -4
  206. package/dist/utils/scriptSegments.js +39 -6
  207. package/dist/utils/textFormattingMerge.d.ts +17 -3
  208. package/dist/utils/textFormattingMerge.js +18 -12
  209. package/dist/utils/trailingText.d.ts +22 -0
  210. package/dist/utils/trailingText.js +32 -0
  211. package/package.json +2 -2
  212. package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
  213. package/dist/layout-engine/justifiedLineFit.js +0 -6
@@ -1,4 +1,4 @@
1
- import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
1
+ import { bulletMarkerFontName, convertBulletToUnicode } from "../../docx/bulletMarkers.js";
2
2
  import { resolveDocumentGridLinePitch } from "../../docx/documentGrid.js";
3
3
  import { getFontAlternate } from "../../fonts/fontAlternates.js";
4
4
  import { buildPageBreakRunDescendantIndex } from "../../internal/pageBreakRunDescendantIndex.js";
@@ -17,6 +17,7 @@ import { directionIsRtl, directionToBidi } from "../../prosemirror/paragraphDire
17
17
  import { resolveEffectiveRunStyleFormatting } from "../../prosemirror/runStyleFormatting.js";
18
18
  import { parseSectionBreakType } from "../../prosemirror/sectionCarrier.js";
19
19
  import { expectTextBoxAnchorAttrs } from "../../prosemirror/textBoxAnchorAttrs.js";
20
+ import { textBoxHostParagraph } from "../../prosemirror/textBoxHostParagraph.js";
20
21
  import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
21
22
  import { createStyleEngine } from "../../style-engine/styleEngine.js";
22
23
  import { normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
@@ -26,12 +27,14 @@ import { resolveThemeFont } from "../../utils/fontResolver.js";
26
27
  import { resolveShadingFill } from "../../utils/formatToStyle.js";
27
28
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
28
29
  import { decodeOoxmlSymbolCharacter } from "../../utils/ooxmlSymbol.js";
30
+ import { mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
29
31
  import { sanitizeImageSrc } from "../../utils/sanitizeImageSrc.js";
30
32
  import { tableOfContentsStyleLevel } from "../../utils/tableOfContentsStyle.js";
31
- import { STYLE_TOGGLE_KEYS } from "../../utils/textFormattingMerge.js";
33
+ import { STYLE_TOGGLE_KEYS, mergeTextFormatting } from "../../utils/textFormattingMerge.js";
32
34
  import { AUTO_PARAGRAPH_SPACING_PX, halfPointsToPixels, halfPointsToPoints, pointsToPixels } from "../../utils/units.js";
33
35
  import { getColumns } from "../sectionColumns.js";
34
36
  import { groupParagraphFrames } from "./paragraphFrames.js";
37
+ import { resolveParagraphMarkFormatting, resolveParagraphMarkOwnFormatting } from "./paragraphMarkFormatting.js";
35
38
  import { panic } from "better-result";
36
39
  import { headingLevelOf, paragraphNumberingLevel, paragraphNumberingReferenceId, sameStatedParagraphNumbering, statesNoBorder } from "@stll/docx-core/model";
37
40
  //#region src/layout-bridge/convert/toFlowBlocks.ts
@@ -42,6 +45,32 @@ const expectDetachedWatermarkHostAttr = (attrs) => {
42
45
  if (typeof value !== "boolean") panic("Invalid ProseMirror detached watermark host attrs:\nparagraph.attrs._detachedWatermarkHost: Expected a boolean.");
43
46
  return value;
44
47
  };
48
+ const NOTE_REFERENCE_MARK_XML = /^<(?:[^\s:/>]+:)?(?:footnoteRef|endnoteRef)[\s/>]/u;
49
+ /**
50
+ * Runs with `idKey` set, in document order: the note references a block list
51
+ * makes, through table cells (and tables within cells) and text boxes.
52
+ */
53
+ function collectNoteRefs(blocks, idKey) {
54
+ const refs = [];
55
+ const walk = (containerBlocks) => {
56
+ for (const block of containerBlocks) if (block.kind === "paragraph") for (const run of block.runs) {
57
+ if (run.kind !== "text") continue;
58
+ const noteId = run[idKey];
59
+ if (noteId !== void 0) refs.push({
60
+ noteId,
61
+ pmPos: run.pmStart ?? 0
62
+ });
63
+ }
64
+ else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) walk(cell.blocks);
65
+ else if (block.kind === "textBox") walk(block.content);
66
+ };
67
+ walk(blocks);
68
+ return refs;
69
+ }
70
+ /** Whether captured run-child markup is a note story's `w:footnoteRef`/`w:endnoteRef`. */
71
+ function isNoteReferenceMarkXml(xml) {
72
+ return NOTE_REFERENCE_MARK_XML.test(xml);
73
+ }
45
74
  /**
46
75
  * Word's layout fallback when an imported OOXML style hierarchy never supplies
47
76
  * `w:sz`. ECMA-376 deliberately leaves that terminal fallback to the consumer;
@@ -256,6 +285,7 @@ function extractRunFormatting(marks, theme, fontAlternates) {
256
285
  const alternate = getFontAlternate(eastAsiaFont, fontAlternates);
257
286
  if (alternate) formatting.eastAsiaAlternateFontFamily = alternate;
258
287
  }
288
+ if (attrs.hint) formatting.eastAsiaHint = attrs.hint === "eastAsia";
259
289
  const complexScriptFont = resolveComplexScriptThemeFont(attrs, theme);
260
290
  if (complexScriptFont) {
261
291
  formatting.complexScriptFontFamily = complexScriptFont;
@@ -564,6 +594,7 @@ function textFormattingToRunFormatting(defaultTextFormatting, theme, fontAlterna
564
594
  const alternate = getFontAlternate(eastAsiaFontFamily, fontAlternates);
565
595
  if (alternate) result.eastAsiaAlternateFontFamily = alternate;
566
596
  }
597
+ if (defaultTextFormatting.fontFamily?.hint) result.eastAsiaHint = defaultTextFormatting.fontFamily.hint === "eastAsia";
567
598
  const complexScriptFontFamily = defaultTextFormatting.fontFamily ? resolveComplexScriptThemeFont(defaultTextFormatting.fontFamily, theme) : void 0;
568
599
  if (complexScriptFontFamily) {
569
600
  result.complexScriptFontFamily = complexScriptFontFamily;
@@ -608,6 +639,34 @@ function textFormattingToRunFormatting(defaultTextFormatting, theme, fontAlterna
608
639
  if (defaultTextFormatting.emphasisMark && defaultTextFormatting.emphasisMark !== "none") result.emphasisMark = defaultTextFormatting.emphasisMark;
609
640
  return result;
610
641
  }
642
+ const LIST_MARKER_FORMATTING_KEYS = [
643
+ "fontFamily",
644
+ "alternateFontFamily",
645
+ "eastAsiaFontFamily",
646
+ "eastAsiaAlternateFontFamily",
647
+ "complexScriptFontFamily",
648
+ "complexScriptAlternateFontFamily",
649
+ "fontSize",
650
+ "complexScriptFontSize",
651
+ "bold",
652
+ "complexScriptBold",
653
+ "italic",
654
+ "complexScriptItalic",
655
+ "rtl",
656
+ "forceComplexScript",
657
+ "color"
658
+ ];
659
+ /**
660
+ * The numbering symbol's typography: the numbering level's `w:rPr` applied
661
+ * over the paragraph mark's run properties (ECMA-376 §17.9.24, §17.3.1.29).
662
+ * Text runs of the paragraph play no part.
663
+ */
664
+ function listMarkerFormattingFor(levelFormatting, paragraphMarkFormatting, theme, fontAlternates) {
665
+ const runFormatting = textFormattingToRunFormatting(mergeTextFormatting(paragraphMarkFormatting, levelFormatting), theme, fontAlternates);
666
+ const formatting = {};
667
+ for (const key of LIST_MARKER_FORMATTING_KEYS) if (runFormatting[key] !== void 0) Reflect.set(formatting, key, runFormatting[key]);
668
+ return Object.keys(formatting).length > 0 ? formatting : void 0;
669
+ }
611
670
  function paragraphRunDefaults(pmAttrs, theme, fontAlternates) {
612
671
  return textFormattingToRunFormatting(pmAttrs.defaultTextFormatting, theme, fontAlternates);
613
672
  }
@@ -615,7 +674,7 @@ function paragraphRunDefaults(pmAttrs, theme, fontAlternates) {
615
674
  * Build an ImageRun from ProseMirror node attrs, applying conditional property assignment
616
675
  * to satisfy exactOptionalPropertyTypes.
617
676
  */
618
- function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange) {
677
+ function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange, forceLayoutInCell) {
619
678
  const run = {
620
679
  kind: "image",
621
680
  src: attrs.src,
@@ -647,7 +706,8 @@ function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange) {
647
706
  const runBorderStyle = cssBorderStyleOf(attrs.borderStyle);
648
707
  if (runBorderStyle) run.borderStyle = runBorderStyle;
649
708
  if (attrs.position !== void 0) run.position = attrs.position;
650
- if (attrs.anchor?.layoutInCell !== void 0) run.layoutInCell = attrs.anchor.layoutInCell;
709
+ if (forceLayoutInCell) run.layoutInCell = true;
710
+ else if (attrs.anchor?.layoutInCell !== void 0) run.layoutInCell = attrs.anchor.layoutInCell;
651
711
  if (trackedChange?.isInsertion) run.isInsertion = true;
652
712
  if (trackedChange?.isDeletion) run.isDeletion = true;
653
713
  if (trackedChange?.changeAuthor !== void 0) run.changeAuthor = trackedChange.changeAuthor;
@@ -790,7 +850,8 @@ function paragraphToRuns(node, startPos, _options, pageBreaks) {
790
850
  return;
791
851
  }
792
852
  if (child.type.name === "preservedXml") {
793
- const { text } = expectPreservedXmlAttrs(child);
853
+ const { xml, text: capturedText } = expectPreservedXmlAttrs(child);
854
+ const text = capturedText === "" && _options.noteReferenceMarkText !== void 0 && isNoteReferenceMarkXml(xml) ? _options.noteReferenceMarkText : capturedText;
794
855
  if (text === "") return;
795
856
  const formatting = extractRunFormatting(effectiveMarks, theme, fontAlternates);
796
857
  applyCharacterStyleToggleFormatting({
@@ -838,7 +899,7 @@ function paragraphToRuns(node, startPos, _options, pageBreaks) {
838
899
  if (!hasPaintableImageSource(attrs) && !hasRelationshipBackedImageBox(attrs)) return;
839
900
  const constrained = constrainImageToPage(attrs.width ?? 100, attrs.height ?? 100, _options.pageContentHeight);
840
901
  const trackedFmt = extractRunFormatting(effectiveMarks, theme, fontAlternates);
841
- const run = buildImageRun(attrs, constrained, childPos, childPos + child.nodeSize, trackedFmt);
902
+ const run = buildImageRun(attrs, constrained, childPos, childPos + child.nodeSize, trackedFmt, _options.forceAnchorLayoutInCell);
842
903
  runs.push(run);
843
904
  return;
844
905
  }
@@ -992,11 +1053,11 @@ function resolveDeletedListMarker(previousListAttrs, listCounterState) {
992
1053
  const marker = computeListMarker(previousListAttrs, listCounterState);
993
1054
  if (marker) return marker;
994
1055
  }
995
- if (previousListAttrs.listMarker) return previousListAttrs.listIsBullet ? convertBulletToUnicode(previousListAttrs.listMarker) : previousListAttrs.listMarker;
1056
+ if (previousListAttrs.listMarker) return previousListAttrs.listIsBullet ? convertBulletToUnicode(previousListAttrs.listMarker, bulletMarkerFontName(previousListAttrs.listMarkerFormatting)) : previousListAttrs.listMarker;
996
1057
  if (previousListAttrs.listIsBullet) return "•";
997
1058
  return null;
998
1059
  }
999
- function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fontAlternates) {
1060
+ function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fontAlternates, paragraphMarkFormatting) {
1000
1061
  const previousListAttrs = toPreviousListAttrs(change.previousFormatting);
1001
1062
  const marker = resolveDeletedListMarker(previousListAttrs, listCounterState);
1002
1063
  if (!marker) return;
@@ -1004,7 +1065,8 @@ function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fon
1004
1065
  attrs.listMarkerRevision = toListMarkerRevision("del", change.info);
1005
1066
  if (previousListAttrs.listIsBullet !== void 0) attrs.listIsBullet = previousListAttrs.listIsBullet;
1006
1067
  if (previousListAttrs.listMarkerHidden !== void 0) attrs.listMarkerHidden = previousListAttrs.listMarkerHidden;
1007
- if (previousListAttrs.listMarkerFormatting) attrs.listMarkerFormatting = textFormattingToRunFormatting(previousListAttrs.listMarkerFormatting, theme, fontAlternates);
1068
+ const listMarkerFormatting = listMarkerFormattingFor(previousListAttrs.listMarkerFormatting, paragraphMarkFormatting(), theme, fontAlternates);
1069
+ if (listMarkerFormatting) attrs.listMarkerFormatting = listMarkerFormatting;
1008
1070
  if (previousListAttrs.listMarkerAlignment) attrs.listMarkerAlignment = previousListAttrs.listMarkerAlignment;
1009
1071
  if (previousListAttrs.listMarkerSuffix) attrs.listMarkerSuffix = previousListAttrs.listMarkerSuffix;
1010
1072
  }
@@ -1041,7 +1103,7 @@ const resolveFlowAlignment = (alignment, rightToLeft) => {
1041
1103
  default: return flow;
1042
1104
  }
1043
1105
  };
1044
- function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStreams, defaultTabStopTwips }) {
1106
+ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStreams, defaultTabStopTwips, paragraphMarkFormatting }) {
1045
1107
  const attrs = {};
1046
1108
  if (pmAttrs.alignment) attrs.alignment = resolveFlowAlignment(pmAttrs.alignment, directionIsRtl(pmAttrs.direction));
1047
1109
  if (pmAttrs.outlineLevel !== void 0) attrs.outlineLevel = pmAttrs.outlineLevel;
@@ -1116,9 +1178,10 @@ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStre
1116
1178
  }
1117
1179
  const shadingRgb = pmAttrs.shading?.fill?.rgb?.toUpperCase();
1118
1180
  if (shadingRgb && shadingRgb !== "FFFFFF" && shadingRgb !== "FFFFFE") attrs.shading = `#${pmAttrs.shading?.fill?.rgb}`;
1119
- if (pmAttrs.tabs && pmAttrs.tabs.length > 0) {
1181
+ const tabs = mergeParagraphTabStops(pmAttrs.listLevelTabs ?? void 0, pmAttrs.tabs ?? void 0);
1182
+ if (tabs && tabs.length > 0) {
1120
1183
  const rightToLeft = directionIsRtl(pmAttrs.direction);
1121
- attrs.tabs = pmAttrs.tabs.map((tab) => {
1184
+ attrs.tabs = tabs.map((tab) => {
1122
1185
  const tabStop = {
1123
1186
  val: resolveTabAlignment(tab.alignment, rightToLeft),
1124
1187
  pos: tab.position
@@ -1156,17 +1219,20 @@ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStre
1156
1219
  }
1157
1220
  const resolvedMarker = applyMarkerAllCaps(advanceVisibleListMarker(pmAttrs, listCounterStreams).marker, pmAttrs.listMarkerAllCaps);
1158
1221
  if (resolvedMarker !== null) attrs.listMarker = resolvedMarker;
1159
- else if (pmAttrs.listMarker) attrs.listMarker = pmAttrs.listIsBullet ? convertBulletToUnicode(pmAttrs.listMarker) : pmAttrs.listMarker;
1222
+ else if (pmAttrs.listMarker) attrs.listMarker = pmAttrs.listIsBullet ? convertBulletToUnicode(pmAttrs.listMarker, bulletMarkerFontName(pmAttrs.listMarkerFormatting)) : pmAttrs.listMarker;
1160
1223
  if (pmAttrs.listIsBullet !== void 0) attrs.listIsBullet = pmAttrs.listIsBullet;
1161
1224
  if (pmAttrs.listMarkerHidden) attrs.listMarkerHidden = true;
1162
- if (pmAttrs.listMarkerFormatting) attrs.listMarkerFormatting = textFormattingToRunFormatting(pmAttrs.listMarkerFormatting, theme, fontAlternates);
1225
+ if (attrs.listMarker !== void 0 || pmAttrs.listMarkerFormatting) {
1226
+ const listMarkerFormatting = listMarkerFormattingFor(pmAttrs.listMarkerFormatting, paragraphMarkFormatting(), theme, fontAlternates);
1227
+ if (listMarkerFormatting) attrs.listMarkerFormatting = listMarkerFormatting;
1228
+ }
1163
1229
  if (pmAttrs.listMarkerAlignment) attrs.listMarkerAlignment = pmAttrs.listMarkerAlignment;
1164
1230
  if (pmAttrs.listMarkerSuffix) attrs.listMarkerSuffix = pmAttrs.listMarkerSuffix;
1165
1231
  if (pmAttrs.listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = pmAttrs.listMarkerSecondSlotOffsetTwips;
1166
1232
  if (!pmAttrs.numPr) {
1167
1233
  const numberingRemovedChange = propertyChanges.find(isRemovedNumberingChange);
1168
1234
  if (numberingRemovedChange) {
1169
- applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, void 0, theme, fontAlternates);
1235
+ applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, void 0, theme, fontAlternates, paragraphMarkFormatting);
1170
1236
  if (resolvedMarker !== null) {
1171
1237
  attrs.listMarker = resolvedMarker;
1172
1238
  attrs.listMarkerRevision = toListMarkerRevision("del", numberingRemovedChange.info);
@@ -1177,7 +1243,7 @@ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStre
1177
1243
  const dtf = pmAttrs.defaultTextFormatting;
1178
1244
  if (dtf) {
1179
1245
  if (dtf.fontSize !== void 0) attrs.defaultFontSize = dtf.fontSize / 2;
1180
- if (attrs.listMarker !== void 0 && !attrs.listMarkerHidden && dtf.fontSizeCs !== void 0 && (dtf.fontSize === void 0 || dtf.fontSizeCs > dtf.fontSize)) attrs.listParagraphMarkFontSize = dtf.fontSizeCs / 2;
1246
+ if (attrs.listMarker !== void 0 && !attrs.listMarkerHidden && (attrs.bidi === true || dtf.rtl === true || dtf.cs === true) && dtf.fontSizeCs !== void 0 && (dtf.fontSize === void 0 || dtf.fontSizeCs > dtf.fontSize)) attrs.listParagraphMarkFontSize = dtf.fontSizeCs / 2;
1181
1247
  if (dtf.fontFamily) {
1182
1248
  const resolvedFamily = resolveWesternThemeFont(dtf.fontFamily, theme);
1183
1249
  if (resolvedFamily) {
@@ -1231,6 +1297,18 @@ function hasOnlyHiddenTextRuns(runs) {
1231
1297
  function hasVisibleParagraphPayload(attrs) {
1232
1298
  return attrs.listMarker !== void 0 && !attrs.listMarkerHidden || attrs.borders?.top !== void 0 || attrs.borders?.bottom !== void 0 || attrs.borders?.left !== void 0 || attrs.borders?.right !== void 0 || attrs.borders?.between !== void 0 || attrs.borders?.bar !== void 0 || attrs.shading !== void 0;
1233
1299
  }
1300
+ /** Whether any `w:pBdr` side draws a rule (`none`/`nil` sides draw nothing). */
1301
+ function drawsParagraphBorder(borders) {
1302
+ if (!borders) return false;
1303
+ return [
1304
+ borders.top,
1305
+ borders.bottom,
1306
+ borders.left,
1307
+ borders.right,
1308
+ borders.between,
1309
+ borders.bar
1310
+ ].some((border) => border?.style !== void 0 && !statesNoBorder(border.style));
1311
+ }
1234
1312
  function convertParagraph(node, startPos, options, pageBreaks) {
1235
1313
  const pmAttrs = expectParagraphAttrs(node);
1236
1314
  const runs = paragraphToRuns(node, startPos, options, pageBreaks);
@@ -1238,8 +1316,10 @@ function convertParagraph(node, startPos, options, pageBreaks) {
1238
1316
  theme: options.theme,
1239
1317
  fontAlternates: options.fontAlternates,
1240
1318
  listCounterStreams: options.listCounterStreams,
1241
- defaultTabStopTwips: options.defaultTabStopTwips
1319
+ defaultTabStopTwips: options.defaultTabStopTwips,
1320
+ paragraphMarkFormatting: () => resolveParagraphMarkFormatting(pmAttrs, options.styleResolver)
1242
1321
  });
1322
+ if (options.numberingTabIgnoresIndent && attrs.listMarker !== void 0) attrs.listNumberingTabIgnoresIndent = true;
1243
1323
  if (options.defaultFont !== void 0) attrs.defaultFontFamily ??= options.defaultFont;
1244
1324
  if (options.defaultSize !== void 0) attrs.defaultFontSize ??= options.defaultSize;
1245
1325
  if (options.lineBreakRules) attrs.lineBreakRules = options.lineBreakRules;
@@ -1248,8 +1328,9 @@ function convertParagraph(node, startPos, options, pageBreaks) {
1248
1328
  const defaultTextFormatting = pmAttrs.defaultTextFormatting;
1249
1329
  if (runs.length === 0 || hasOnlyVisuallyEmptyTextRuns(runs)) {
1250
1330
  if (pmAttrs._originalFormatting && Object.entries(pmAttrs._originalFormatting).some(([key, value]) => key !== "runProperties" && value !== void 0 && value !== null)) attrs.hasDirectParagraphFormatting = true;
1251
- const paragraphMarkFormatting = pmAttrs._originalFormatting?.runProperties;
1252
- if (paragraphMarkFormatting && Object.values(paragraphMarkFormatting).some((value) => value !== void 0 && value !== null)) attrs.hasDirectParagraphMarkFormatting = true;
1331
+ const directParagraphMarkFormatting = pmAttrs._originalFormatting?.runProperties;
1332
+ if (directParagraphMarkFormatting && Object.values(directParagraphMarkFormatting).some((value) => value !== void 0 && value !== null)) attrs.hasDirectParagraphMarkFormatting = true;
1333
+ const paragraphMarkFormatting = resolveParagraphMarkOwnFormatting(directParagraphMarkFormatting, options.styleResolver);
1253
1334
  if (paragraphMarkFormatting?.fontSize !== void 0) attrs.defaultFontSize = paragraphMarkFormatting.fontSize / 2;
1254
1335
  const paragraphMarkFontFamily = paragraphMarkFormatting?.fontFamily ? resolveWesternThemeFont(paragraphMarkFormatting.fontFamily, options.theme) : void 0;
1255
1336
  if (paragraphMarkFontFamily) {
@@ -1393,14 +1474,18 @@ function splitParagraphAtPageBreaks({ pageBreaks, paragraph, splitPageBreakAndPa
1393
1474
  fragmentStart = pageBreak.pmEnd;
1394
1475
  }
1395
1476
  const remainingRuns = partitioned.remaining;
1477
+ let paragraphMarkCarrier;
1396
1478
  if (remainingRuns.length > 0) appendParagraph(remainingRuns, fragmentStart, paragraph.pmEnd ?? fragmentStart);
1397
1479
  else if (paragraph.runs.length === 0 || splitPageBreakAndParagraphMark && pageBreaks.at(-1)?.pmEnd === (paragraph.pmEnd ?? fragmentStart) - 1) {
1398
1480
  appendParagraph([], fragmentStart, paragraph.pmEnd ?? fragmentStart);
1399
1481
  const carrier = result.at(-1);
1400
- if (carrier?.kind === "paragraph" && !splitPageBreakAndParagraphMark && !hasVisibleParagraphPayload(carrier.attrs ?? {})) carrier.attrs = {
1401
- ...carrier.attrs,
1402
- suppressEmptyParagraphHeight: true
1403
- };
1482
+ if (carrier?.kind === "paragraph" && !splitPageBreakAndParagraphMark && !hasVisibleParagraphPayload(carrier.attrs ?? {})) {
1483
+ carrier.attrs = {
1484
+ ...carrier.attrs,
1485
+ suppressEmptyParagraphHeight: true
1486
+ };
1487
+ paragraphMarkCarrier = carrier;
1488
+ }
1404
1489
  }
1405
1490
  const fragments = result.filter((block) => block.kind === "paragraph");
1406
1491
  for (let index = 0; index < fragments.length; index += 1) {
@@ -1410,8 +1495,20 @@ function splitParagraphAtPageBreaks({ pageBreaks, paragraph, splitPageBreakAndPa
1410
1495
  if (attrs) fragment.attrs = attrs;
1411
1496
  else delete fragment.attrs;
1412
1497
  }
1498
+ if (paragraphMarkCarrier?.attrs) paragraphMarkCarrier.attrs = withoutParagraphSpacing(paragraphMarkCarrier.attrs);
1413
1499
  return result;
1414
1500
  }
1501
+ function withoutParagraphSpacing(source) {
1502
+ const attrs = { ...source };
1503
+ if (attrs.spacing) {
1504
+ const { before: _before, after: _after, ...lineSpacing } = attrs.spacing;
1505
+ if (Object.keys(lineSpacing).length > 0) attrs.spacing = lineSpacing;
1506
+ else delete attrs.spacing;
1507
+ }
1508
+ delete attrs.automaticSpacing;
1509
+ delete attrs.spacingExplicit;
1510
+ return attrs;
1511
+ }
1415
1512
  /**
1416
1513
  * Word keeps a final empty body paragraph after a table as an editable anchor,
1417
1514
  * but that final anchor does not create a page of its own. Earlier authored
@@ -1513,12 +1610,22 @@ function convertTableCell(node, startPos, options, tableCellMargins) {
1513
1610
  const authoredPageBreakPosition = options.firstPageBreakRunPosition(node);
1514
1611
  const leadingParagraph = node.firstChild?.type.name === "paragraph" && countPageBreakRuns(node) === 1 && options.firstPageBreakRunPosition(node.firstChild) === authoredPageBreakPosition ? node.firstChild : void 0;
1515
1612
  const pageBreaks = [];
1516
- node.forEach((child) => {
1613
+ const convertCellChild = (child, childStart) => {
1517
1614
  if (child.type.name === "paragraph") {
1518
- const block = convertParagraph(child, offset, options, child === leadingParagraph ? pageBreaks : void 0);
1615
+ const block = convertParagraph(child, childStart, options, child === leadingParagraph ? pageBreaks : void 0);
1519
1616
  blocks.push(block);
1520
- } else if (child.type.name === "table") blocks.push(convertTable(child, offset, options));
1521
- else if (child.type.name === "textBox") blocks.push(convertTextBoxNode(child, offset, options));
1617
+ } else if (child.type.name === "table") blocks.push(convertTable(child, childStart, options));
1618
+ else if (child.type.name === "textBox") blocks.push(convertTextBoxNode(child, childStart, options));
1619
+ else if (child.type.name === "blockSdt") {
1620
+ let sdtChildStart = childStart + 1;
1621
+ child.forEach((sdtChild) => {
1622
+ convertCellChild(sdtChild, sdtChildStart);
1623
+ sdtChildStart += sdtChild.nodeSize;
1624
+ });
1625
+ }
1626
+ };
1627
+ node.forEach((child) => {
1628
+ convertCellChild(child, offset);
1522
1629
  offset += child.nodeSize;
1523
1630
  });
1524
1631
  const trailingBlock = blocks.at(-1);
@@ -1669,6 +1776,8 @@ function convertTable(node, startPos, options) {
1669
1776
  if (originalFormatting?.layout !== void 0) tableBlock.layout = originalFormatting.layout;
1670
1777
  if (justification) tableBlock.justification = justification;
1671
1778
  if (indentPx !== void 0) tableBlock.indent = indentPx;
1779
+ const cellSpacing = originalFormatting?.cellSpacing;
1780
+ if (cellSpacing?.type === "dxa" && cellSpacing.value > 0) tableBlock.cellSpacing = twipsToPixels(cellSpacing.value);
1672
1781
  if (options.tableIndentCompatibility && !dropsAuthoredIndent) tableBlock.indentCompatibility = options.tableIndentCompatibility;
1673
1782
  if (floatingPx) tableBlock.floating = floatingPx;
1674
1783
  if (rightToLeft) tableBlock.bidi = true;
@@ -1718,6 +1827,44 @@ function convertImage(node, startPos, pageContentHeight) {
1718
1827
  if (imageBorderStyle) imgBlock.borderStyle = imageBorderStyle;
1719
1828
  return imgBlock;
1720
1829
  }
1830
+ const EMUS_PER_PIXEL = 9525;
1831
+ /**
1832
+ * The `wp:effectExtent` of an inline (`wp:inline`) box, in unrounded pixels.
1833
+ *
1834
+ * An inline object occupies its extent plus the effect extent on every side,
1835
+ * so the box itself sits `l`/`t` inside the space the line gives it. On a
1836
+ * `wp:anchor` the position offsets already locate the shape's own extent, so
1837
+ * the reservation only affects wrapping and is not applied here.
1838
+ */
1839
+ function inlineEffectExtentPx(wrapType, extent) {
1840
+ if (wrapType !== void 0 && wrapType !== "inline" || extent === void 0) return;
1841
+ const px = (emu) => Math.max(0, (emu ?? 0) / EMUS_PER_PIXEL);
1842
+ const resolved = {
1843
+ top: px(extent.top),
1844
+ bottom: px(extent.bottom),
1845
+ left: px(extent.left),
1846
+ right: px(extent.right)
1847
+ };
1848
+ return resolved.top === 0 && resolved.bottom === 0 && resolved.left === 0 && resolved.right === 0 ? void 0 : resolved;
1849
+ }
1850
+ const GRADIENT_POSITION_SCALE = 1e5;
1851
+ /**
1852
+ * A linear `a:gradFill` with its stop colors resolved through the theme. A path
1853
+ * gradient (`a:path`) is not painted: nothing here places its focus.
1854
+ */
1855
+ function resolveLinearGradientFill(fill, theme) {
1856
+ const gradient = fill?.gradient;
1857
+ if (gradient === void 0 || gradient.type !== "linear" || gradient.stops.length === 0) return;
1858
+ const stops = gradient.stops.map((stop) => ({
1859
+ offset: Math.min(1, Math.max(0, stop.position / GRADIENT_POSITION_SCALE)),
1860
+ color: resolveColor(stop.color, theme)
1861
+ })).toSorted((left, right) => left.offset - right.offset);
1862
+ return {
1863
+ angle: gradient.angle ?? 0,
1864
+ scaled: gradient.scaled ?? false,
1865
+ stops
1866
+ };
1867
+ }
1721
1868
  /** Convert a textBox PM node to a TextBoxBlock. */
1722
1869
  function convertTextBoxNode(node, startPos, opts) {
1723
1870
  const attrs = expectTextBoxAttrs(node);
@@ -1750,6 +1897,10 @@ function convertTextBoxNode(node, startPos, opts) {
1750
1897
  if (attrs.autoFit !== void 0) textBox.autoFit = attrs.autoFit;
1751
1898
  if (attrs.textWrap !== void 0) textBox.textWrap = attrs.textWrap;
1752
1899
  if (attrs.fillColor !== void 0) textBox.fillColor = attrs.fillColor;
1900
+ else {
1901
+ const fillGradient = resolveLinearGradientFill(attrs.gradientFill, opts.theme);
1902
+ if (fillGradient !== void 0) textBox.fillGradient = fillGradient;
1903
+ }
1753
1904
  if (attrs.outlineWidth !== void 0) textBox.outlineWidth = attrs.outlineWidth;
1754
1905
  if (attrs.outlineColor !== void 0) textBox.outlineColor = attrs.outlineColor;
1755
1906
  if (attrs.outlineStyle !== void 0) textBox.outlineStyle = attrs.outlineStyle;
@@ -1763,6 +1914,19 @@ function convertTextBoxNode(node, startPos, opts) {
1763
1914
  if (attrs.distLeft !== void 0) textBox.distLeft = attrs.distLeft;
1764
1915
  if (attrs.distRight !== void 0) textBox.distRight = attrs.distRight;
1765
1916
  if (attrs.position !== void 0) textBox.position = attrs.position;
1917
+ const effectExtent = inlineEffectExtentPx(attrs.wrapType, attrs.wrapEffectExtentSlots?.drawing);
1918
+ if (effectExtent !== void 0) textBox.effectExtent = effectExtent;
1919
+ const host = textBoxHostParagraph(node);
1920
+ if (host && attrs.position === void 0 && (attrs.wrapType ?? "inline") === "inline") {
1921
+ const hostAttrs = expectParagraphAttrs(host);
1922
+ textBox.hostParagraph = convertParagraphAttrs(hostAttrs, {
1923
+ theme: opts.theme,
1924
+ fontAlternates: opts.fontAlternates,
1925
+ listCounterStreams: opts.listCounterStreams,
1926
+ defaultTabStopTwips: opts.defaultTabStopTwips,
1927
+ paragraphMarkFormatting: () => resolveParagraphMarkFormatting(hostAttrs, opts.styleResolver)
1928
+ });
1929
+ }
1766
1930
  if (attrs._docxGroupId !== void 0) setTextBoxGroupId(textBox, attrs._docxGroupId);
1767
1931
  const anchorBlockId = attrs._docxAnchorId ? opts.textBoxAnchorBlockIds.get(attrs._docxAnchorId) : void 0;
1768
1932
  if (anchorBlockId !== void 0) Reflect.set(textBox, TEXT_BOX_ANCHOR_BLOCK_ID, anchorBlockId);
@@ -1804,8 +1968,12 @@ function applySectionStartsToBoundaries(blocks, finalSectionStart) {
1804
1968
  * continuous section resumes there too. Accept both the legacy generated
1805
1969
  * carrier and the inline atom's one-token paragraph-mark fragment; neither is
1806
1970
  * a physical layout boundary.
1971
+ *
1972
+ * A section break in `breaksWithoutMarker` ends at an empty w:sectPr paragraph
1973
+ * that projected no block, so a page break just before it ends an earlier
1974
+ * paragraph and still starts the next page.
1807
1975
  */
1808
- function coalesceTrailingPageBreakBeforeContinuousSection(blocks, splitPageBreakAndParagraphMark) {
1976
+ function coalesceTrailingPageBreakBeforeContinuousSection(blocks, splitPageBreakAndParagraphMark, breaksWithoutMarker) {
1809
1977
  if (splitPageBreakAndParagraphMark) return [...blocks];
1810
1978
  const result = [];
1811
1979
  for (let index = 0; index < blocks.length; index += 1) {
@@ -1815,8 +1983,8 @@ function coalesceTrailingPageBreakBeforeContinuousSection(blocks, splitPageBreak
1815
1983
  const isLegacyGeneratedCarrier = block?.kind === "pageBreak" && carrier?.kind === "paragraph" && carrier.runs.length === 0 && block.pmStart !== void 0 && block.pmStart === carrier.pmStart && carrier.pmStart === carrier.pmEnd;
1816
1984
  const isInlineParagraphMarkCarrier = block?.kind === "pageBreak" && carrier?.kind === "paragraph" && carrier.runs.length === 0 && block.pmEnd !== void 0 && carrier.pmStart === block.pmEnd && carrier.pmEnd === block.pmEnd + 1;
1817
1985
  const isUnresolvedInsertion = block?.kind === "pageBreak" && block.isInsertion === true;
1818
- if (block?.kind === "pageBreak" && !isUnresolvedInsertion && carrier?.kind === "sectionBreak" && carrier.type === "continuous") continue;
1819
- if (block?.kind === "pageBreak" && !isUnresolvedInsertion && (isLegacyGeneratedCarrier || isInlineParagraphMarkCarrier) && section?.kind === "sectionBreak" && section.type === "continuous") {
1986
+ if (block?.kind === "pageBreak" && !isUnresolvedInsertion && carrier?.kind === "sectionBreak" && carrier.type === "continuous" && !breaksWithoutMarker.has(carrier.id)) continue;
1987
+ if (block?.kind === "pageBreak" && !isUnresolvedInsertion && (isLegacyGeneratedCarrier || isInlineParagraphMarkCarrier) && section?.kind === "sectionBreak" && section.type === "continuous" && !breaksWithoutMarker.has(section.id)) {
1820
1988
  index += 1;
1821
1989
  continue;
1822
1990
  }
@@ -1884,6 +2052,7 @@ function toFlowBlocks(doc, options = {}) {
1884
2052
  },
1885
2053
  firstPageBreakRunPosition,
1886
2054
  textBoxAnchorBlockIds: /* @__PURE__ */ new Map(),
2055
+ numberingTabIgnoresIndent: doc.attrs["_doNotUseIndentAsNumberingTabStop"] === true,
1887
2056
  styleResolver: createStyleEngine(options.styles),
1888
2057
  numberedRefResults: resolveNumberedRefFields(doc, {
1889
2058
  listCounterState: cloneListCounterState(listCounterState),
@@ -1917,11 +2086,72 @@ function toFlowBlocks(doc, options = {}) {
1917
2086
  const pageBreaks = [];
1918
2087
  const paragraph = convertParagraph(node, pos, opts, pageBreaks);
1919
2088
  if (stripLeadingLineBreak && paragraph.runs.at(0)?.kind === "lineBreak") paragraph.runs.shift();
1920
- for (const block of splitParagraphAtPageBreaks({
2089
+ const projected = splitParagraphAtPageBreaks({
1921
2090
  pageBreaks,
1922
2091
  paragraph,
1923
2092
  splitPageBreakAndParagraphMark: options.splitPageBreakAndParagraphMark === true
1924
- })) trackedPush(block);
2093
+ });
2094
+ if (pageBreaks.length > 0) placeTextBoxAnchorsBeforePageBreaks(node, pos, projected);
2095
+ for (const block of projected) trackedPush(block);
2096
+ };
2097
+ /** Blocks emitted before the current section's first block. */
2098
+ let sectionStartBlockCount = 0;
2099
+ /** Section breaks whose empty w:sectPr paragraph projected no block. */
2100
+ const sectionBreaksWithoutMarker = /* @__PURE__ */ new Set();
2101
+ /**
2102
+ * The page break a text box anchored in an earlier part of its paragraph
2103
+ * must be laid out before, keyed by `_docxAnchorId`.
2104
+ */
2105
+ const textBoxPageBreakByAnchorId = /* @__PURE__ */ new Map();
2106
+ /** Text boxes held back until every block is projected. */
2107
+ const textBoxesBeforePageBreak = /* @__PURE__ */ new Map();
2108
+ /**
2109
+ * An anchored object sits on the page holding its anchor run. When that run
2110
+ * precedes a `w:br w:type="page"` in the same paragraph, the object belongs
2111
+ * to the page the break ends. Either way its paragraph-relative position is
2112
+ * measured from the part of the paragraph holding the anchor.
2113
+ */
2114
+ const placeTextBoxAnchorsBeforePageBreaks = (node, pos, projected) => {
2115
+ let breakIndex = 0;
2116
+ node.descendants((child, childOffset) => {
2117
+ if (child.type.name !== "textBoxAnchor") return true;
2118
+ const anchorPos = pos + 1 + childOffset;
2119
+ while (breakIndex < projected.length) {
2120
+ const block = projected[breakIndex];
2121
+ if (block?.kind === "pageBreak" && (block.pmStart ?? 0) > anchorPos) break;
2122
+ breakIndex += 1;
2123
+ }
2124
+ const { anchorId } = expectTextBoxAnchorAttrs(child);
2125
+ const pageBreak = projected[breakIndex];
2126
+ if (pageBreak?.kind === "pageBreak") textBoxPageBreakByAnchorId.set(anchorId, pageBreak);
2127
+ const anchorFragment = pageBreak ? projected[breakIndex - 1] : projected.at(-1);
2128
+ if (anchorFragment?.kind === "paragraph") opts.textBoxAnchorBlockIds.set(anchorId, anchorFragment.id);
2129
+ else opts.textBoxAnchorBlockIds.delete(anchorId);
2130
+ return false;
2131
+ });
2132
+ };
2133
+ const pushTextBox = (node, pos) => {
2134
+ const textBox = convertTextBoxNode(node, pos, opts);
2135
+ const anchorId = expectTextBoxAttrs(node)._docxAnchorId;
2136
+ const pageBreak = anchorId === void 0 ? void 0 : textBoxPageBreakByAnchorId.get(anchorId);
2137
+ if (!pageBreak) {
2138
+ trackedPush(textBox);
2139
+ return;
2140
+ }
2141
+ tagBlockWithSdtStack(textBox);
2142
+ const heldBack = textBoxesBeforePageBreak.get(pageBreak);
2143
+ if (heldBack) heldBack.push(textBox);
2144
+ else textBoxesBeforePageBreak.set(pageBreak, [textBox]);
2145
+ };
2146
+ /** Put every held-back text box just before its page break, in one pass. */
2147
+ const releaseTextBoxesBeforePageBreaks = () => {
2148
+ if (textBoxesBeforePageBreak.size === 0) return;
2149
+ const inFlowOrder = blocks.splice(0);
2150
+ for (const block of inFlowOrder) {
2151
+ const heldBack = block.kind === "pageBreak" ? textBoxesBeforePageBreak.get(block) : void 0;
2152
+ for (const textBox of heldBack ?? []) blocks.push(textBox);
2153
+ blocks.push(block);
2154
+ }
1925
2155
  };
1926
2156
  const trailingPageBreakSectionPositions = /* @__PURE__ */ new Set();
1927
2157
  const consumedPageBreakPositions = /* @__PURE__ */ new Set();
@@ -2004,7 +2234,11 @@ function toFlowBlocks(doc, options = {}) {
2004
2234
  const hasListFormatting = pmAttrs.numPr !== null && pmAttrs.numPr !== void 0 || pmAttrs.listMarker !== null && pmAttrs.listMarker !== void 0;
2005
2235
  const firstChild = node.firstChild;
2006
2236
  const startsWithColumnBreak = firstChild?.type.name === "hardBreak" && expectHardBreakAttrs(firstChild).breakType === "column";
2007
- if (node.childCount === 1 && startsWithColumnBreak) {
2237
+ const isStandaloneColumnBreak = node.childCount === 1 && startsWithColumnBreak;
2238
+ const isEmptySectionMark = hasSectionBreak && node.content.size === 0;
2239
+ const opensSection = blocks.length === sectionStartBlockCount;
2240
+ let markerDropped = false;
2241
+ if (isStandaloneColumnBreak) {
2008
2242
  const columnBreak = {
2009
2243
  kind: "columnBreak",
2010
2244
  id: nextBlockId(),
@@ -2021,7 +2255,12 @@ function toFlowBlocks(doc, options = {}) {
2021
2255
  };
2022
2256
  trackedPush(columnBreak);
2023
2257
  pushParagraphProjection(node, pos, true);
2024
- } else if (node.content.size > 0 || hasListFormatting || !hasSectionBreak) pushParagraphProjection(node, pos);
2258
+ } else if (!isEmptySectionMark) pushParagraphProjection(node, pos);
2259
+ else if (hasListFormatting || drawsParagraphBorder(pmAttrs.borders) || opensSection) {
2260
+ pushParagraphProjection(node, pos);
2261
+ const mark = blocks.at(-1);
2262
+ if (!opensSection && mark?.kind === "paragraph" && mark.attrs?.pageBreakBefore) delete mark.attrs.pageBreakBefore;
2263
+ } else markerDropped = true;
2025
2264
  if (hasSectionBreak) {
2026
2265
  if (trailingPageBreakSectionPositions.has(pos)) {
2027
2266
  const sourceParagraph = blocks.at(-1);
@@ -2093,6 +2332,8 @@ function toFlowBlocks(doc, options = {}) {
2093
2332
  if (columns) sectionBreak.columns = columns;
2094
2333
  }
2095
2334
  trackedPush(sectionBreak);
2335
+ sectionStartBlockCount = blocks.length;
2336
+ if (markerDropped) sectionBreaksWithoutMarker.add(sectionBreak.id);
2096
2337
  }
2097
2338
  break;
2098
2339
  }
@@ -2105,7 +2346,7 @@ function toFlowBlocks(doc, options = {}) {
2105
2346
  break;
2106
2347
  }
2107
2348
  case "textBox":
2108
- trackedPush(convertTextBoxNode(node, pos, opts));
2349
+ pushTextBox(node, pos);
2109
2350
  break;
2110
2351
  case "horizontalRule":
2111
2352
  case "pageBreak": {
@@ -2125,15 +2366,19 @@ function toFlowBlocks(doc, options = {}) {
2125
2366
  doc.forEach((node, nodeOffset) => {
2126
2367
  visit(node, offset + nodeOffset);
2127
2368
  });
2369
+ releaseTextBoxesBeforePageBreaks();
2128
2370
  reserveLeadingEmptyOutlineHeight(blocks);
2129
- suppressFinalEmptyParagraphAfterTable(blocks);
2130
- suppressFinalParagraphInRepeatedEmptySuffix(blocks);
2371
+ const trailingEndnoteIds = options.trailingEndnoteIds;
2372
+ if (!(trailingEndnoteIds !== void 0 && trailingEndnoteIds.size > 0 && collectNoteRefs(blocks, "endnoteRefId").some(({ noteId }) => trailingEndnoteIds.has(noteId)))) {
2373
+ suppressFinalEmptyParagraphAfterTable(blocks);
2374
+ suppressFinalParagraphInRepeatedEmptySuffix(blocks);
2375
+ }
2131
2376
  const mergedBlocks = mergeRunInParagraphs(blocks);
2132
2377
  const tableCellLinePitch = doc.attrs["_adjustLineHeightInTable"] === true ? "sectionGrid" : void 0;
2133
2378
  return groupParagraphFrames(coalesceTrailingPageBreakBeforeContinuousSection(applySectionStartsToBoundaries(applySectionDocumentGrid(mergedBlocks, {
2134
2379
  finalLinePitchTwips: opts.finalSectionDocumentGridLinePitchTwips,
2135
2380
  tableCellLinePitch
2136
- }), readFinalSectionStart(doc)), options.splitPageBreakAndParagraphMark === true), nextBlockId);
2381
+ }), readFinalSectionStart(doc)), options.splitPageBreakAndParagraphMark === true, sectionBreaksWithoutMarker), nextBlockId);
2137
2382
  }
2138
2383
  function applySectionDocumentGrid(blocks, { finalLinePitchTwips, tableCellLinePitch }) {
2139
2384
  const result = [...blocks];
@@ -2234,4 +2479,4 @@ function mergeRunInParagraphs(blocks) {
2234
2479
  return out;
2235
2480
  }
2236
2481
  //#endregion
2237
- export { WORD_UNSPECIFIED_FONT_SIZE, convertBorderSpecToLayout, formatCounter, resetBlockIdCounter, resolveListTemplate, toFlowBlocks };
2482
+ export { WORD_UNSPECIFIED_FONT_SIZE, collectNoteRefs, convertBorderSpecToLayout, formatCounter, isNoteReferenceMarkXml, resetBlockIdCounter, resolveListTemplate, toFlowBlocks };
@@ -1,7 +1,7 @@
1
1
  import { getHeaderRowsHeight } from "../../layout-engine/index.js";
2
2
  import { measuredLineRangeHeight } from "../../layout-engine/lineFlow.js";
3
3
  import { getTableCellContentWidth } from "../../layout-engine/measure/tableCellFloating.js";
4
- import { buildTableCellGrid, buildTableCellPlacements } from "../../layout-engine/measure/tableCellGrid.js";
4
+ import { buildTableCellGrid, buildTableCellPlacements, getTableCellSpacingInsetsY } from "../../layout-engine/measure/tableCellGrid.js";
5
5
  import { resolveTableCellPadding } from "../../layout-engine/types.js";
6
6
  //#region src/layout-bridge/engine/hitTest.ts
7
7
  /**
@@ -222,7 +222,8 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
222
222
  const cellPlacements = buildTableCellPlacements({
223
223
  grid: buildTableCellGrid(tableBlock.rows, tableMeasure.columnWidths.length),
224
224
  columnWidths: tableMeasure.columnWidths,
225
- bidi: tableBlock.bidi === true
225
+ bidi: tableBlock.bidi === true,
226
+ cellSpacing: tableBlock.cellSpacing
226
227
  });
227
228
  let nearestDistance = Infinity;
228
229
  for (let c = 0; c < row.cells.length; c++) {
@@ -267,7 +268,8 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
267
268
  const cellLocalX = localX - cellLeft - padLeft;
268
269
  const cellContentWidth = getTableCellContentWidth(cell, cellMeasure);
269
270
  const clipOffset = isClickOnHeader ? 0 : tableFragment.topClip ?? 0;
270
- const cellLocalY = localY - rowTop + clipOffset;
271
+ const cellSpacingTop = getTableCellSpacingInsetsY(tableBlock.cellSpacing, rowIndex, cell.rowSpan ?? 1, tableBlock.rows.length).top;
272
+ const cellLocalY = localY - rowTop + clipOffset - cellSpacingTop;
271
273
  return {
272
274
  fragment: tableFragment,
273
275
  block: tableBlock,